Merge "Optimizing: Reduce arena memory used by GraphChecker."
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 6952d69..6befec5 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -63,7 +63,11 @@
     ART_TARGET_ARCH_64 := $(TARGET_ARCH)
   else
     # TODO: ???
-    $(error Do not know what to do with this multi-target configuration!)
+    $(warning Do not know what to do with this multi-target configuration!)
+    ART_PHONY_TEST_TARGET_SUFFIX := 32
+    2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
+    ART_TARGET_ARCH_32 := $(TARGET_ARCH)
+    ART_TARGET_ARCH_64 :=
   endif
 else
   ifneq ($(filter %64,$(TARGET_ARCH)),)
diff --git a/compiler/Android.mk b/compiler/Android.mk
index f12f007..e6ff8f6 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -299,28 +299,6 @@
     endif
   endif
 
-  ifeq ($$(art_target_or_host),target)
-    ifeq ($$(art_ndebug_or_debug),debug)
-      $(TARGET_OUT_EXECUTABLES)/dex2oatd: $$(LOCAL_INSTALLED_MODULE)
-    else
-      $(TARGET_OUT_EXECUTABLES)/dex2oat: $$(LOCAL_INSTALLED_MODULE)
-    endif
-  else # host
-    ifeq ($$(art_ndebug_or_debug),debug)
-      ifeq ($$(art_static_or_shared),static)
-        $(HOST_OUT_EXECUTABLES)/dex2oatds: $$(LOCAL_INSTALLED_MODULE)
-      else
-        $(HOST_OUT_EXECUTABLES)/dex2oatd: $$(LOCAL_INSTALLED_MODULE)
-      endif
-    else
-      ifeq ($$(art_static_or_shared),static)
-        $(HOST_OUT_EXECUTABLES)/dex2oats: $$(LOCAL_INSTALLED_MODULE)
-      else
-        $(HOST_OUT_EXECUTABLES)/dex2oat: $$(LOCAL_INSTALLED_MODULE)
-      endif
-    endif
-  endif
-
   # Clear locally defined variables.
   art_target_or_host :=
   art_ndebug_or_debug :=
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 7c9ce1e..1491a18 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -64,7 +64,6 @@
       DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
       DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
     }
-    DCHECK_EQ(it->second->GetDexGcMap().size(), verified_method->GetDexGcMap().size());
     // Delete the new verified method since there was already an existing one registered. It
     // is unsafe to replace the existing one since the JIT may be using it to generate a
     // native GC map.
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 9ae2164..5c0253c 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -30,7 +30,6 @@
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "utils.h"
-#include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier-inl.h"
 #include "verifier/reg_type-inl.h"
 #include "verifier/register_line-inl.h"
@@ -49,14 +48,6 @@
                          method_verifier->HasInstructionThatWillThrow()));
 
   if (compile) {
-    /* Generate a register map. */
-    if (!verified_method->GenerateGcMap(method_verifier)) {
-      return nullptr;  // Not a real failure, but a failure to encode.
-    }
-    if (kIsDebugBuild) {
-      VerifyGcMap(method_verifier, verified_method->dex_gc_map_);
-    }
-
     // TODO: move this out when DEX-to-DEX supports devirtualization.
     if (method_verifier->HasVirtualOrInterfaceInvokes()) {
       verified_method->GenerateDevirtMap(method_verifier);
@@ -90,120 +81,6 @@
   return std::binary_search(safe_cast_set_.begin(), safe_cast_set_.end(), pc);
 }
 
-bool VerifiedMethod::GenerateGcMap(verifier::MethodVerifier* method_verifier) {
-  DCHECK(dex_gc_map_.empty());
-  size_t num_entries, ref_bitmap_bits, pc_bits;
-  ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits);
-  const size_t ref_bitmap_bytes = RoundUp(ref_bitmap_bits, kBitsPerByte) / kBitsPerByte;
-  static constexpr size_t kFormatBits = 3;
-  // We have 16 - kFormatBits available for the ref_bitmap_bytes.
-  if ((ref_bitmap_bytes >> (16u - kFormatBits)) != 0) {
-    LOG(WARNING) << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers: "
-                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
-                                 *method_verifier->GetMethodReference().dex_file);
-    return false;
-  }
-  // There are 2 bytes to encode the number of entries.
-  if (num_entries > std::numeric_limits<uint16_t>::max()) {
-    LOG(WARNING) << "Cannot encode GC map for method with " << num_entries << " entries: "
-                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
-                                 *method_verifier->GetMethodReference().dex_file);
-    return false;
-  }
-  size_t pc_bytes;
-  verifier::RegisterMapFormat format;
-  if (pc_bits <= kBitsPerByte) {
-    format = verifier::kRegMapFormatCompact8;
-    pc_bytes = 1;
-  } else if (pc_bits <= kBitsPerByte * 2) {
-    format = verifier::kRegMapFormatCompact16;
-    pc_bytes = 2;
-  } else {
-    LOG(WARNING) << "Cannot encode GC map for method with "
-                 << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2): "
-                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
-                                 *method_verifier->GetMethodReference().dex_file);
-    return false;
-  }
-  size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
-  dex_gc_map_.reserve(table_size);
-  // Write table header.
-  dex_gc_map_.push_back(format | ((ref_bitmap_bytes & ~0xFF) >> (kBitsPerByte - kFormatBits)));
-  dex_gc_map_.push_back(ref_bitmap_bytes & 0xFF);
-  dex_gc_map_.push_back(num_entries & 0xFF);
-  dex_gc_map_.push_back((num_entries >> 8) & 0xFF);
-  // Write table data.
-  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
-  for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
-    if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
-      dex_gc_map_.push_back(i & 0xFF);
-      if (pc_bytes == 2) {
-        dex_gc_map_.push_back((i >> 8) & 0xFF);
-      }
-      verifier::RegisterLine* line = method_verifier->GetRegLine(i);
-      line->WriteReferenceBitMap(method_verifier, &dex_gc_map_, ref_bitmap_bytes);
-    }
-  }
-  DCHECK_EQ(dex_gc_map_.size(), table_size);
-  return true;
-}
-
-void VerifiedMethod::VerifyGcMap(verifier::MethodVerifier* method_verifier,
-                                 const std::vector<uint8_t>& data) {
-  // Check that for every GC point there is a map entry, there aren't entries for non-GC points,
-  // that the table data is well formed and all references are marked (or not) in the bitmap.
-  verifier::DexPcToReferenceMap map(&data[0]);
-  CHECK_EQ(data.size(), map.RawSize()) << map.NumEntries() << " " << map.RegWidth();
-  size_t map_index = 0;
-  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
-  for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
-    const uint8_t* reg_bitmap = map.FindBitMap(i, false);
-    if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
-      DCHECK_LT(map_index, map.NumEntries());
-      DCHECK_EQ(map.GetDexPc(map_index), i);
-      DCHECK_EQ(map.GetBitMap(map_index), reg_bitmap);
-      map_index++;
-      verifier::RegisterLine* line = method_verifier->GetRegLine(i);
-      for (size_t j = 0; j < code_item->registers_size_; j++) {
-        if (line->GetRegisterType(method_verifier, j).IsNonZeroReferenceTypes()) {
-          DCHECK_LT(j / kBitsPerByte, map.RegWidth());
-          DCHECK_EQ((reg_bitmap[j / kBitsPerByte] >> (j % kBitsPerByte)) & 1, 1);
-        } else if ((j / kBitsPerByte) < map.RegWidth()) {
-          DCHECK_EQ((reg_bitmap[j / kBitsPerByte] >> (j % kBitsPerByte)) & 1, 0);
-        } else {
-          // If a register doesn't contain a reference then the bitmap may be shorter than the line.
-        }
-      }
-    } else {
-      DCHECK(i >= 65536 || reg_bitmap == nullptr);
-    }
-  }
-}
-
-void VerifiedMethod::ComputeGcMapSizes(verifier::MethodVerifier* method_verifier,
-                                       size_t* gc_points, size_t* ref_bitmap_bits,
-                                       size_t* log2_max_gc_pc) {
-  size_t local_gc_points = 0;
-  size_t max_insn = 0;
-  size_t max_ref_reg = -1;
-  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
-  for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
-    if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
-      local_gc_points++;
-      max_insn = i;
-      verifier::RegisterLine* line = method_verifier->GetRegLine(i);
-      max_ref_reg = line->GetMaxNonZeroReferenceReg(method_verifier, max_ref_reg);
-    }
-  }
-  *gc_points = local_gc_points;
-  *ref_bitmap_bits = max_ref_reg + 1;  // If max register is 0 we need 1 bit to encode (ie +1).
-  size_t i = 0;
-  while ((1U << i) <= max_insn) {
-    i++;
-  }
-  *log2_max_gc_pc = i;
-}
-
 bool VerifiedMethod::GenerateDequickenMap(verifier::MethodVerifier* method_verifier) {
   if (method_verifier->HasFailures()) {
     return false;
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 12d0219..495acf0 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -47,10 +47,6 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
   ~VerifiedMethod() = default;
 
-  const std::vector<uint8_t>& GetDexGcMap() const {
-    return dex_gc_map_;
-  }
-
   const DevirtualizationMap& GetDevirtMap() const {
     return devirt_map_;
   }
@@ -114,7 +110,6 @@
   void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  std::vector<uint8_t> dex_gc_map_;
   DevirtualizationMap devirt_map_;
   // Dequicken map is required for compiling quickened byte codes. The quicken maps from
   // dex PC to dex method index or dex field index based on the instruction.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index ea16cb2..d29d528 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2212,7 +2212,8 @@
 
 class VerifyClassVisitor : public CompilationVisitor {
  public:
-  explicit VerifyClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
+  VerifyClassVisitor(const ParallelCompilationManager* manager, LogSeverity log_level)
+     : manager_(manager), log_level_(log_level) {}
 
   virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
     ATRACE_CALL();
@@ -2250,7 +2251,7 @@
                                                 &class_def,
                                                 Runtime::Current()->GetCompilerCallbacks(),
                                                 true /* allow soft failures */,
-                                                true /* log hard failures */,
+                                                log_level_,
                                                 &error_msg) ==
                                                     verifier::MethodVerifier::kHardFailure) {
         LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
@@ -2259,7 +2260,7 @@
       }
     } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
       CHECK(klass->IsResolved()) << PrettyClass(klass.Get());
-      class_linker->VerifyClass(soa.Self(), klass);
+      class_linker->VerifyClass(soa.Self(), klass, log_level_);
 
       if (klass->IsErroneous()) {
         // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
@@ -2282,6 +2283,7 @@
 
  private:
   const ParallelCompilationManager* const manager_;
+  const LogSeverity log_level_;
 };
 
 void CompilerDriver::VerifyDexFile(jobject class_loader,
@@ -2294,7 +2296,10 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
                                      thread_pool);
-  VerifyClassVisitor visitor(&context);
+  LogSeverity log_level = GetCompilerOptions().AbortOnHardVerifierFailure()
+                              ? LogSeverity::INTERNAL_FATAL
+                              : LogSeverity::WARNING;
+  VerifyClassVisitor visitor(&context, log_level);
   context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
 }
 
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 32869ec..f113921 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -54,7 +54,6 @@
 #include "parallel_move_resolver.h"
 #include "ssa_liveness_analysis.h"
 #include "utils/assembler.h"
-#include "verifier/dex_gc_map.h"
 #include "vmap_table.h"
 
 namespace art {
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 266cb10..c06d19d 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -141,7 +141,7 @@
   DCHECK(stack_.empty());
   map_.clear();
 
-  // Determine the loop's trip count.
+  // Determine the loop's trip-count.
   VisitControl(loop);
 }
 
@@ -917,6 +917,7 @@
         info1->induction_class == info2->induction_class &&
         info1->operation       == info2->operation       &&
         info1->fetch           == info2->fetch           &&
+        info1->type            == info2->type            &&
         InductionEqual(info1->op_a, info2->op_a)         &&
         InductionEqual(info1->op_b, info2->op_b);
   }
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 0fbb67d..580d24b 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -157,6 +157,13 @@
         iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
   }
 
+  // Returns true if instructions have identical induction.
+  bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) {
+    return HInductionVarAnalysis::InductionEqual(
+      iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1),
+      iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
+  }
+
   // Performs InductionVarAnalysis (after proper set up).
   void PerformInductionVarAnalysis() {
     graph_->BuildDominatorTree();
@@ -228,6 +235,9 @@
   EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
   EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
 
+  // Offset matters!
+  EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
+
   // Trip-count.
   EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
                GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
@@ -320,6 +330,10 @@
   PerformInductionVarAnalysis();
 
   EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+
+  // Both increments get same induction.
+  EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
+  EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
 }
 
 TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
@@ -570,6 +584,33 @@
   }
 }
 
+TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) {
+  // Setup:
+  // for (int i = 0; i < 100; i++) {
+  //   k = (byte) i;
+  //   a[k] = 0;
+  //   a[i] = 0;
+  // }
+  BuildLoopNest(1);
+  HInstruction *conv = InsertInstruction(
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+  HInstruction* store1 = InsertArrayStore(conv, 0);
+  HInstruction* store2 = InsertArrayStore(basic_[0], 0);
+  PerformInductionVarAnalysis();
+
+  // Regular int induction (i) is "transferred" over conversion into byte induction (k).
+  EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
+  EXPECT_STREQ("((1) * i + (0)):PrimInt",  GetInductionInfo(store2->InputAt(1), 0).c_str());
+  EXPECT_STREQ("((1) * i + (1)):PrimInt",  GetInductionInfo(increment_[0], 0).c_str());
+
+  // Type matters!
+  EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
+
+  // Trip-count.
+  EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
+               GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
 TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
   // Setup:
   // for (byte i = -128; i < 127; i++) {  // just fits!
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 09ca8b7..294d00f 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -458,6 +458,7 @@
   }
 
   for (HNewInstance* new_instance : uninitialized_strings_) {
+    DCHECK(new_instance->IsInBlock());
     // Replace NewInstance of String with NullConstant if not used prior to
     // calling StringFactory. In case of deoptimization, the interpreter is
     // expected to skip null check on the `this` argument of the StringFactory call.
@@ -972,7 +973,13 @@
     // Replacing the NewInstance might render it redundant. Keep a list of these
     // to be visited once it is clear whether it is has remaining uses.
     if (arg_this->IsNewInstance()) {
-      uninitialized_strings_.push_back(arg_this->AsNewInstance());
+      HNewInstance* new_instance = arg_this->AsNewInstance();
+      // Note that in some rare cases (b/27847265), the same NewInstance may be seen
+      // multiple times. We should only consider it once for removal, so we
+      // ensure it is not added more than once.
+      if (!ContainsElement(uninitialized_strings_, new_instance)) {
+        uninitialized_strings_.push_back(new_instance);
+      }
     } else {
       DCHECK(arg_this->IsPhi());
       // NewInstance is not the direct input of the StringFactory call. It might
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index 2958dc6..fb37804 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -88,7 +88,7 @@
     std::memset(header_data.data, 0, sizeof(header_data.data));
     DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
     std::copy_n(DexFile::kDexMagic, 4u, header->magic_);
-    std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u);
+    std::copy_n(DexFile::kDexMagicVersions[0], 4u, header->magic_ + 4u);
     header->header_size_ = sizeof(DexFile::Header);
     header->endian_tag_ = DexFile::kDexEndianConstant;
     header->link_size_ = 0u;  // Unused.
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 48a9d91..335b6e1 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -61,7 +61,6 @@
 #include "stack_map.h"
 #include "ScopedLocalRef.h"
 #include "thread_list.h"
-#include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
 #include "vmap_table.h"
 #include "well_known_classes.h"
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 0c6541e..542a2c4 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -187,7 +187,6 @@
   type_lookup_table.cc \
   utf.cc \
   utils.cc \
-  verifier/dex_gc_map.cc \
   verifier/instruction_flags.cc \
   verifier/method_verifier.cc \
   verifier/reg_type.cc \
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 7a620e3..212e5bd 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -185,14 +185,15 @@
 LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, int error)
   : data_(new LogMessageData(file, line, severity, error)) {
   if (PrintDirectly(severity)) {
-    static const char* log_characters = "VDIWEFF";
-    CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
-    stream() << ProgramInvocationShortName() << " " << log_characters[static_cast<size_t>(severity)]
+    static constexpr char kLogCharacters[] = { 'N', 'V', 'D', 'I', 'W', 'E', 'F', 'F' };
+    static_assert(arraysize(kLogCharacters) == static_cast<size_t>(INTERNAL_FATAL) + 1,
+                  "Wrong character array size");
+    stream() << ProgramInvocationShortName() << " " << kLogCharacters[static_cast<size_t>(severity)]
              << " " << getpid() << " " << ::art::GetTid() << " " << file << ":" <<  line << "]";
   }
 }
 LogMessage::~LogMessage() {
-  if (!PrintDirectly(data_->GetSeverity())) {
+  if (!PrintDirectly(data_->GetSeverity()) && data_->GetSeverity() != LogSeverity::NONE) {
     if (data_->GetSeverity() < gMinimumLogSeverity) {
       return;  // No need to format something we're not going to output.
     }
@@ -236,6 +237,7 @@
 
 #ifdef __ANDROID__
 static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
+  ANDROID_LOG_VERBOSE,  // NONE, use verbose as stand-in, will never be printed.
   ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN,
   ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL
 };
@@ -245,16 +247,20 @@
 
 void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity log_severity,
                          const char* message) {
+  if (log_severity == LogSeverity::NONE) {
+    return;
+  }
+
 #ifdef __ANDROID__
   const char* tag = ProgramInvocationShortName();
-  int priority = kLogSeverityToAndroidLogPriority[log_severity];
+  int priority = kLogSeverityToAndroidLogPriority[static_cast<size_t>(log_severity)];
   if (priority == ANDROID_LOG_FATAL) {
     LOG_PRI(priority, tag, "%s:%u] %s", file, line, message);
   } else {
     LOG_PRI(priority, tag, "%s", message);
   }
 #else
-  static const char* log_characters = "VDIWEFF";
+  static const char* log_characters = "NVDIWEFF";
   CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
   char severity = log_characters[log_severity];
   fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n",
@@ -264,10 +270,14 @@
 
 void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverity log_severity,
                                  const char* message) {
+  if (log_severity == LogSeverity::NONE) {
+    return;
+  }
+
 #ifdef __ANDROID__
   // Use android_writeLog() to avoid stack-based buffers used by android_printLog().
   const char* tag = ProgramInvocationShortName();
-  int priority = kLogSeverityToAndroidLogPriority[log_severity];
+  int priority = kLogSeverityToAndroidLogPriority[static_cast<size_t>(log_severity)];
   char* buf = nullptr;
   size_t buf_size = 0u;
   if (priority == ANDROID_LOG_FATAL) {
@@ -285,13 +295,14 @@
     android_writeLog(priority, tag, message);
   }
 #else
-  static const char* log_characters = "VDIWEFF";
-  CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
+  static constexpr char kLogCharacters[] = { 'N', 'V', 'D', 'I', 'W', 'E', 'F', 'F' };
+  static_assert(arraysize(kLogCharacters) == static_cast<size_t>(INTERNAL_FATAL) + 1,
+                "Wrong character array size");
 
   const char* program_name = ProgramInvocationShortName();
   TEMP_FAILURE_RETRY(write(STDERR_FILENO, program_name, strlen(program_name)));
   TEMP_FAILURE_RETRY(write(STDERR_FILENO, " ", 1));
-  TEMP_FAILURE_RETRY(write(STDERR_FILENO, &log_characters[log_severity], 1));
+  TEMP_FAILURE_RETRY(write(STDERR_FILENO, &kLogCharacters[static_cast<size_t>(log_severity)], 1));
   TEMP_FAILURE_RETRY(write(STDERR_FILENO, " ", 1));
   // TODO: pid and tid.
   TEMP_FAILURE_RETRY(write(STDERR_FILENO, file, strlen(file)));
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 8aaeaac..97280c3 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -24,6 +24,7 @@
 namespace art {
 
 enum LogSeverity {
+  NONE,            // Fake level, don't log at all.
   VERBOSE,
   DEBUG,
   INFO,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 99e38d9..f2c2f03 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3750,7 +3750,7 @@
   return false;
 }
 
-void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
+void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass, LogSeverity log_level) {
   // TODO: assert that the monitor on the Class is held
   ObjectLock<mirror::Class> lock(self, klass);
 
@@ -3853,7 +3853,7 @@
                                                              klass.Get(),
                                                              runtime->GetCompilerCallbacks(),
                                                              runtime->IsAotCompiler(),
-                                                             runtime->IsAotCompiler(),
+                                                             log_level,
                                                              &error_msg);
   }
   if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index b4fbe1c..886f586 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -449,7 +449,9 @@
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!Roles::uninterruptible_);
 
-  void VerifyClass(Thread* self, Handle<mirror::Class> klass)
+  void VerifyClass(Thread* self,
+                   Handle<mirror::Class> klass,
+                   LogSeverity log_level = LogSeverity::NONE)
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_);
   bool VerifyClassUsingOatFile(const DexFile& dex_file,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 4a0a6fc..60caa73 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -62,7 +62,12 @@
 namespace art {
 
 const uint8_t DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
-const uint8_t DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
+const uint8_t DexFile::kDexMagicVersions[DexFile::kNumDexVersions][DexFile::kDexVersionLen] = {
+  {'0', '3', '5', '\0'},
+  // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
+  // files with that version number would erroneously be accepted and run.
+  {'0', '3', '7', '\0'}
+};
 
 bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
   CHECK(checksum != nullptr);
@@ -493,7 +498,12 @@
 
 bool DexFile::IsVersionValid(const uint8_t* magic) {
   const uint8_t* version = &magic[sizeof(kDexMagic)];
-  return (memcmp(version, kDexMagicVersion, sizeof(kDexMagicVersion)) == 0);
+  for (uint32_t i = 0; i < kNumDexVersions; i++) {
+    if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) {
+      return true;
+    }
+  }
+  return false;
 }
 
 uint32_t DexFile::GetVersion() const {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index e497e9c..6849984 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -58,7 +58,10 @@
 class DexFile {
  public:
   static const uint8_t kDexMagic[];
-  static const uint8_t kDexMagicVersion[];
+  static constexpr size_t kNumDexVersions = 2;
+  static constexpr size_t kDexVersionLen = 4;
+  static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
+
   static constexpr size_t kSha1DigestSize = 20;
   static constexpr uint32_t kDexEndianConstant = 0x12345678;
 
@@ -71,7 +74,7 @@
   // The value of an invalid index.
   static const uint16_t kDexNoIndex16 = 0xFFFF;
 
-  // The separator charactor in MultiDex locations.
+  // The separator character in MultiDex locations.
   static constexpr char kMultiDexSeparator = ':';
 
   // A string version of the previous. This is a define so that we can merge string literals in the
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 6317f5e..6d1403c 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -342,11 +342,7 @@
         updated_vregs = GetThread()->GetUpdatedVRegFlags(frame_id);
         DCHECK(updated_vregs != nullptr);
       }
-      if (GetCurrentOatQuickMethodHeader()->IsOptimized()) {
-        HandleOptimizingDeoptimization(method, new_frame, updated_vregs);
-      } else {
-        HandleQuickDeoptimization(method, new_frame, updated_vregs);
-      }
+      HandleOptimizingDeoptimization(method, new_frame, updated_vregs);
       if (updated_vregs != nullptr) {
         // Calling Thread::RemoveDebuggerShadowFrameMapping will also delete the updated_vregs
         // array so this must come after we processed the frame.
@@ -475,132 +471,6 @@
     return static_cast<VRegKind>(kinds.at(reg * 2));
   }
 
-  void HandleQuickDeoptimization(ArtMethod* m,
-                                 ShadowFrame* new_frame,
-                                 const bool* updated_vregs)
-      SHARED_REQUIRES(Locks::mutator_lock_) {
-    const DexFile::CodeItem* code_item = m->GetCodeItem();
-    CHECK(code_item != nullptr) << "No code item for " << PrettyMethod(m);
-    uint16_t num_regs = code_item->registers_size_;
-    uint32_t dex_pc = GetDexPc();
-    StackHandleScope<2> hs(GetThread());  // Dex cache and class loader.
-    mirror::Class* declaring_class = m->GetDeclaringClass();
-    Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
-    Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
-    verifier::MethodVerifier verifier(GetThread(), h_dex_cache->GetDexFile(), h_dex_cache,
-                                      h_class_loader, &m->GetClassDef(), code_item,
-                                      m->GetDexMethodIndex(), m, m->GetAccessFlags(), true, true,
-                                      true, true);
-    bool verifier_success = verifier.Verify();
-    CHECK(verifier_success) << PrettyMethod(m);
-    {
-      ScopedStackedShadowFramePusher pusher(GetThread(), new_frame,
-                                            StackedShadowFrameType::kShadowFrameUnderConstruction);
-      const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
-
-      // Markers for dead values, used when the verifier knows a Dex register is undefined,
-      // or when the compiler knows the register has not been initialized, or is not used
-      // anymore in the method.
-      static constexpr uint32_t kDeadValue = 0xEBADDE09;
-      static constexpr uint64_t kLongDeadValue = 0xEBADDE09EBADDE09;
-      for (uint16_t reg = 0; reg < num_regs; ++reg) {
-        if (updated_vregs != nullptr && updated_vregs[reg]) {
-          // Keep the value set by debugger.
-          continue;
-        }
-        VRegKind kind = GetVRegKind(reg, kinds);
-        switch (kind) {
-          case kUndefined:
-            new_frame->SetVReg(reg, kDeadValue);
-            break;
-          case kConstant:
-            new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
-            break;
-          case kReferenceVReg: {
-            uint32_t value = 0;
-            // Check IsReferenceVReg in case the compiled GC map doesn't agree with the verifier.
-            // We don't want to copy a stale reference into the shadow frame as a reference.
-            // b/20736048
-            if (GetVReg(m, reg, kind, &value) && IsReferenceVReg(m, reg)) {
-              new_frame->SetVRegReference(reg, reinterpret_cast<mirror::Object*>(value));
-            } else {
-              new_frame->SetVReg(reg, kDeadValue);
-            }
-            break;
-          }
-          case kLongLoVReg:
-            if (GetVRegKind(reg + 1, kinds) == kLongHiVReg) {
-              // Treat it as a "long" register pair.
-              uint64_t value = 0;
-              if (GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &value)) {
-                new_frame->SetVRegLong(reg, value);
-              } else {
-                new_frame->SetVRegLong(reg, kLongDeadValue);
-              }
-            } else {
-              uint32_t value = 0;
-              if (GetVReg(m, reg, kind, &value)) {
-                new_frame->SetVReg(reg, value);
-              } else {
-                new_frame->SetVReg(reg, kDeadValue);
-              }
-            }
-            break;
-          case kLongHiVReg:
-            if (GetVRegKind(reg - 1, kinds) == kLongLoVReg) {
-              // Nothing to do: we treated it as a "long" register pair.
-            } else {
-              uint32_t value = 0;
-              if (GetVReg(m, reg, kind, &value)) {
-                new_frame->SetVReg(reg, value);
-              } else {
-                new_frame->SetVReg(reg, kDeadValue);
-              }
-            }
-            break;
-          case kDoubleLoVReg:
-            if (GetVRegKind(reg + 1, kinds) == kDoubleHiVReg) {
-              uint64_t value = 0;
-              if (GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &value)) {
-                // Treat it as a "double" register pair.
-                new_frame->SetVRegLong(reg, value);
-              } else {
-                new_frame->SetVRegLong(reg, kLongDeadValue);
-              }
-            } else {
-              uint32_t value = 0;
-              if (GetVReg(m, reg, kind, &value)) {
-                new_frame->SetVReg(reg, value);
-              } else {
-                new_frame->SetVReg(reg, kDeadValue);
-              }
-            }
-            break;
-          case kDoubleHiVReg:
-            if (GetVRegKind(reg - 1, kinds) == kDoubleLoVReg) {
-              // Nothing to do: we treated it as a "double" register pair.
-            } else {
-              uint32_t value = 0;
-              if (GetVReg(m, reg, kind, &value)) {
-                new_frame->SetVReg(reg, value);
-              } else {
-                new_frame->SetVReg(reg, kDeadValue);
-              }
-            }
-            break;
-          default:
-            uint32_t value = 0;
-            if (GetVReg(m, reg, kind, &value)) {
-              new_frame->SetVReg(reg, value);
-            } else {
-              new_frame->SetVReg(reg, kDeadValue);
-            }
-            break;
-        }
-      }
-    }
-  }
-
   QuickExceptionHandler* const exception_handler_;
   ShadowFrame* prev_shadow_frame_;
   bool stacked_shadow_frame_pushed_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 42b5a4a..e3adf9f 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -70,7 +70,6 @@
 #include "thread_list.h"
 #include "thread-inl.h"
 #include "utils.h"
-#include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
 #include "verify_object-inl.h"
 #include "vmap_table.h"
diff --git a/runtime/verifier/dex_gc_map.cc b/runtime/verifier/dex_gc_map.cc
deleted file mode 100644
index c435f9f..0000000
--- a/runtime/verifier/dex_gc_map.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-#include "verifier/dex_gc_map.h"
-
-#include "base/logging.h"
-
-namespace art {
-namespace verifier {
-
-const uint8_t* DexPcToReferenceMap::FindBitMap(uint16_t dex_pc, bool error_if_not_present) const {
-  size_t num_entries = NumEntries();
-  // Do linear or binary search?
-  static const size_t kSearchThreshold = 8;
-  if (num_entries < kSearchThreshold) {
-    for (size_t i = 0; i < num_entries; i++)  {
-      if (GetDexPc(i) == dex_pc) {
-        return GetBitMap(i);
-      }
-    }
-  } else {
-    int lo = 0;
-    int hi = num_entries -1;
-    while (hi >= lo) {
-      int mid = (hi + lo) / 2;
-      int mid_pc = GetDexPc(mid);
-      if (dex_pc > mid_pc) {
-        lo = mid + 1;
-      } else if (dex_pc < mid_pc) {
-        hi = mid - 1;
-      } else {
-        return GetBitMap(mid);
-      }
-    }
-  }
-  if (error_if_not_present) {
-    LOG(ERROR) << "Didn't find reference bit map for dex_pc " << dex_pc;
-  }
-  return nullptr;
-}
-
-}  // namespace verifier
-}  // namespace art
diff --git a/runtime/verifier/dex_gc_map.h b/runtime/verifier/dex_gc_map.h
deleted file mode 100644
index 03a7821..0000000
--- a/runtime/verifier/dex_gc_map.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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_RUNTIME_VERIFIER_DEX_GC_MAP_H_
-#define ART_RUNTIME_VERIFIER_DEX_GC_MAP_H_
-
-#include <stdint.h>
-
-#include "base/logging.h"
-#include "base/macros.h"
-
-namespace art {
-namespace verifier {
-
-/*
- * Format enumeration for RegisterMap data area.
- */
-enum RegisterMapFormat {
-  kRegMapFormatUnknown = 0,
-  kRegMapFormatNone = 1,       // Indicates no map data follows.
-  kRegMapFormatCompact8 = 2,   // Compact layout, 8-bit addresses.
-  kRegMapFormatCompact16 = 3,  // Compact layout, 16-bit addresses.
-};
-
-// Lightweight wrapper for Dex PC to reference bit maps.
-class DexPcToReferenceMap {
- public:
-  explicit DexPcToReferenceMap(const uint8_t* data) : data_(data) {
-    CHECK(data_ != nullptr);
-  }
-
-  // The total size of the reference bit map including header.
-  size_t RawSize() const {
-    return EntryWidth() * NumEntries() + 4u /* header */;
-  }
-
-  // The number of entries in the table
-  size_t NumEntries() const {
-    return GetData()[2] | (GetData()[3] << 8);
-  }
-
-  // Get the Dex PC at the given index
-  uint16_t GetDexPc(size_t index) const {
-    size_t entry_offset = index * EntryWidth();
-    if (DexPcWidth() == 1) {
-      return Table()[entry_offset];
-    } else {
-      return Table()[entry_offset] | (Table()[entry_offset + 1] << 8);
-    }
-  }
-
-  // Return address of bitmap encoding what are live references
-  const uint8_t* GetBitMap(size_t index) const {
-    size_t entry_offset = index * EntryWidth();
-    return &Table()[entry_offset + DexPcWidth()];
-  }
-
-  // Find the bitmap associated with the given dex pc
-  const uint8_t* FindBitMap(uint16_t dex_pc, bool error_if_not_present = true) const;
-
-  // The number of bytes used to encode registers
-  size_t RegWidth() const {
-    return GetData()[1] | ((GetData()[0] & ~kRegMapFormatMask) << kRegMapFormatShift);
-  }
-
- private:
-  // Table of num_entries * (dex pc, bitmap)
-  const uint8_t* Table() const {
-    return GetData() + 4;
-  }
-
-  // The format of the table of the PCs for the table
-  RegisterMapFormat Format() const {
-    return static_cast<RegisterMapFormat>(GetData()[0] & kRegMapFormatMask);
-  }
-
-  // Number of bytes used to encode a dex pc
-  size_t DexPcWidth() const {
-    RegisterMapFormat format = Format();
-    switch (format) {
-      case kRegMapFormatCompact8:
-        return 1;
-      case kRegMapFormatCompact16:
-        return 2;
-      default:
-        LOG(FATAL) << "Invalid format " << static_cast<int>(format);
-        return -1;
-    }
-  }
-
-  // The width of an entry in the table
-  size_t EntryWidth() const {
-    return DexPcWidth() + RegWidth();
-  }
-
-  const uint8_t* GetData() const {
-    return data_;
-  }
-
-  static const int kRegMapFormatShift = 5;
-  static const uint8_t kRegMapFormatMask = 0x7;
-
-  const uint8_t* const data_;  // The header and table data
-};
-
-}  // namespace verifier
-}  // namespace art
-
-#endif  // ART_RUNTIME_VERIFIER_DEX_GC_MAP_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 537d9c9..3d5f84e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -47,7 +47,6 @@
 #include "scoped_thread_state_change.h"
 #include "utils.h"
 #include "handle_scope-inl.h"
-#include "verifier/dex_gc_map.h"
 
 namespace art {
 namespace verifier {
@@ -123,7 +122,7 @@
                                                         mirror::Class* klass,
                                                         CompilerCallbacks* callbacks,
                                                         bool allow_soft_failures,
-                                                        bool log_hard_failures,
+                                                        LogSeverity log_level,
                                                         std::string* error) {
   if (klass->IsVerified()) {
     return kNoFailure;
@@ -162,7 +161,7 @@
                      class_def,
                      callbacks,
                      allow_soft_failures,
-                     log_hard_failures,
+                     log_level,
                      error);
 }
 
@@ -196,7 +195,7 @@
                                                           Handle<mirror::ClassLoader> class_loader,
                                                           CompilerCallbacks* callbacks,
                                                           bool allow_soft_failures,
-                                                          bool log_hard_failures,
+                                                          LogSeverity log_level,
                                                           bool need_precise_constants,
                                                           std::string* error_string) {
   DCHECK(it != nullptr);
@@ -237,7 +236,7 @@
                                                       it->GetMethodAccessFlags(),
                                                       callbacks,
                                                       allow_soft_failures,
-                                                      log_hard_failures,
+                                                      log_level,
                                                       need_precise_constants,
                                                       &hard_failure_msg);
     if (result.kind == kHardFailure) {
@@ -267,7 +266,7 @@
                                                         const DexFile::ClassDef* class_def,
                                                         CompilerCallbacks* callbacks,
                                                         bool allow_soft_failures,
-                                                        bool log_hard_failures,
+                                                        LogSeverity log_level,
                                                         std::string* error) {
   DCHECK(class_def != nullptr);
   ScopedTrace trace(__FUNCTION__);
@@ -300,7 +299,7 @@
                                                           class_loader,
                                                           callbacks,
                                                           allow_soft_failures,
-                                                          log_hard_failures,
+                                                          log_level,
                                                           false /* need precise constants */,
                                                           error);
   // Virtual methods.
@@ -313,7 +312,7 @@
                                                            class_loader,
                                                            callbacks,
                                                            allow_soft_failures,
-                                                           log_hard_failures,
+                                                           log_level,
                                                            false /* need precise constants */,
                                                            error);
 
@@ -361,15 +360,26 @@
                                                          uint32_t method_access_flags,
                                                          CompilerCallbacks* callbacks,
                                                          bool allow_soft_failures,
-                                                         bool log_hard_failures,
+                                                         LogSeverity log_level,
                                                          bool need_precise_constants,
                                                          std::string* hard_failure_msg) {
   MethodVerifier::FailureData result;
   uint64_t start_ns = kTimeVerifyMethod ? NanoTime() : 0;
 
-  MethodVerifier verifier(self, dex_file, dex_cache, class_loader, class_def, code_item,
-                          method_idx, method, method_access_flags, true, allow_soft_failures,
-                          need_precise_constants, true);
+  MethodVerifier verifier(self,
+                          dex_file,
+                          dex_cache,
+                          class_loader,
+                          class_def,
+                          code_item,
+                          method_idx,
+                          method,
+                          method_access_flags,
+                          true /* can_load_classes */,
+                          allow_soft_failures,
+                          need_precise_constants,
+                          false /* verify to dump */,
+                          true /* allow_thread_suspension */);
   if (verifier.Verify()) {
     // Verification completed, however failures may be pending that didn't cause the verification
     // to hard fail.
@@ -397,9 +407,12 @@
       result.kind = kSoftFailure;
     } else {
       CHECK(verifier.have_pending_hard_failure_);
-      if (VLOG_IS_ON(verifier) || log_hard_failures) {
-        verifier.DumpFailures(LOG(INFO) << "Verification error in "
-                                        << PrettyMethod(method_idx, *dex_file) << "\n");
+      if (VLOG_IS_ON(verifier)) {
+        log_level = LogSeverity::VERBOSE;
+      }
+      if (log_level > LogSeverity::VERBOSE) {
+        verifier.DumpFailures(LOG(log_level) << "Verification error in "
+                                             << PrettyMethod(method_idx, *dex_file) << "\n");
       }
       if (hard_failure_msg != nullptr) {
         CHECK(!verifier.failure_messages_.empty());
@@ -441,9 +454,20 @@
                                                     const DexFile::CodeItem* code_item,
                                                     ArtMethod* method,
                                                     uint32_t method_access_flags) {
-  MethodVerifier* verifier = new MethodVerifier(self, dex_file, dex_cache, class_loader,
-                                                class_def, code_item, dex_method_idx, method,
-                                                method_access_flags, true, true, true, true);
+  MethodVerifier* verifier = new MethodVerifier(self,
+                                                dex_file,
+                                                dex_cache,
+                                                class_loader,
+                                                class_def,
+                                                code_item,
+                                                dex_method_idx,
+                                                method,
+                                                method_access_flags,
+                                                true /* can_load_classes */,
+                                                true /* allow_soft_failures */,
+                                                true /* need_precise_constants */,
+                                                true /* verify_to_dump */,
+                                                true /* allow_thread_suspension */);
   verifier->Verify();
   verifier->DumpFailures(vios->Stream());
   vios->Stream() << verifier->info_messages_.str();
@@ -520,9 +544,20 @@
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
-  MethodVerifier verifier(hs.Self(), m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
-                          m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(),
-                          false, true, false, false);
+  MethodVerifier verifier(hs.Self(),
+                          m->GetDexFile(),
+                          dex_cache,
+                          class_loader,
+                          &m->GetClassDef(),
+                          m->GetCodeItem(),
+                          m->GetDexMethodIndex(),
+                          m,
+                          m->GetAccessFlags(),
+                          false /* can_load_classes */,
+                          true  /* allow_soft_failures */,
+                          false /* need_precise_constants */,
+                          false /* verify_to_dump */,
+                          false /* allow_thread_suspension */);
   verifier.interesting_dex_pc_ = dex_pc;
   verifier.monitor_enter_dex_pcs_ = monitor_enter_dex_pcs;
   verifier.FindLocksAtDexPc();
@@ -564,9 +599,20 @@
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
-  MethodVerifier verifier(hs.Self(), m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
-                          m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), true,
-                          true, false, true);
+  MethodVerifier verifier(hs.Self(),
+                          m->GetDexFile(),
+                          dex_cache,
+                          class_loader,
+                          &m->GetClassDef(),
+                          m->GetCodeItem(),
+                          m->GetDexMethodIndex(),
+                          m,
+                          m->GetAccessFlags(),
+                          true  /* can_load_classes */,
+                          true  /* allow_soft_failures */,
+                          false /* need_precise_constants */,
+                          false /* verify_to_dump */,
+                          true  /* allow_thread_suspension */);
   return verifier.FindAccessedFieldAtDexPc(dex_pc);
 }
 
@@ -593,9 +639,20 @@
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
-  MethodVerifier verifier(hs.Self(), m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
-                          m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), true,
-                          true, false, true);
+  MethodVerifier verifier(hs.Self(),
+                          m->GetDexFile(),
+                          dex_cache,
+                          class_loader,
+                          &m->GetClassDef(),
+                          m->GetCodeItem(),
+                          m->GetDexMethodIndex(),
+                          m,
+                          m->GetAccessFlags(),
+                          true  /* can_load_classes */,
+                          true  /* allow_soft_failures */,
+                          false /* need_precise_constants */,
+                          false /* verify_to_dump */,
+                          true  /* allow_thread_suspension */);
   return verifier.FindInvokedMethodAtDexPc(dex_pc);
 }
 
@@ -2425,6 +2482,10 @@
         if (!array_type.IsArrayTypes()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type "
                                             << array_type;
+        } else if (array_type.IsUnresolvedTypes()) {
+          // If it's an unresolved array type, it must be non-primitive.
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data for array of type "
+                                            << array_type;
         } else {
           const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
           DCHECK(!component_type.IsConflict());
@@ -4209,6 +4270,7 @@
       const RegType& precise_type = reg_types_.FromUninitialized(res_type);
       work_line_->SetRegisterType<LockOp::kClear>(this, inst->VRegA_22c(), precise_type);
     } else {
+      DCHECK(!res_type.IsUnresolvedMergedReference());
       // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of
       // the list and fail. It's legal, if silly, for arg_count to be zero.
       const RegType& expected_type = reg_types_.GetComponentType(res_type, GetClassLoader());
@@ -4253,6 +4315,19 @@
       }
     } else if (!array_type.IsArrayTypes()) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
+    } else if (array_type.IsUnresolvedMergedReference()) {
+      // Unresolved array types must be reference array types.
+      if (is_primitive) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "reference array type " << array_type
+                    << " source for category 1 aget";
+      } else {
+        Fail(VERIFY_ERROR_NO_CLASS) << "cannot verify aget for " << array_type
+            << " because of missing class";
+        // Approximate with java.lang.Object[].
+        work_line_->SetRegisterType<LockOp::kClear>(this,
+                                                    inst->VRegA_23x(),
+                                                    reg_types_.JavaLangObject(false));
+      }
     } else {
       /* verify the class */
       const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
@@ -4363,6 +4438,15 @@
       work_line_->VerifyRegisterType(this, inst->VRegA_23x(), *modified_reg_type);
     } else if (!array_type.IsArrayTypes()) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
+    } else if (array_type.IsUnresolvedMergedReference()) {
+      // Unresolved array types must be reference array types.
+      if (is_primitive) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "put insn has type '" << insn_type
+                                          << "' but unresolved type '" << array_type << "'";
+      } else {
+        Fail(VERIFY_ERROR_NO_CLASS) << "cannot verify aput for " << array_type
+                                    << " because of missing class";
+      }
     } else {
       const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
       const uint32_t vregA = inst->VRegA_23x();
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 6d8e1ab..ebb0b8c 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -43,7 +43,6 @@
 
 namespace verifier {
 
-class DexPcToReferenceMap;
 class MethodVerifier;
 class RegisterLine;
 using RegisterLineArenaUniquePtr = std::unique_ptr<RegisterLine, RegisterLineArenaDelete>;
@@ -143,12 +142,12 @@
     kHardFailure,
   };
 
-  /* Verify a class. Returns "kNoFailure" on success. */
+  // Verify a class. Returns "kNoFailure" on success.
   static FailureKind VerifyClass(Thread* self,
                                  mirror::Class* klass,
                                  CompilerCallbacks* callbacks,
                                  bool allow_soft_failures,
-                                 bool log_hard_failures,
+                                 LogSeverity log_level,
                                  std::string* error)
       SHARED_REQUIRES(Locks::mutator_lock_);
   static FailureKind VerifyClass(Thread* self,
@@ -158,7 +157,7 @@
                                  const DexFile::ClassDef* class_def,
                                  CompilerCallbacks* callbacks,
                                  bool allow_soft_failures,
-                                 bool log_hard_failures,
+                                 LogSeverity log_level,
                                  std::string* error)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -220,35 +219,6 @@
     return can_load_classes_;
   }
 
-  MethodVerifier(Thread* self,
-                 const DexFile* dex_file,
-                 Handle<mirror::DexCache> dex_cache,
-                 Handle<mirror::ClassLoader> class_loader,
-                 const DexFile::ClassDef* class_def,
-                 const DexFile::CodeItem* code_item,
-                 uint32_t method_idx,
-                 ArtMethod* method,
-                 uint32_t access_flags,
-                 bool can_load_classes,
-                 bool allow_soft_failures,
-                 bool need_precise_constants,
-                 bool allow_thread_suspension)
-          SHARED_REQUIRES(Locks::mutator_lock_)
-      : MethodVerifier(self,
-                       dex_file,
-                       dex_cache,
-                       class_loader,
-                       class_def,
-                       code_item,
-                       method_idx,
-                       method,
-                       access_flags,
-                       can_load_classes,
-                       allow_soft_failures,
-                       need_precise_constants,
-                       false,
-                       allow_thread_suspension) {}
-
   ~MethodVerifier();
 
   // Run verification on the method. Returns true if verification completes and false if the input
@@ -304,20 +274,6 @@
   }
 
  private:
-  void UninstantiableError(const char* descriptor);
-  static bool IsInstantiableOrPrimitive(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
-
-  // Is the method being verified a constructor? See the comment on the field.
-  bool IsConstructor() const {
-    return is_constructor_;
-  }
-
-  // Is the method verified static?
-  bool IsStatic() const {
-    return (method_access_flags_ & kAccStatic) != 0;
-  }
-
-  // Private constructor for dumping.
   MethodVerifier(Thread* self,
                  const DexFile* dex_file,
                  Handle<mirror::DexCache> dex_cache,
@@ -334,6 +290,19 @@
                  bool allow_thread_suspension)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  void UninstantiableError(const char* descriptor);
+  static bool IsInstantiableOrPrimitive(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Is the method being verified a constructor? See the comment on the field.
+  bool IsConstructor() const {
+    return is_constructor_;
+  }
+
+  // Is the method verified static?
+  bool IsStatic() const {
+    return (method_access_flags_ & kAccStatic) != 0;
+  }
+
   // Adds the given string to the beginning of the last failure message.
   void PrependToLastFailMessage(std::string);
 
@@ -362,7 +331,7 @@
                                    Handle<mirror::ClassLoader> class_loader,
                                    CompilerCallbacks* callbacks,
                                    bool allow_soft_failures,
-                                   bool log_hard_failures,
+                                   LogSeverity log_level,
                                    bool need_precise_constants,
                                    std::string* error_string)
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -388,7 +357,7 @@
                                   uint32_t method_access_flags,
                                   CompilerCallbacks* callbacks,
                                   bool allow_soft_failures,
-                                  bool log_hard_failures,
+                                  LogSeverity log_level,
                                   bool need_precise_constants,
                                   std::string* hard_failure_msg)
       SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 946f842..b036313 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -37,8 +37,13 @@
 
     // Verify the class
     std::string error_msg;
-    ASSERT_TRUE(MethodVerifier::VerifyClass(self, klass, nullptr, true, true, &error_msg)
-                    == MethodVerifier::kNoFailure) << error_msg;
+    MethodVerifier::FailureKind failure = MethodVerifier::VerifyClass(self,
+                                                                      klass,
+                                                                      nullptr,
+                                                                      true,
+                                                                      LogSeverity::WARNING,
+                                                                      &error_msg);
+    ASSERT_TRUE(failure == MethodVerifier::kNoFailure) << error_msg;
   }
 
   void VerifyDexFile(const DexFile& dex)
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 0894f5d..308c2aa 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -517,9 +517,21 @@
   }
 }
 
+bool RegType::IsJavaLangObject() const SHARED_REQUIRES(Locks::mutator_lock_) {
+  return IsReference() && GetClass()->IsObjectClass();
+}
+
 bool RegType::IsObjectArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_) {
-  if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
-    // Primitive arrays will always resolve
+  if (IsUnresolvedTypes()) {
+    DCHECK(!IsUnresolvedMergedReference());
+
+    if (IsUnresolvedSuperClass()) {
+      // Cannot be an array, as the superclass of arrays is java.lang.Object (which cannot be
+      // unresolved).
+      return false;
+    }
+
+    // Primitive arrays will always resolve.
     DCHECK(descriptor_[1] == 'L' || descriptor_[1] == '[');
     return descriptor_[0] == '[';
   } else if (HasClass()) {
@@ -530,12 +542,15 @@
   }
 }
 
-bool RegType::IsJavaLangObject() const SHARED_REQUIRES(Locks::mutator_lock_) {
-  return IsReference() && GetClass()->IsObjectClass();
-}
-
 bool RegType::IsArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_) {
-  if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
+  if (IsUnresolvedTypes()) {
+    DCHECK(!IsUnresolvedMergedReference());
+
+    if (IsUnresolvedSuperClass()) {
+      // Cannot be an array, as the superclass of arrays is java.lang.Object (which cannot be
+      // unresolved).
+      return false;
+    }
     return descriptor_[0] == '[';
   } else if (HasClass()) {
     return GetClass()->IsArrayClass();
@@ -793,11 +808,50 @@
   }
 }
 void UnresolvedMergedType::CheckInvariants() const {
+  CHECK(reg_type_cache_ != nullptr);
+
   // Unresolved merged types: merged types should be defined.
   CHECK(descriptor_.empty()) << *this;
   CHECK(klass_.IsNull()) << *this;
+
+  CHECK(!resolved_part_.IsConflict());
   CHECK(resolved_part_.IsReferenceTypes());
   CHECK(!resolved_part_.IsUnresolvedTypes());
+
+  CHECK(resolved_part_.IsZero() ||
+        !(resolved_part_.IsArrayTypes() && !resolved_part_.IsObjectArrayTypes()));
+
+  CHECK_GT(unresolved_types_.NumSetBits(), 0U);
+  bool unresolved_is_array =
+      reg_type_cache_->GetFromId(unresolved_types_.GetHighestBitSet()).IsArrayTypes();
+  for (uint32_t idx : unresolved_types_.Indexes()) {
+    const RegType& t = reg_type_cache_->GetFromId(idx);
+    CHECK_EQ(unresolved_is_array, t.IsArrayTypes());
+  }
+
+  if (!resolved_part_.IsZero()) {
+    CHECK_EQ(resolved_part_.IsArrayTypes(), unresolved_is_array);
+  }
+}
+
+bool UnresolvedMergedType::IsArrayTypes() const {
+  // For a merge to be an array, both the resolved and the unresolved part need to be object
+  // arrays.
+  // (Note: we encode a missing resolved part [which doesn't need to be an array] as zero.)
+
+  if (!resolved_part_.IsZero() && !resolved_part_.IsArrayTypes()) {
+    return false;
+  }
+
+  // It is enough to check just one of the merged types. Otherwise the merge should have been
+  // collapsed (checked in CheckInvariants on construction).
+  uint32_t idx = unresolved_types_.GetHighestBitSet();
+  const RegType& unresolved = reg_type_cache_->GetFromId(idx);
+  return unresolved.IsArrayTypes();
+}
+bool UnresolvedMergedType::IsObjectArrayTypes() const {
+  // Same as IsArrayTypes, as primitive arrays are always resolved.
+  return IsArrayTypes();
 }
 
 void UnresolvedReferenceType::CheckInvariants() const {
@@ -824,6 +878,14 @@
     return false;
   }
 
+  if (IsUnresolvedMergedReference() || src.IsUnresolvedMergedReference()) {
+    // An unresolved array type means that it's an array of some reference type. Reference arrays
+    // can never be assigned to primitive-type arrays, and vice versa. So it is a soft error if
+    // both arrays are reference arrays, otherwise a hard error.
+    *soft_error = IsObjectArrayTypes() && src.IsObjectArrayTypes();
+    return false;
+  }
+
   const RegType& cmp1 = reg_types.GetComponentType(*this, class_loader.Get());
   const RegType& cmp2 = reg_types.GetComponentType(src, class_loader.Get());
 
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 7c7981e..4837490 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -172,8 +172,8 @@
   }
   virtual bool HasClassVirtual() const { return false; }
   bool IsJavaLangObject() const SHARED_REQUIRES(Locks::mutator_lock_);
-  bool IsArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
-  bool IsObjectArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
+  virtual bool IsArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
+  virtual bool IsObjectArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
   Primitive::Type GetPrimitiveType() const;
   bool IsJavaLangObjectArray() const
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -905,6 +905,9 @@
 
   bool IsUnresolvedTypes() const OVERRIDE { return true; }
 
+  bool IsArrayTypes() const OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+  bool IsObjectArrayTypes() const OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+
   std::string Dump() const OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
 
  private:
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index b171b75..71c2a90 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -347,29 +347,39 @@
                        kDefaultArenaBitVectorBytes * kBitsPerByte,  // Allocate at least 8 bytes.
                        true);                                       // Is expandable.
   const RegType* left_resolved;
+  bool left_unresolved_is_array;
   if (left.IsUnresolvedMergedReference()) {
-    const UnresolvedMergedType* left_merge = down_cast<const UnresolvedMergedType*>(&left);
-    types.Copy(&left_merge->GetUnresolvedTypes());
-    left_resolved = &left_merge->GetResolvedPart();
+    const UnresolvedMergedType& left_merge = *down_cast<const UnresolvedMergedType*>(&left);
+
+    types.Copy(&left_merge.GetUnresolvedTypes());
+    left_resolved = &left_merge.GetResolvedPart();
+    left_unresolved_is_array = left.IsArrayTypes();
   } else if (left.IsUnresolvedTypes()) {
     types.ClearAllBits();
     types.SetBit(left.GetId());
     left_resolved = &Zero();
+    left_unresolved_is_array = left.IsArrayTypes();
   } else {
     types.ClearAllBits();
     left_resolved = &left;
+    left_unresolved_is_array = false;
   }
 
   const RegType* right_resolved;
+  bool right_unresolved_is_array;
   if (right.IsUnresolvedMergedReference()) {
-    const UnresolvedMergedType* right_merge = down_cast<const UnresolvedMergedType*>(&right);
-    types.Union(&right_merge->GetUnresolvedTypes());
-    right_resolved = &right_merge->GetResolvedPart();
+    const UnresolvedMergedType& right_merge = *down_cast<const UnresolvedMergedType*>(&right);
+
+    types.Union(&right_merge.GetUnresolvedTypes());
+    right_resolved = &right_merge.GetResolvedPart();
+    right_unresolved_is_array = right.IsArrayTypes();
   } else if (right.IsUnresolvedTypes()) {
     types.SetBit(right.GetId());
     right_resolved = &Zero();
+    right_unresolved_is_array = right.IsArrayTypes();
   } else {
     right_resolved = &right;
+    right_unresolved_is_array = false;
   }
 
   // Merge the resolved parts. Left and right might be equal, so use SafeMerge.
@@ -379,6 +389,23 @@
     return Conflict();
   }
 
+  bool resolved_merged_is_array = resolved_parts_merged.IsArrayTypes();
+  if (left_unresolved_is_array || right_unresolved_is_array || resolved_merged_is_array) {
+    // Arrays involved, see if we need to merge to Object.
+
+    // Is the resolved part a primitive array?
+    if (resolved_merged_is_array && !resolved_parts_merged.IsObjectArrayTypes()) {
+      return JavaLangObject(false /* precise */);
+    }
+
+    // Is any part not an array (but exists)?
+    if ((!left_unresolved_is_array && left_resolved != &left) ||
+        (!right_unresolved_is_array && right_resolved != &right) ||
+        !resolved_merged_is_array) {
+      return JavaLangObject(false /* precise */);
+    }
+  }
+
   // Check if entry already exists.
   for (size_t i = primitive_count_; i < entries_.size(); i++) {
     const RegType* cur_entry = entries_[i];
@@ -584,6 +611,7 @@
   if (!array.IsArrayTypes()) {
     return Conflict();
   } else if (array.IsUnresolvedTypes()) {
+    DCHECK(!array.IsUnresolvedMergedReference());  // Caller must make sure not to ask for this.
     const std::string descriptor(array.GetDescriptor().as_string());
     return FromDescriptor(loader, descriptor.c_str() + 1, false);
   } else {
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 29d87c4..d2f3485 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -127,17 +127,6 @@
   }
 }
 
-inline size_t RegisterLine::GetMaxNonZeroReferenceReg(MethodVerifier* verifier,
-                                                      size_t max_ref_reg) const {
-  size_t i = static_cast<int>(max_ref_reg) < 0 ? 0 : max_ref_reg;
-  for (; i < num_regs_; i++) {
-    if (GetRegisterType(verifier, i).IsNonZeroReferenceTypes()) {
-      max_ref_reg = i;
-    }
-  }
-  return max_ref_reg;
-}
-
 inline bool RegisterLine::VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc,
                                              const RegType& check_type) {
   // Verify the src register type against the check type refining the type of the register
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 82c371d..71aa94e 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -543,24 +543,5 @@
   return changed;
 }
 
-void RegisterLine::WriteReferenceBitMap(MethodVerifier* verifier,
-                                        std::vector<uint8_t>* data, size_t max_bytes) {
-  for (size_t i = 0; i < num_regs_; i += 8) {
-    uint8_t val = 0;
-    for (size_t j = 0; j < 8 && (i + j) < num_regs_; j++) {
-      // Note: we write 1 for a Reference but not for Null
-      if (GetRegisterType(verifier, i + j).IsNonZeroReferenceTypes()) {
-        val |= 1 << j;
-      }
-    }
-    if ((i / 8) >= max_bytes) {
-      DCHECK_EQ(0, val);
-      continue;
-    }
-    DCHECK_LT(i / 8, max_bytes) << "val=" << static_cast<uint32_t>(val);
-    data->push_back(val);
-  }
-}
-
 }  // namespace verifier
 }  // namespace art
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 15ae202..56846c1 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -346,11 +346,6 @@
   bool MergeRegisters(MethodVerifier* verifier, const RegisterLine* incoming_line)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  size_t GetMaxNonZeroReferenceReg(MethodVerifier* verifier, size_t max_ref_reg) const;
-
-  // Write a bit at each register location that holds a reference.
-  void WriteReferenceBitMap(MethodVerifier* verifier, std::vector<uint8_t>* data, size_t max_bytes);
-
   size_t GetMonitorEnterCount() const {
     return monitors_.size();
   }
diff --git a/test/370-dex-v37/build b/test/370-dex-v37/build
new file mode 100755
index 0000000..f472428
--- /dev/null
+++ b/test/370-dex-v37/build
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@"
+
+if [[ $@ != *"--jvm"* ]]; then
+  # Change the generated dex file to have a v36 magic number if it is version 35
+  if test -f classes.dex && head -c 7 classes.dex | grep -q 035; then
+    # place ascii value '037' into the classes.dex file starting at byte 4.
+    printf '037' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
+    rm -f $TEST_NAME.jar
+    zip $TEST_NAME.jar classes.dex
+  fi
+fi
diff --git a/test/370-dex-v37/expected.txt b/test/370-dex-v37/expected.txt
new file mode 100644
index 0000000..af5626b
--- /dev/null
+++ b/test/370-dex-v37/expected.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/test/370-dex-v37/info.txt b/test/370-dex-v37/info.txt
new file mode 100644
index 0000000..5ca9c76
--- /dev/null
+++ b/test/370-dex-v37/info.txt
@@ -0,0 +1 @@
+Print "Hello, World!" with a version 37 dex file.
diff --git a/test/370-dex-v37/src/Main.java b/test/370-dex-v37/src/Main.java
new file mode 100644
index 0000000..1ef6289
--- /dev/null
+++ b/test/370-dex-v37/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
+  }
+}
diff --git a/test/591-new-instance-string/expected.txt b/test/591-new-instance-string/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/591-new-instance-string/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/591-new-instance-string/info.txt b/test/591-new-instance-string/info.txt
new file mode 100644
index 0000000..16c7b1f
--- /dev/null
+++ b/test/591-new-instance-string/info.txt
@@ -0,0 +1 @@
+Regression test on new-instance that reaches multiple <init> calls.
diff --git a/test/591-new-instance-string/smali/new-instance.smali b/test/591-new-instance-string/smali/new-instance.smali
new file mode 100644
index 0000000..42559ca
--- /dev/null
+++ b/test/591-new-instance-string/smali/new-instance.smali
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2016 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.
+
+.class public LNewInstance;
+.super Ljava/lang/Object;
+
+.method public static multipleInit(I)V
+    .registers 2
+    new-instance v0, Ljava/lang/String;
+    if-eqz v1, :Skip
+    invoke-direct {v0}, Ljava/lang/String;-><init>()V
+    goto :Done
+:Skip
+    invoke-direct {v0}, Ljava/lang/String;-><init>()V
+:Done
+    return-void
+.end method
diff --git a/test/591-new-instance-string/src/Main.java b/test/591-new-instance-string/src/Main.java
new file mode 100644
index 0000000..bd59b95
--- /dev/null
+++ b/test/591-new-instance-string/src/Main.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.*;
+
+public class Main {
+
+  public static void main(String args[]) throws Throwable {
+    Class<?> c = Class.forName("NewInstance");
+    Method m = c.getMethod("multipleInit", int.class);
+    m.invoke(null, 0);
+    m.invoke(null, 1);
+    System.out.println("passed");
+  }
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 8808a50..c2a9a31 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -60,4 +60,10 @@
 b/26594149 (8)
 b/27148248
 b/26965384
+b/27799205 (1)
+b/27799205 (2)
+b/27799205 (3)
+b/27799205 (4)
+b/27799205 (5)
+b/27799205 (6)
 Done!
diff --git a/test/800-smali/smali/b_27799205_1.smali b/test/800-smali/smali/b_27799205_1.smali
new file mode 100644
index 0000000..92bfc80
--- /dev/null
+++ b/test/800-smali/smali/b_27799205_1.smali
@@ -0,0 +1,37 @@
+.class public LB27799205_1;
+.super Ljava/lang/Object;
+
+# A class with an unresolved array type should not fail hard (unless it's a primitive-type access).
+
+.method public static run()V
+.registers 1
+       return-void
+.end method
+
+.method public static test([Ljava/lang/Object;[Ldo/not/resolve/K;Z)V
+.registers 6
+       # Make v0, v1 and v2 null. We'll use v0 as a merge of the inputs, v1 as null, and v2 as 0.
+       const v0, 0
+       const v1, 0
+       const v2, 0
+
+       # Conditional jump so we have a merge point.
+       if-eqz v5, :LabelSelectUnresolved
+
+:LabelSelectResolved
+       move-object v0, v3
+       goto :LabelMerged
+
+:LabelSelectUnresolved
+       move-object v0, v4
+       goto :LabelMerged
+
+:LabelMerged
+       # At this point, v0 will be the unresolved merge.
+
+       # Test aput: v0[v2] = v1.
+       aput-object v1, v0, v2
+
+       return-void
+
+.end method
diff --git a/test/800-smali/smali/b_27799205_2.smali b/test/800-smali/smali/b_27799205_2.smali
new file mode 100644
index 0000000..e730b1e
--- /dev/null
+++ b/test/800-smali/smali/b_27799205_2.smali
@@ -0,0 +1,37 @@
+.class public LB27799205_2;
+.super Ljava/lang/Object;
+
+# A class with an unresolved array type should not fail hard (unless it's a primitive-type access).
+
+.method public static run()V
+.registers 1
+       return-void
+.end method
+
+.method public static test([Ljava/lang/Object;[Ldo/not/resolve/K;Z)V
+.registers 6
+       # Make v0, v1 and v2 null. We'll use v0 as a merge of the inputs, v1 as null, and v2 as 0.
+       const v0, 0
+       const v1, 0
+       const v2, 0
+
+       # Conditional jump so we have a merge point.
+       if-eqz v5, :LabelSelectUnresolved
+
+:LabelSelectResolved
+       move-object v0, v3
+       goto :LabelMerged
+
+:LabelSelectUnresolved
+       move-object v0, v4
+       goto :LabelMerged
+
+:LabelMerged
+       # At this point, v0 will be the unresolved merge.
+
+       # Test aput: v0[v2] = v1.
+       aput v1, v0, v2
+
+       return-void
+
+.end method
diff --git a/test/800-smali/smali/b_27799205_3.smali b/test/800-smali/smali/b_27799205_3.smali
new file mode 100644
index 0000000..1cb025e
--- /dev/null
+++ b/test/800-smali/smali/b_27799205_3.smali
@@ -0,0 +1,39 @@
+.class public LB27799205_3;
+.super Ljava/lang/Object;
+
+# A class with an unresolved array type should not fail hard (unless it's a primitive-type access).
+# Make sure that merging is pro-active.
+
+.method public static run()V
+.registers 1
+       return-void
+.end method
+
+# Use some non-object non-array input (non-Object because the merge should be Object).
+.method public static test(Ljava/lang/Integer;[Ldo/not/resolve/K;Z)V
+.registers 6
+       # Make v0, v1 and v2 null. We'll use v0 as a merge of the inputs, v1 as null, and v2 as 0.
+       const v0, 0
+       const v1, 0
+       const v2, 0
+
+       # Conditional jump so we have a merge point.
+       if-eqz v5, :LabelSelectUnresolved
+
+:LabelSelectResolved
+       move-object v0, v3
+       goto :LabelMerged
+
+:LabelSelectUnresolved
+       move-object v0, v4
+       goto :LabelMerged
+
+:LabelMerged
+       # At this point, v0 should be Object.
+
+       # Test aput-object: v0[v2] = v1. Should fail for v0 not being an array.
+       aput-object v1, v0, v2
+
+       return-void
+
+.end method
diff --git a/test/800-smali/smali/b_27799205_4.smali b/test/800-smali/smali/b_27799205_4.smali
new file mode 100644
index 0000000..e42951a
--- /dev/null
+++ b/test/800-smali/smali/b_27799205_4.smali
@@ -0,0 +1,39 @@
+.class public LB27799205_4;
+.super Ljava/lang/Object;
+
+# A class with an unresolved array type should not fail hard (unless it's a primitive-type access).
+# Make sure that merging is pro-active.
+
+.method public static run()V
+.registers 1
+       return-void
+.end method
+
+# Use some primitive-type array input.
+.method public static test([I[Ldo/not/resolve/K;Z)V
+.registers 6
+       # Make v0, v1 and v2 null. We'll use v0 as a merge of the inputs, v1 as null, and v2 as 0.
+       const v0, 0
+       const v1, 0
+       const v2, 0
+
+       # Conditional jump so we have a merge point.
+       if-eqz v5, :LabelSelectUnresolved
+
+:LabelSelectResolved
+       move-object v0, v3
+       goto :LabelMerged
+
+:LabelSelectUnresolved
+       move-object v0, v4
+       goto :LabelMerged
+
+:LabelMerged
+       # At this point, v0 should be Object.
+
+       # Test aput-object: v0[v2] = v1. Should fail for v0 not being an array.
+       aput-object v1, v0, v2
+
+       return-void
+
+.end method
diff --git a/test/800-smali/smali/b_27799205_5.smali b/test/800-smali/smali/b_27799205_5.smali
new file mode 100644
index 0000000..6c7b183
--- /dev/null
+++ b/test/800-smali/smali/b_27799205_5.smali
@@ -0,0 +1,39 @@
+.class public LB27799205_5;
+.super Ljava/lang/Object;
+
+# A class with an unresolved array type should not fail hard (unless it's a primitive-type access).
+# Make sure that merging is pro-active.
+
+.method public static run()V
+.registers 1
+       return-void
+.end method
+
+# Use some non-resolvable non-array type.
+.method public static test(Ldo/not/resolve/L;[Ldo/not/resolve/K;Z)V
+.registers 6
+       # Make v0, v1 and v2 null. We'll use v0 as a merge of the inputs, v1 as null, and v2 as 0.
+       const v0, 0
+       const v1, 0
+       const v2, 0
+
+       # Conditional jump so we have a merge point.
+       if-eqz v5, :LabelSelectUnresolved
+
+:LabelSelectResolved
+       move-object v0, v3
+       goto :LabelMerged
+
+:LabelSelectUnresolved
+       move-object v0, v4
+       goto :LabelMerged
+
+:LabelMerged
+       # At this point, v0 should be Object.
+
+       # Test aput-object: v0[v2] = v1. Should fail for v0 not being an array.
+       aput-object v1, v0, v2
+
+       return-void
+
+.end method
diff --git a/test/800-smali/smali/b_27799205_6.smali b/test/800-smali/smali/b_27799205_6.smali
new file mode 100644
index 0000000..d0154f7
--- /dev/null
+++ b/test/800-smali/smali/b_27799205_6.smali
@@ -0,0 +1,24 @@
+.class public LB27799205_6;
+.super Ljava/lang/Object;
+
+# A class with an unresolved array type should not fail hard (unless it's a primitive-type access).
+# Make sure that non-merged types still work.
+
+.method public static run()V
+.registers 1
+       return-void
+.end method
+
+# Use some non-resolvable array type.
+.method public static test([Ldo/not/resolve/K;)Ldo/not/resolve/K;
+.registers 3
+       const v0, 0
+       const v1, 0
+       # v2 = p0
+
+       # v0 := v2[v1]
+       aget-object v0, v2, v1
+
+       return-object v0
+
+.end method
diff --git a/test/800-smali/smali/b_27799205_helper.smali b/test/800-smali/smali/b_27799205_helper.smali
new file mode 100644
index 0000000..e6d0985
--- /dev/null
+++ b/test/800-smali/smali/b_27799205_helper.smali
@@ -0,0 +1,47 @@
+.class public LB27799205Helper;
+.super Ljava/lang/Object;
+
+# Helper for B27799205. Reflection tries to resolve all types. That's bad for intentionally
+# unresolved types. It makes it harder to distinguish what kind of error we got.
+
+.method public static run1()V
+.registers 1
+       invoke-static {}, LB27799205_1;->run()V
+
+       return-void
+.end method
+
+.method public static run2()V
+.registers 1
+       invoke-static {}, LB27799205_2;->run()V
+
+       return-void
+.end method
+
+.method public static run3()V
+.registers 1
+       invoke-static {}, LB27799205_3;->run()V
+
+       return-void
+.end method
+
+.method public static run4()V
+.registers 1
+       invoke-static {}, LB27799205_4;->run()V
+
+       return-void
+.end method
+
+.method public static run5()V
+.registers 1
+       invoke-static {}, LB27799205_5;->run()V
+
+       return-void
+.end method
+
+.method public static run6()V
+.registers 1
+       invoke-static {}, LB27799205_6;->run()V
+
+       return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 4e6de46..2001cb4 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -164,6 +164,16 @@
                 null));
         testCases.add(new TestCase("b/26965384", "B26965384", "run", null, new VerifyError(),
                 null));
+        testCases.add(new TestCase("b/27799205 (1)", "B27799205Helper", "run1", null, null, null));
+        testCases.add(new TestCase("b/27799205 (2)", "B27799205Helper", "run2", null,
+                new VerifyError(), null));
+        testCases.add(new TestCase("b/27799205 (3)", "B27799205Helper", "run3", null,
+                new VerifyError(), null));
+        testCases.add(new TestCase("b/27799205 (4)", "B27799205Helper", "run4", null,
+                new VerifyError(), null));
+        testCases.add(new TestCase("b/27799205 (5)", "B27799205Helper", "run5", null,
+                new VerifyError(), null));
+        testCases.add(new TestCase("b/27799205 (6)", "B27799205Helper", "run6", null, null, null));
     }
 
     public void runTests() {