Merge "Disable test-660 for no-prebuild"
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 24e3450..2cbabcf 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -2920,6 +2920,102 @@
                 static_cast<FRegister>(wt));
 }
 
+void MipsAssembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b),
+                static_cast<FRegister>(wd),
+                static_cast<FRegister>(ws),
+                static_cast<FRegister>(wt));
+}
+
 void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst,
                                                 FRegister src,
                                                 bool is_double) {
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index e42bb3f..a7ff931 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -613,6 +613,19 @@
   void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
   void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
 
+  void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
   // Helper for replicating floating point value in all destination elements.
   void ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double);
 
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index 6ee2a5c..b72a14e 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -1752,6 +1752,66 @@
   DriverStr(RepeatVVV(&mips::MipsAssembler::IlvrD, "ilvr.d ${reg1}, ${reg2}, ${reg3}"), "ilvr.d");
 }
 
+TEST_F(AssemblerMIPS32r6Test, MaddvB) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.b");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvH) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.h");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvW) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvD) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvB) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.b");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvH) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.h");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvW) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvD) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmaddW) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"),
+            "fmadd.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmaddD) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"),
+            "fmadd.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmsubW) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"),
+            "fmsub.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmsubD) {
+  DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"),
+            "fmsub.d");
+}
+
 #undef __
 
 }  // namespace art
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 9039854..7a1beb6 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -1899,6 +1899,66 @@
   EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14);
 }
 
+void Mips64Assembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+  CHECK(HasMsa());
+  EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b);
+}
+
 void Mips64Assembler::ReplicateFPToVectorRegister(VectorRegister dst,
                                                   FpuRegister src,
                                                   bool is_double) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 5e88033..c39d120 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -796,6 +796,19 @@
   void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
   void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
 
+  void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+  void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
   // Helper for replicating floating point value in all destination elements.
   void ReplicateFPToVectorRegister(VectorRegister dst, FpuRegister src, bool is_double);
 
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index bdf9598..021e335 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -3340,6 +3340,66 @@
             "ilvr.d");
 }
 
+TEST_F(AssemblerMIPS64Test, MaddvB) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvH) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvW) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvD) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"),
+            "maddv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvB) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvH) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvW) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvD) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"),
+            "msubv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FmaddW) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"),
+            "fmadd.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FmaddD) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"),
+            "fmadd.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FmsubW) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"),
+            "fmsub.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FmsubD) {
+  DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"),
+            "fmsub.d");
+}
+
 #undef __
 
 }  // namespace art
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 346f5a7..0d453ef 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -30,15 +30,6 @@
         android: {
             // Use the 32-bit version of dex2oat on devices
             compile_multilib: "prefer32",
-
-            sanitize: {
-                // ASan slows down dex2oat by ~3.5x, which translates into
-                // extremely slow first boot. Disabled to help speed up
-                // SANITIZE_TARGET mode.
-                // Bug: 22233158
-                address: false,
-                coverage: false,
-            },
         },
     },
 
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 7cb216e..1a395a4 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -477,6 +477,10 @@
   { kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" },
   { kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" },
   { kMsaMask | (0x7 << 23), kMsa | (0x5 << 23) | 0x14, "ilvr", "Vkmn" },
+  { kMsaMask | (0x7 << 23), kMsa | (0x1 << 23) | 0x12, "maddv", "Vkmn" },
+  { kMsaMask | (0x7 << 23), kMsa | (0x2 << 23) | 0x12, "msubv", "Vkmn" },
+  { kMsaMask | (0xf << 22), kMsa | (0x4 << 22) | 0x1b, "fmadd", "Ukmn" },
+  { kMsaMask | (0xf << 22), kMsa | (0x5 << 22) | 0x1b, "fmsub", "Ukmn" },
 };
 
 static uint32_t ReadU32(const uint8_t* ptr) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 74c04d1..d06ba78 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -370,7 +370,8 @@
       quick_imt_conflict_trampoline_(nullptr),
       quick_generic_jni_trampoline_(nullptr),
       quick_to_interpreter_bridge_trampoline_(nullptr),
-      image_pointer_size_(kRuntimePointerSize) {
+      image_pointer_size_(kRuntimePointerSize),
+      cha_(new ClassHierarchyAnalysis()) {
   CHECK(intern_table_ != nullptr);
   static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_),
                 "Array cache size wrong.");
@@ -1138,49 +1139,6 @@
   const ImageHeader& header_;
 };
 
-class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
- public:
-  explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {}
-
-  virtual void Visit(ArtMethod* method)
-      REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) {
-    ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
-    if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
-      CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass);
-    }
-  }
-
- private:
-  ClassTable* const table_;
-};
-
-class VerifyDirectInterfacesInTableClassVisitor {
- public:
-  explicit VerifyDirectInterfacesInTableClassVisitor(ObjPtr<mirror::ClassLoader> class_loader)
-      : class_loader_(class_loader), self_(Thread::Current()) { }
-
-  bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader_) {
-      classes_.push_back(klass);
-    }
-    return true;
-  }
-
-  void Check() const REQUIRES_SHARED(Locks::mutator_lock_) {
-    for (ObjPtr<mirror::Class> klass : classes_) {
-      for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
-        CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr)
-            << klass->PrettyDescriptor() << " iface #" << i;
-      }
-    }
-  }
-
- private:
-  ObjPtr<mirror::ClassLoader> class_loader_;
-  Thread* self_;
-  std::vector<ObjPtr<mirror::Class>> classes_;
-};
-
 class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
  public:
   VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
@@ -1763,6 +1721,63 @@
   std::vector<const ImageSection*> runtime_method_sections_;
 };
 
+static void VerifyAppImage(const ImageHeader& header,
+                           const Handle<mirror::ClassLoader>& class_loader,
+                           const Handle<mirror::ObjectArray<mirror::DexCache> >& dex_caches,
+                           ClassTable* class_table, gc::space::ImageSpace* space)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  {
+    class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
+     public:
+      explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {}
+
+      virtual void Visit(ArtMethod* method)
+          REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) {
+        ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
+        if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+          CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass);
+        }
+      }
+
+     private:
+      ClassTable* const table_;
+    };
+    VerifyClassInTableArtMethodVisitor visitor(class_table);
+    header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
+  }
+  {
+    // Verify that all direct interfaces of classes in the class table are also resolved.
+    std::vector<ObjPtr<mirror::Class>> classes;
+    auto verify_direct_interfaces_in_table = [&](ObjPtr<mirror::Class> klass)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader.Get()) {
+        classes.push_back(klass);
+      }
+      return true;
+    };
+    class_table->Visit(verify_direct_interfaces_in_table);
+    Thread* self = Thread::Current();
+    for (ObjPtr<mirror::Class> klass : classes) {
+      for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
+        CHECK(klass->GetDirectInterface(self, klass, i) != nullptr)
+            << klass->PrettyDescriptor() << " iface #" << i;
+      }
+    }
+  }
+  // Check that all non-primitive classes in dex caches are also in the class table.
+  for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+    ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+    mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
+    for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+      ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
+      if (klass != nullptr && !klass->IsPrimitive()) {
+        CHECK(class_table->Contains(klass))
+            << klass->PrettyDescriptor() << " " << dex_cache->GetDexFile()->GetLocation();
+      }
+    }
+  }
+}
+
 bool ClassLinker::AddImageSpace(
     gc::space::ImageSpace* space,
     Handle<mirror::ClassLoader> class_loader,
@@ -2016,28 +2031,13 @@
     WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
     class_table->AddClassSet(std::move(temp_set));
   }
+
   if (kIsDebugBuild && app_image) {
     // This verification needs to happen after the classes have been added to the class loader.
     // Since it ensures classes are in the class table.
-    VerifyClassInTableArtMethodVisitor visitor2(class_table);
-    header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize);
-    // Verify that all direct interfaces of classes in the class table are also resolved.
-    VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get());
-    class_table->Visit(visitor);
-    visitor.Check();
-    // Check that all non-primitive classes in dex caches are also in the class table.
-    for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
-      ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
-      mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
-      for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
-        ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
-        if (klass != nullptr && !klass->IsPrimitive()) {
-          CHECK(class_table->Contains(klass)) << klass->PrettyDescriptor()
-              << " " << dex_cache->GetDexFile()->GetLocation();
-        }
-      }
-    }
+    VerifyAppImage(header, class_loader, dex_caches, class_table, space);
   }
+
   VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
   return true;
 }
@@ -2315,16 +2315,15 @@
   JavaVMExt* const vm = runtime->GetJavaVM();
   vm->DeleteWeakGlobalRef(self, data.weak_root);
   // Notify the JIT that we need to remove the methods and/or profiling info.
-  ClassHierarchyAnalysis* const cha = runtime->GetClassHierarchyAnalysis();
   if (runtime->GetJit() != nullptr) {
     jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
     if (code_cache != nullptr) {
       // For the JIT case, RemoveMethodsIn removes the CHA dependencies.
       code_cache->RemoveMethodsIn(self, *data.allocator);
     }
-  } else {
+  } else if (cha_ != nullptr) {
     // If we don't have a JIT, we need to manually remove the CHA dependencies manually.
-    cha->RemoveDependenciesForLinearAlloc(data.allocator);
+    cha_->RemoveDependenciesForLinearAlloc(data.allocator);
   }
   delete data.allocator;
   delete data.class_table;
@@ -3494,7 +3493,8 @@
                                         ObjPtr<mirror::DexCache> dex_cache) {
   CHECK(dex_cache != nullptr) << dex_file.GetLocation();
   boot_class_path_.push_back(&dex_file);
-  RegisterBootClassPathDexFile(dex_file, dex_cache);
+  WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_);
+  RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr);
 }
 
 void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file,
@@ -3677,12 +3677,6 @@
   return h_dex_cache.Get();
 }
 
-void ClassLinker::RegisterBootClassPathDexFile(const DexFile& dex_file,
-                                               ObjPtr<mirror::DexCache> dex_cache) {
-  WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_);
-  RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr);
-}
-
 bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) {
   ReaderMutexLock mu(self, *Locks::dex_lock_);
   return DecodeDexCache(self, FindDexCacheDataLocked(dex_file)) != nullptr;
@@ -5487,7 +5481,9 @@
     // Update CHA info based on whether we override methods.
     // Have to do this before setting the class as resolved which allows
     // instantiation of klass.
-    Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(klass);
+    if (cha_ != nullptr) {
+      cha_->UpdateAfterLoadingOf(klass);
+    }
 
     // This will notify waiters on klass that saw the not yet resolved
     // class in the class_table_ during EnsureResolved.
@@ -5535,7 +5531,9 @@
     // Update CHA info based on whether we override methods.
     // Have to do this before setting the class as resolved which allows
     // instantiation of klass.
-    Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(h_new_class);
+    if (cha_ != nullptr) {
+      cha_->UpdateAfterLoadingOf(h_new_class);
+    }
 
     // This will notify waiters on temp class that saw the not yet resolved class in the
     // class_table_ during EnsureResolved.
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cb28187..62fb45b 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -60,6 +60,7 @@
   using MethodDexCacheType = std::atomic<MethodDexCachePair>;
 }  // namespace mirror
 
+class ClassHierarchyAnalysis;
 class ClassTable;
 template<class T> class Handle;
 class ImtConflictTable;
@@ -396,9 +397,6 @@
                                            ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES(!Locks::dex_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void RegisterBootClassPathDexFile(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache)
-      REQUIRES(!Locks::dex_lock_)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   const std::vector<const DexFile*>& GetBootClassPath() {
     return boot_class_path_;
@@ -672,6 +670,10 @@
   bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  ClassHierarchyAnalysis* GetClassHierarchyAnalysis() {
+    return cha_.get();
+  }
+
   struct DexCacheData {
     // Construct an invalid data object.
     DexCacheData()
@@ -718,7 +720,7 @@
       REQUIRES(!Locks::dex_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  static void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
+  void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void VisitClassesInternal(ClassVisitor* visitor)
@@ -1268,6 +1270,8 @@
   // Image pointer size.
   PointerSize image_pointer_size_;
 
+  std::unique_ptr<ClassHierarchyAnalysis> cha_;
+
   class FindVirtualMethodHolderVisitor;
 
   friend class AppImageClassLoadersAndDexCachesHelper;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 2aa2ff2..a030a51 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -642,7 +642,7 @@
   // method_headers are expected to be in the executable region.
   {
     MutexLock mu(Thread::Current(), *Locks::cha_lock_);
-    Runtime::Current()->GetClassHierarchyAnalysis()
+    Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()
         ->RemoveDependentsWithMethodHeaders(method_headers);
   }
 
@@ -977,7 +977,7 @@
         << "Should not be using cha on debuggable apps/runs!";
 
     for (ArtMethod* single_impl : cha_single_implementation_list) {
-      Runtime::Current()->GetClassHierarchyAnalysis()->AddDependency(
+      Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()->AddDependency(
           single_impl, method, method_header);
     }
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2712419..6fbf64b 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -63,7 +63,6 @@
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/unix_file/fd_file.h"
-#include "cha.h"
 #include "class_linker-inl.h"
 #include "compiler_callbacks.h"
 #include "debugger.h"
@@ -259,8 +258,7 @@
       pruned_dalvik_cache_(false),
       // Initially assume we perceive jank in case the process state is never updated.
       process_state_(kProcessStateJankPerceptible),
-      zygote_no_threads_(false),
-      cha_(nullptr) {
+      zygote_no_threads_(false) {
   static_assert(Runtime::kCalleeSaveSize ==
                     static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
 
@@ -382,7 +380,6 @@
   delete monitor_list_;
   delete monitor_pool_;
   delete class_linker_;
-  delete cha_;
   delete heap_;
   delete intern_table_;
   delete oat_file_manager_;
@@ -1287,7 +1284,6 @@
 
   CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);
   class_linker_ = new ClassLinker(intern_table_);
-  cha_ = new ClassHierarchyAnalysis;
   if (GetHeap()->HasBootImageSpace()) {
     bool result = class_linker_->InitFromBootImage(&error_msg);
     if (!result) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 9424596..7e4b896 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -73,7 +73,6 @@
 class ArenaPool;
 class ArtMethod;
 enum class CalleeSaveType: uint32_t;
-class ClassHierarchyAnalysis;
 class ClassLinker;
 class CompilerCallbacks;
 class DexFile;
@@ -650,10 +649,6 @@
   void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
   void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
 
-  ClassHierarchyAnalysis* GetClassHierarchyAnalysis() {
-    return cha_;
-  }
-
   void AttachAgent(const std::string& agent_arg);
 
   const std::list<ti::Agent>& GetAgents() const {
@@ -944,8 +939,6 @@
   // Generic system-weak holders.
   std::vector<gc::AbstractSystemWeakHolder*> system_weak_holders_;
 
-  ClassHierarchyAnalysis* cha_;
-
   std::unique_ptr<RuntimeCallbacks> callbacks_;
 
   std::atomic<uint32_t> deoptimization_counts_[
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
index d37bd34..b0ce5a8 100644
--- a/tools/dexfuzz/src/dexfuzz/DexFuzz.java
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -33,9 +33,9 @@
  * Entrypoint class for dexfuzz.
  */
 public class DexFuzz {
-  // Last version update 1.5: added register clobber mutator.
+  // Last version update 1.6: added temporary register to ArrayLengthChanger.
   private static int majorVersion = 1;
-  private static int minorVersion = 5;
+  private static int minorVersion = 6;
   private static int seedChangeVersion = 0;
 
   /**
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java
index aba7971..e640b4e 100644
--- a/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java
@@ -28,8 +28,6 @@
 import java.util.List;
 import java.util.Random;
 
-// This mutation might change the length of an array but can also change the
-// value of the register in every place it is used.
 public class NewArrayLengthChanger extends CodeMutator {
   /**
    * Every CodeMutator has an AssociatedMutation, representing the
@@ -116,20 +114,46 @@
     MutatableCode mutatableCode = mutation.mutatableCode;
     MInsn newArrayInsn = newArrayLengthInsns.get(mutation.newArrayToChangeIdx);
     int newArrayInsnIdx = mutatableCode.getInstructionIndex(newArrayInsn);
+    // If the original new-array instruction is no longer present
+    // in the code (as indicated by a negative index), we make a
+    // best effort to find any other new-array instruction to
+    // apply the mutation to. If that effort fails, we simply
+    // bail by doing nothing.
+    if (newArrayInsnIdx < 0) {
+      newArrayInsnIdx = scanNewArray(mutatableCode);
+      if (newArrayInsnIdx == -1) {
+        return;
+      }
+    }
 
     MInsn newInsn = new MInsn();
     newInsn.insn = new Instruction();
     newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_16);
+    mutatableCode.allocateTemporaryVRegs(1);
+    newArrayInsn.insn.vregB = mutatableCode.getTemporaryVReg(0);
     newInsn.insn.vregA = (int) newArrayInsn.insn.vregB;
     // New length chosen randomly between 1 to 100.
     newInsn.insn.vregB = rng.nextInt(100);
     mutatableCode.insertInstructionAt(newInsn, newArrayInsnIdx);
     Log.info("Changed the length of the array to " + newInsn.insn.vregB);
     stats.incrementStat("Changed length of new array");
+    mutatableCode.finishedUsingTemporaryVRegs();
   }
 
   private boolean isNewArray(MInsn mInsn) {
     Opcode opcode = mInsn.insn.info.opcode;
     return opcode == Opcode.NEW_ARRAY;
   }
+
+  // Return the index of first new-array in the method, -1 otherwise.
+  private int scanNewArray(MutatableCode mutatableCode) {
+    int idx = 0;
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isNewArray(mInsn)) {
+        return idx;
+      }
+      idx++;
+    }
+    return -1;
+  }
 }
\ No newline at end of file