Merge "Give directions on attaching gdb to run-test target"
diff --git a/Android.mk b/Android.mk
index 7852be5..0c20973 100644
--- a/Android.mk
+++ b/Android.mk
@@ -427,7 +427,7 @@
 define build-art-hiddenapi
 $(shell if [ ! -d frameworks/base ]; then \
   mkdir -p ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING; \
-	touch ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-{blacklist,dark-greylist,light-greylist}.txt; \
+	touch ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-{whitelist,blacklist,dark-greylist,light-greylist}.txt; \
   fi;)
 endef
 
diff --git a/build/Android.bp b/build/Android.bp
index 62f71ff..3eb4aaf 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -18,16 +18,30 @@
 }
 
 art_clang_tidy_errors = [
-    // Protect scoped things like MutexLock.
-    "bugprone-unused-raii",
+    "bugprone-lambda-function-name",
+    "bugprone-unused-raii",  // Protect scoped things like MutexLock.
+    "bugprone-virtual-near-miss",
+    "modernize-use-bool-literals",
+    "modernize-use-nullptr",
+    "modernize-use-using",
+    "performance-faster-string-find",
     "performance-for-range-copy",
+    "performance-implicit-conversion-in-loop",
     "performance-unnecessary-copy-initialization",
     "performance-unnecessary-value-param",
     "misc-unused-using-decls",
 ]
 // Should be: strings.Join(art_clang_tidy_errors, ",").
-art_clang_tidy_errors_str = "bugprone-unused-raii"
+art_clang_tidy_errors_str = "bugprone-lambda-function-name"
+        + ",bugprone-unused-raii"
+        + ",bugprone-virtual-near-miss"
+        + ",modernize-redundant-void-arg"
+        + ",modernize-use-bool-literals"
+        + ",modernize-use-nullptr"
+        + ",modernize-use-using"
+        + ",performance-faster-string-find"
         + ",performance-for-range-copy"
+        + ",performance-implicit-conversion-in-loop"
         + ",performance-unnecessary-copy-initialization"
         + ",performance-unnecessary-value-param"
         + ",misc-unused-using-decls"
@@ -44,6 +58,11 @@
     // No exceptions.
     "-misc-noexcept-move-constructor",
     "-performance-noexcept-move-constructor",
+    // "Modernization" we don't agree with.
+    "-modernize-use-auto",
+    "-modernize-return-braced-init-list",
+    "-modernize-use-default-member-init",
+    "-modernize-pass-by-value",
 ]
 
 art_global_defaults {
diff --git a/build/art.go b/build/art.go
index 6c08486..f3cd3ca 100644
--- a/build/art.go
+++ b/build/art.go
@@ -66,7 +66,7 @@
 			"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
 	}
 
-	if envTrue(ctx, "ART_USE_GENERATIONAL_CC") {
+	if !envFalse(ctx, "ART_USE_GENERATIONAL_CC") {
 		cflags = append(cflags, "-DART_USE_GENERATIONAL_CC=1")
 	}
 
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 233f61c..fe05992 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -41,12 +41,6 @@
 namespace art {
 namespace debug {
 
-typedef std::vector<DexFile::LocalInfo> LocalInfos;
-
-static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) {
-  static_cast<LocalInfos*>(ctx)->push_back(entry);
-}
-
 static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
   std::vector<const char*> names;
   DCHECK(mi->dex_file != nullptr);
@@ -251,11 +245,12 @@
       }
 
       // Write local variables.
-      LocalInfos local_infos;
+      std::vector<DexFile::LocalInfo> local_infos;
       if (accessor.DecodeDebugLocalInfo(is_static,
                                         mi->dex_method_index,
-                                        LocalInfoCallback,
-                                        &local_infos)) {
+                                        [&](const DexFile::LocalInfo& entry) {
+                                          local_infos.push_back(entry);
+                                        })) {
         for (const DexFile::LocalInfo& var : local_infos) {
           if (var.reg_ < accessor.RegistersSize() - accessor.InsSize()) {
             info_.StartTag(DW_TAG_variable);
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index fe8b766..183173b 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -41,7 +41,7 @@
 class Matcher {
  public:
   // Match function type.
-  typedef bool MatchFn(Matcher* matcher);
+  using MatchFn = bool(Matcher*);
 
   template <size_t size>
   static bool Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]);
diff --git a/compiler/jni/quick/mips/calling_convention_mips.h b/compiler/jni/quick/mips/calling_convention_mips.h
index 165fc60..8b395a0 100644
--- a/compiler/jni/quick/mips/calling_convention_mips.h
+++ b/compiler/jni/quick/mips/calling_convention_mips.h
@@ -87,7 +87,7 @@
  private:
   // Padding to ensure longs and doubles are not split in o32.
   size_t padding_;
-  size_t use_fp_arg_registers_;
+  bool use_fp_arg_registers_;
 
   DISALLOW_COPY_AND_ASSIGN(MipsJniCallingConvention);
 };
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index e84896b..d440cf3 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -197,7 +197,7 @@
     return GetNumberOfJitStringRoots() + GetNumberOfJitClassRoots();
   }
 
-  void EmitJitRoots(Handle<mirror::ObjectArray<mirror::Object>> roots)
+  void EmitJitRoots(/*out*/std::vector<Handle<mirror::Object>>* roots)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
@@ -230,29 +230,31 @@
 };
 
 void CodeGenerator::CodeGenerationData::EmitJitRoots(
-    Handle<mirror::ObjectArray<mirror::Object>> roots) {
-  DCHECK_EQ(static_cast<size_t>(roots->GetLength()), GetNumberOfJitRoots());
+    /*out*/std::vector<Handle<mirror::Object>>* roots) {
+  DCHECK(roots->empty());
+  roots->reserve(GetNumberOfJitRoots());
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   size_t index = 0;
   for (auto& entry : jit_string_roots_) {
     // Update the `roots` with the string, and replace the address temporarily
     // stored to the index in the table.
     uint64_t address = entry.second;
-    roots->Set(index, reinterpret_cast<StackReference<mirror::String>*>(address)->AsMirrorPtr());
-    DCHECK(roots->Get(index) != nullptr);
+    roots->emplace_back(reinterpret_cast<StackReference<mirror::Object>*>(address));
+    DCHECK(roots->back() != nullptr);
+    DCHECK(roots->back()->IsString());
     entry.second = index;
     // Ensure the string is strongly interned. This is a requirement on how the JIT
     // handles strings. b/32995596
-    class_linker->GetInternTable()->InternStrong(
-        reinterpret_cast<mirror::String*>(roots->Get(index)));
+    class_linker->GetInternTable()->InternStrong(roots->back()->AsString());
     ++index;
   }
   for (auto& entry : jit_class_roots_) {
     // Update the `roots` with the class, and replace the address temporarily
     // stored to the index in the table.
     uint64_t address = entry.second;
-    roots->Set(index, reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr());
-    DCHECK(roots->Get(index) != nullptr);
+    roots->emplace_back(reinterpret_cast<StackReference<mirror::Object>*>(address));
+    DCHECK(roots->back() != nullptr);
+    DCHECK(roots->back()->IsClass());
     entry.second = index;
     ++index;
   }
@@ -1645,8 +1647,8 @@
 }
 
 void CodeGenerator::EmitJitRoots(uint8_t* code,
-                                 Handle<mirror::ObjectArray<mirror::Object>> roots,
-                                 const uint8_t* roots_data) {
+                                 const uint8_t* roots_data,
+                                 /*out*/std::vector<Handle<mirror::Object>>* roots) {
   code_generation_data_->EmitJitRoots(roots);
   EmitJitRootPatches(code, roots_data);
 }
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index e77d621..4e73e0b 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -356,8 +356,8 @@
   // Fills the `literals` array with literals collected during code generation.
   // Also emits literal patches.
   void EmitJitRoots(uint8_t* code,
-                    Handle<mirror::ObjectArray<mirror::Object>> roots,
-                    const uint8_t* roots_data)
+                    const uint8_t* roots_data,
+                    /*out*/std::vector<Handle<mirror::Object>>* roots)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsLeafMethod() const {
@@ -622,7 +622,7 @@
   // otherwise return a fall-back info that should be used instead.
   virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) = 0;
+      ArtMethod* method) = 0;
 
   // Generate a call to a static or direct method.
   virtual void GenerateStaticOrDirectCall(
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index d56f7aa..25eadcd 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4053,7 +4053,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+      ArtMethod* method ATTRIBUTE_UNUSED) {
   // On ARM64 we support all dispatch types.
   return desired_dispatch_info;
 }
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 2e7a20b..1ba58b1 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -557,7 +557,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) override;
+      ArtMethod* method) override;
 
   void GenerateStaticOrDirectCall(
       HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) override;
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 3580975..d5149b3 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -8650,7 +8650,7 @@
 // otherwise return a fall-back info that should be used instead.
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
     const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-    HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+    ArtMethod* method ATTRIBUTE_UNUSED) {
   return desired_dispatch_info;
 }
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 33502d4..5edca87 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -547,7 +547,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) override;
+      ArtMethod* method) override;
 
   void GenerateStaticOrDirectCall(
       HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) override;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index d74a7a7..1cf5515 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -7964,7 +7964,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+      ArtMethod* method ATTRIBUTE_UNUSED) {
   return desired_dispatch_info;
 }
 
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index bf95893..5080731 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -563,7 +563,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) override;
+      ArtMethod* method) override;
 
   void GenerateStaticOrDirectCall(
       HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) override;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 7c89808..27534b0 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -6059,7 +6059,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+      ArtMethod* method ATTRIBUTE_UNUSED) {
   // On MIPS64 we support all dispatch types.
   return desired_dispatch_info;
 }
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index ddc154d..52f3a62 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -541,7 +541,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) override;
+      ArtMethod* method) override;
 
   void GenerateStaticOrDirectCall(
       HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) override;
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index 43169ba..e79a96b 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -1277,6 +1277,74 @@
   }
 }
 
+void LocationsBuilderARM64::VisitVecDotProd(HVecDotProd* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+  DCHECK(instruction->GetPackedType() == DataType::Type::kInt32);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetInAt(1, Location::RequiresFpuRegister());
+  locations->SetInAt(2, Location::RequiresFpuRegister());
+  locations->SetOut(Location::SameAsFirstInput());
+
+  // For Int8 and Uint8 we need a temp register.
+  if (DataType::Size(instruction->InputAt(1)->AsVecOperation()->GetPackedType()) == 1) {
+    locations->AddTemp(Location::RequiresFpuRegister());
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitVecDotProd(HVecDotProd* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  VRegister acc = VRegisterFrom(locations->InAt(0));
+  VRegister left = VRegisterFrom(locations->InAt(1));
+  VRegister right = VRegisterFrom(locations->InAt(2));
+  HVecOperation* a = instruction->InputAt(1)->AsVecOperation();
+  HVecOperation* b = instruction->InputAt(2)->AsVecOperation();
+  DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()),
+            HVecOperation::ToSignedType(b->GetPackedType()));
+  DCHECK_EQ(instruction->GetPackedType(), DataType::Type::kInt32);
+  DCHECK_EQ(4u, instruction->GetVectorLength());
+
+  size_t inputs_data_size = DataType::Size(a->GetPackedType());
+  switch (inputs_data_size) {
+    case 1u: {
+      DCHECK_EQ(16u, a->GetVectorLength());
+      VRegister tmp = VRegisterFrom(locations->GetTemp(0));
+      if (instruction->IsZeroExtending()) {
+        // TODO: Use Armv8.4-A UDOT instruction when it is available.
+        __ Umull(tmp.V8H(), left.V8B(), right.V8B());
+        __ Uaddw(acc.V4S(), acc.V4S(), tmp.V4H());
+        __ Uaddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+
+        __ Umull2(tmp.V8H(), left.V16B(), right.V16B());
+        __ Uaddw(acc.V4S(), acc.V4S(), tmp.V4H());
+        __ Uaddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+      } else {
+        // TODO: Use Armv8.4-A SDOT instruction when it is available.
+        __ Smull(tmp.V8H(), left.V8B(), right.V8B());
+        __ Saddw(acc.V4S(), acc.V4S(), tmp.V4H());
+        __ Saddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+
+        __ Smull2(tmp.V8H(), left.V16B(), right.V16B());
+        __ Saddw(acc.V4S(), acc.V4S(), tmp.V4H());
+        __ Saddw2(acc.V4S(), acc.V4S(), tmp.V8H());
+      }
+      break;
+    }
+    case 2u:
+      DCHECK_EQ(8u, a->GetVectorLength());
+      if (instruction->IsZeroExtending()) {
+        __ Umlal(acc.V4S(), left.V4H(), right.V4H());
+        __ Umlal2(acc.V4S(), left.V8H(), right.V8H());
+      } else {
+        __ Smlal(acc.V4S(), left.V4H(), right.V4H());
+        __ Smlal2(acc.V4S(), left.V8H(), right.V8H());
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type size: " << inputs_data_size;
+  }
+}
+
 // Helper to set up locations for vector memory operations.
 static void CreateVecMemLocations(ArenaAllocator* allocator,
                                   HVecMemoryOperation* instruction,
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 7b66b17..62b6c4e 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -854,6 +854,14 @@
   }
 }
 
+void LocationsBuilderARMVIXL::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 // Return whether the vector memory access operation is guaranteed to be word-aligned (ARM word
 // size equals to 4).
 static bool IsWordAligned(HVecMemoryOperation* instruction) {
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index df0e148..24f4fb2 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -1274,6 +1274,14 @@
   }
 }
 
+void LocationsBuilderMIPS::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 // Helper to set up locations for vector memory operations.
 static void CreateVecMemLocations(ArenaAllocator* allocator,
                                   HVecMemoryOperation* instruction,
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index de354b6..972c49e 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -1272,6 +1272,14 @@
   }
 }
 
+void LocationsBuilderMIPS64::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 // Helper to set up locations for vector memory operations.
 static void CreateVecMemLocations(ArenaAllocator* allocator,
                                   HVecMemoryOperation* instruction,
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 2502275..c52ecc7 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -1143,6 +1143,14 @@
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
+void LocationsBuilderX86::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorX86::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 // Helper to set up locations for vector memory operations.
 static void CreateVecMemLocations(ArenaAllocator* allocator,
                                   HVecMemoryOperation* instruction,
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 4a67daf..87d0106 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -1116,6 +1116,14 @@
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
+void LocationsBuilderX86_64::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecDotProd(HVecDotProd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 // Helper to set up locations for vector memory operations.
 static void CreateVecMemLocations(ArenaAllocator* allocator,
                                   HVecMemoryOperation* instruction,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 6a27081..39cbe5e 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4785,7 +4785,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+      ArtMethod* method ATTRIBUTE_UNUSED) {
   return desired_dispatch_info;
 }
 
@@ -8301,7 +8301,7 @@
   uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
   uintptr_t address =
       reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
-  typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+  using unaligned_uint32_t __attribute__((__aligned__(1))) = uint32_t;
   reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] =
      dchecked_integral_cast<uint32_t>(address);
 }
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 6154771..93b0461 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -410,7 +410,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) override;
+      ArtMethod* method) override;
 
   // Generate a call to a static or direct method.
   void GenerateStaticOrDirectCall(
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 489652b..e458dff 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -978,7 +978,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86_64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+      ArtMethod* method ATTRIBUTE_UNUSED) {
   return desired_dispatch_info;
 }
 
@@ -7542,7 +7542,7 @@
   uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
   uintptr_t address =
       reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
-  typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+  using unaligned_uint32_t __attribute__((__aligned__(1))) = uint32_t;
   reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] =
      dchecked_integral_cast<uint32_t>(address);
 }
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index f77a5c8..1e71397 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -409,7 +409,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) override;
+      ArtMethod* method) override;
 
   void GenerateStaticOrDirectCall(
       HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) override;
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h
index 5ac6e46..3cbcc9e 100644
--- a/compiler/optimizing/data_type.h
+++ b/compiler/optimizing/data_type.h
@@ -231,6 +231,21 @@
     }
   }
 
+  static Type ToUnsigned(Type type) {
+    switch (type) {
+      case Type::kInt8:
+        return Type::kUint8;
+      case Type::kInt16:
+        return Type::kUint16;
+      case Type::kInt32:
+        return Type::kUint32;
+      case Type::kInt64:
+        return Type::kUint64;
+      default:
+        return type;
+    }
+  }
+
   static const char* PrettyDescriptor(Type type);
 
  private:
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 31db8c2..a1af2be 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -106,8 +106,7 @@
   }
 }
 
-typedef Disassembler* create_disasm_prototype(InstructionSet instruction_set,
-                                              DisassemblerOptions* options);
+using create_disasm_prototype = Disassembler*(InstructionSet, DisassemblerOptions*);
 class HGraphVisualizerDisassembler {
  public:
   HGraphVisualizerDisassembler(InstructionSet instruction_set,
@@ -564,6 +563,14 @@
     StartAttributeStream("kind") << instruction->GetOpKind();
   }
 
+  void VisitVecDotProd(HVecDotProd* instruction) override {
+    VisitVecOperation(instruction);
+    DataType::Type arg_type = instruction->InputAt(1)->AsVecOperation()->GetPackedType();
+    StartAttributeStream("type") << (instruction->IsZeroExtending() ?
+                                    DataType::ToUnsigned(arg_type) :
+                                    DataType::ToSigned(arg_type));
+  }
+
 #if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
   void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) override {
     StartAttributeStream("kind") << instruction->GetOpKind();
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 3ba7414..1be96fb 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -2022,13 +2022,11 @@
   // optimization that could lead to a HDeoptimize. The following optimizations do not.
   HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner");
   HConstantFolding fold(callee_graph, "constant_folding$inliner");
-  HSharpening sharpening(callee_graph, codegen_);
   InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_);
   IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_);
 
   HOptimization* optimizations[] = {
     &intrinsics,
-    &sharpening,
     &simplify,
     &fold,
     &dce,
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 034761d..72b6748 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -978,11 +978,8 @@
       }
     }
 
-    HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
-        HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall,
-        HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        0u
-    };
+    HInvokeStaticOrDirect::DispatchInfo dispatch_info =
+        HSharpening::SharpenInvokeStaticOrDirect(resolved_method, code_generator_);
     MethodReference target_method(resolved_method->GetDexFile(),
                                   resolved_method->GetDexMethodIndex());
     invoke = new (allocator_) HInvokeStaticOrDirect(allocator_,
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index bb96c21..ad50bb8 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1317,7 +1317,7 @@
   }
 
   HNeg* neg = left_is_neg ? left->AsNeg() : right->AsNeg();
-  if ((left_is_neg ^ right_is_neg) && neg->HasOnlyOneNonEnvironmentUse()) {
+  if (left_is_neg != right_is_neg && neg->HasOnlyOneNonEnvironmentUse()) {
     // Replace code looking like
     //    NEG tmp, b
     //    ADD dst, a, tmp
@@ -2290,7 +2290,7 @@
       // the invoke, as we would need to look it up in the current dex file, and it
       // is unlikely that it exists. The most usual situation for such typed
       // arraycopy methods is a direct pointer to the boot image.
-      HSharpening::SharpenInvokeStaticOrDirect(invoke, codegen_);
+      invoke->SetDispatchInfo(HSharpening::SharpenInvokeStaticOrDirect(method, codegen_));
     }
   }
 }
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index bc59fcf..38e4c89 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -1092,7 +1092,8 @@
     assembler->MaybeUnpoisonHeapReference(tmp);
   }
   __ Subs(tmp, tmp, expected);
-  __ B(ne, failure, (failure == loop_exit) ? kNear : kBranchWithoutHint);
+  static_cast<vixl32::MacroAssembler*>(assembler->GetVIXLAssembler())->
+      B(ne, failure, /* hint= */ (failure == loop_exit) ? kNear : kBranchWithoutHint);
   if (type == DataType::Type::kReference) {
     assembler->MaybePoisonHeapReference(value);
   }
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 7d66155..12b180d 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -351,7 +351,10 @@
 
 // Translates vector operation to reduction kind.
 static HVecReduce::ReductionKind GetReductionKind(HVecOperation* reduction) {
-  if (reduction->IsVecAdd() || reduction->IsVecSub() || reduction->IsVecSADAccumulate()) {
+  if (reduction->IsVecAdd() ||
+      reduction->IsVecSub() ||
+      reduction->IsVecSADAccumulate() ||
+      reduction->IsVecDotProd()) {
     return HVecReduce::kSum;
   }
   LOG(FATAL) << "Unsupported SIMD reduction " << reduction->GetId();
@@ -431,6 +434,23 @@
   }
 }
 
+// Returns the narrower type out of instructions a and b types.
+static DataType::Type GetNarrowerType(HInstruction* a, HInstruction* b) {
+  DataType::Type type = a->GetType();
+  if (DataType::Size(b->GetType()) < DataType::Size(type)) {
+    type = b->GetType();
+  }
+  if (a->IsTypeConversion() &&
+      DataType::Size(a->InputAt(0)->GetType()) < DataType::Size(type)) {
+    type = a->InputAt(0)->GetType();
+  }
+  if (b->IsTypeConversion() &&
+      DataType::Size(b->InputAt(0)->GetType()) < DataType::Size(type)) {
+    type = b->InputAt(0)->GetType();
+  }
+  return type;
+}
+
 //
 // Public methods.
 //
@@ -1289,6 +1309,7 @@
     DataType::Type type = instruction->GetType();
     // Recognize SAD idiom or direct reduction.
     if (VectorizeSADIdiom(node, instruction, generate_code, type, restrictions) ||
+        VectorizeDotProdIdiom(node, instruction, generate_code, type, restrictions) ||
         (TrySetVectorType(type, &restrictions) &&
          VectorizeUse(node, instruction, generate_code, type, restrictions))) {
       if (generate_code) {
@@ -1531,11 +1552,11 @@
         case DataType::Type::kBool:
         case DataType::Type::kUint8:
         case DataType::Type::kInt8:
-          *restrictions |= kNoDiv | kNoReduction;
+          *restrictions |= kNoDiv | kNoReduction | kNoDotProd;
           return TrySetVectorLength(8);
         case DataType::Type::kUint16:
         case DataType::Type::kInt16:
-          *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction;
+          *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoDotProd;
           return TrySetVectorLength(4);
         case DataType::Type::kInt32:
           *restrictions |= kNoDiv | kNoWideSAD;
@@ -1580,12 +1601,23 @@
           case DataType::Type::kBool:
           case DataType::Type::kUint8:
           case DataType::Type::kInt8:
-            *restrictions |=
-                kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoSAD;
+            *restrictions |= kNoMul |
+                             kNoDiv |
+                             kNoShift |
+                             kNoAbs |
+                             kNoSignedHAdd |
+                             kNoUnroundedHAdd |
+                             kNoSAD |
+                             kNoDotProd;
             return TrySetVectorLength(16);
           case DataType::Type::kUint16:
           case DataType::Type::kInt16:
-            *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoSAD;
+            *restrictions |= kNoDiv |
+                             kNoAbs |
+                             kNoSignedHAdd |
+                             kNoUnroundedHAdd |
+                             kNoSAD|
+                             kNoDotProd;
             return TrySetVectorLength(8);
           case DataType::Type::kInt32:
             *restrictions |= kNoDiv | kNoSAD;
@@ -1610,11 +1642,11 @@
           case DataType::Type::kBool:
           case DataType::Type::kUint8:
           case DataType::Type::kInt8:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoDotProd;
             return TrySetVectorLength(16);
           case DataType::Type::kUint16:
           case DataType::Type::kInt16:
-            *restrictions |= kNoDiv | kNoStringCharAt;
+            *restrictions |= kNoDiv | kNoStringCharAt | kNoDotProd;
             return TrySetVectorLength(8);
           case DataType::Type::kInt32:
             *restrictions |= kNoDiv;
@@ -1639,11 +1671,11 @@
           case DataType::Type::kBool:
           case DataType::Type::kUint8:
           case DataType::Type::kInt8:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoDotProd;
             return TrySetVectorLength(16);
           case DataType::Type::kUint16:
           case DataType::Type::kInt16:
-            *restrictions |= kNoDiv | kNoStringCharAt;
+            *restrictions |= kNoDiv | kNoStringCharAt | kNoDotProd;
             return TrySetVectorLength(8);
           case DataType::Type::kInt32:
             *restrictions |= kNoDiv;
@@ -2071,18 +2103,7 @@
   HInstruction* r = a;
   HInstruction* s = b;
   bool is_unsigned = false;
-  DataType::Type sub_type = a->GetType();
-  if (DataType::Size(b->GetType()) < DataType::Size(sub_type)) {
-    sub_type = b->GetType();
-  }
-  if (a->IsTypeConversion() &&
-      DataType::Size(a->InputAt(0)->GetType()) < DataType::Size(sub_type)) {
-    sub_type = a->InputAt(0)->GetType();
-  }
-  if (b->IsTypeConversion() &&
-      DataType::Size(b->InputAt(0)->GetType()) < DataType::Size(sub_type)) {
-    sub_type = b->InputAt(0)->GetType();
-  }
+  DataType::Type sub_type = GetNarrowerType(a, b);
   if (reduction_type != sub_type &&
       (!IsNarrowerOperands(a, b, sub_type, &r, &s, &is_unsigned) || is_unsigned)) {
     return false;
@@ -2123,6 +2144,75 @@
   return false;
 }
 
+// Method recognises the following dot product idiom:
+//   q += a * b for operands a, b whose type is narrower than the reduction one.
+// Provided that the operands have the same type or are promoted to a wider form.
+// Since this may involve a vector length change, the idiom is handled by going directly
+// to a dot product node (rather than relying combining finer grained nodes later).
+bool HLoopOptimization::VectorizeDotProdIdiom(LoopNode* node,
+                                              HInstruction* instruction,
+                                              bool generate_code,
+                                              DataType::Type reduction_type,
+                                              uint64_t restrictions) {
+  if (!instruction->IsAdd() || (reduction_type != DataType::Type::kInt32)) {
+    return false;
+  }
+
+  HInstruction* q = instruction->InputAt(0);
+  HInstruction* v = instruction->InputAt(1);
+  if (!v->IsMul() || v->GetType() != reduction_type) {
+    return false;
+  }
+
+  HInstruction* a = v->InputAt(0);
+  HInstruction* b = v->InputAt(1);
+  HInstruction* r = a;
+  HInstruction* s = b;
+  DataType::Type op_type = GetNarrowerType(a, b);
+  bool is_unsigned = false;
+
+  if (!IsNarrowerOperands(a, b, op_type, &r, &s, &is_unsigned)) {
+    return false;
+  }
+  op_type = HVecOperation::ToProperType(op_type, is_unsigned);
+
+  if (!TrySetVectorType(op_type, &restrictions) ||
+      HasVectorRestrictions(restrictions, kNoDotProd)) {
+    return false;
+  }
+
+  DCHECK(r != nullptr && s != nullptr);
+  // Accept dot product idiom for vectorizable operands. Vectorized code uses the shorthand
+  // idiomatic operation. Sequential code uses the original scalar expressions.
+  if (generate_code && vector_mode_ != kVector) {  // de-idiom
+    r = a;
+    s = b;
+  }
+  if (VectorizeUse(node, q, generate_code, op_type, restrictions) &&
+      VectorizeUse(node, r, generate_code, op_type, restrictions) &&
+      VectorizeUse(node, s, generate_code, op_type, restrictions)) {
+    if (generate_code) {
+      if (vector_mode_ == kVector) {
+        vector_map_->Put(instruction, new (global_allocator_) HVecDotProd(
+            global_allocator_,
+            vector_map_->Get(q),
+            vector_map_->Get(r),
+            vector_map_->Get(s),
+            reduction_type,
+            is_unsigned,
+            GetOtherVL(reduction_type, op_type, vector_length_),
+            kNoDexPc));
+        MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom);
+      } else {
+        GenerateVecOp(v, vector_map_->Get(r), vector_map_->Get(s), reduction_type);
+        GenerateVecOp(instruction, vector_map_->Get(q), vector_map_->Get(v), reduction_type);
+      }
+    }
+    return true;
+  }
+  return false;
+}
+
 //
 // Vectorization heuristics.
 //
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 2b202fd..1a842c4 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -82,6 +82,7 @@
     kNoReduction     = 1 << 9,   // no reduction
     kNoSAD           = 1 << 10,  // no sum of absolute differences (SAD)
     kNoWideSAD       = 1 << 11,  // no sum of absolute differences (SAD) with operand widening
+    kNoDotProd       = 1 << 12,  // no dot product
   };
 
   /*
@@ -217,6 +218,11 @@
                          bool generate_code,
                          DataType::Type type,
                          uint64_t restrictions);
+  bool VectorizeDotProdIdiom(LoopNode* node,
+                             HInstruction* instruction,
+                             bool generate_code,
+                             DataType::Type type,
+                             uint64_t restrictions);
 
   // Vectorization heuristics.
   Alignment ComputeAlignment(HInstruction* offset,
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 68f1a24..76887f9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1453,6 +1453,7 @@
   M(VecSetScalars, VecOperation)                                        \
   M(VecMultiplyAccumulate, VecOperation)                                \
   M(VecSADAccumulate, VecOperation)                                     \
+  M(VecDotProd, VecOperation)                                           \
   M(VecLoad, VecMemoryOperation)                                        \
   M(VecStore, VecMemoryOperation)                                       \
 
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index c7539f2..597e399 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -1021,6 +1021,66 @@
   DEFAULT_COPY_CONSTRUCTOR(VecSADAccumulate);
 };
 
+// Performs dot product of two vectors and adds the result to wider precision components in
+// the accumulator.
+//
+// viz. DOT_PRODUCT([ a1, .. , am], [ x1, .. , xn ], [ y1, .. , yn ]) =
+//                  [ a1 + sum(xi * yi), .. , am + sum(xj * yj) ],
+//      for m <= n, non-overlapping sums,
+//      for either both signed or both unsigned operands x, y.
+//
+// Notes:
+//   - packed type reflects the type of sum reduction, not the type of the operands.
+//   - IsZeroExtending() is used to determine the kind of signed/zero extension to be
+//     performed for the operands.
+//
+// TODO: Support types other than kInt32 for packed type.
+class HVecDotProd final : public HVecOperation {
+ public:
+  HVecDotProd(ArenaAllocator* allocator,
+              HInstruction* accumulator,
+              HInstruction* left,
+              HInstruction* right,
+              DataType::Type packed_type,
+              bool is_zero_extending,
+              size_t vector_length,
+              uint32_t dex_pc)
+    : HVecOperation(kVecDotProd,
+                    allocator,
+                    packed_type,
+                    SideEffects::None(),
+                    /* number_of_inputs */ 3,
+                    vector_length,
+                    dex_pc) {
+    DCHECK(HasConsistentPackedTypes(accumulator, packed_type));
+    DCHECK(DataType::IsIntegralType(packed_type));
+    DCHECK(left->IsVecOperation());
+    DCHECK(right->IsVecOperation());
+    DCHECK_EQ(ToSignedType(left->AsVecOperation()->GetPackedType()),
+              ToSignedType(right->AsVecOperation()->GetPackedType()));
+    SetRawInputAt(0, accumulator);
+    SetRawInputAt(1, left);
+    SetRawInputAt(2, right);
+    SetPackedFlag<kFieldHDotProdIsZeroExtending>(is_zero_extending);
+  }
+
+  bool IsZeroExtending() const { return GetPackedFlag<kFieldHDotProdIsZeroExtending>(); }
+
+  bool CanBeMoved() const override { return true; }
+
+  DECLARE_INSTRUCTION(VecDotProd);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecDotProd);
+
+ private:
+  // Additional packed bits.
+  static constexpr size_t kFieldHDotProdIsZeroExtending =
+      HVecOperation::kNumberOfVectorOpPackedBits;
+  static constexpr size_t kNumberOfHDotProdPackedBits = kFieldHDotProdIsZeroExtending + 1;
+  static_assert(kNumberOfHDotProdPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+};
+
 // Loads a vector from memory, viz. load(mem, 1)
 // yield the vector [ mem(1), .. , mem(n) ].
 class HVecLoad final : public HVecMemoryOperation {
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 142ddb5..75466a3 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -84,8 +84,6 @@
       return HDeadCodeElimination::kDeadCodeEliminationPassName;
     case OptimizationPass::kInliner:
       return HInliner::kInlinerPassName;
-    case OptimizationPass::kSharpening:
-      return HSharpening::kSharpeningPassName;
     case OptimizationPass::kSelectGenerator:
       return HSelectGenerator::kSelectGeneratorPassName;
     case OptimizationPass::kInstructionSimplifier:
@@ -148,7 +146,6 @@
   X(OptimizationPass::kLoopOptimization);
   X(OptimizationPass::kScheduling);
   X(OptimizationPass::kSelectGenerator);
-  X(OptimizationPass::kSharpening);
   X(OptimizationPass::kSideEffectsAnalysis);
 #ifdef ART_ENABLE_CODEGEN_arm
   X(OptimizationPass::kInstructionSimplifierArm);
@@ -264,9 +261,6 @@
                                        pass_name);
         break;
       }
-      case OptimizationPass::kSharpening:
-        opt = new (allocator) HSharpening(graph, codegen, pass_name);
-        break;
       case OptimizationPass::kSelectGenerator:
         opt = new (allocator) HSelectGenerator(graph, handles, stats, pass_name);
         break;
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index 88b283c..c258d51 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -84,7 +84,6 @@
   kLoopOptimization,
   kScheduling,
   kSelectGenerator,
-  kSharpening,
   kSideEffectsAnalysis,
 #ifdef ART_ENABLE_CODEGEN_arm
   kInstructionSimplifierArm,
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index ff1207a..3a550ef 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -399,7 +399,8 @@
                             PassObserver* pass_observer,
                             VariableSizedHandleScope* handles) const;
 
-  void GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo method_debug_info)
+  void GenerateJitDebugInfo(ArtMethod* method,
+                            const debug::MethodDebugInfo& method_debug_info)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   std::unique_ptr<OptimizingCompilerStats> compilation_stats_;
@@ -624,7 +625,6 @@
   OptimizationDef optimizations[] = {
     // Initial optimizations.
     OptDef(OptimizationPass::kIntrinsicsRecognizer),
-    OptDef(OptimizationPass::kSharpening),
     OptDef(OptimizationPass::kConstantFolding),
     OptDef(OptimizationPass::kInstructionSimplifier),
     OptDef(OptimizationPass::kDeadCodeElimination,
@@ -1219,7 +1219,7 @@
     const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
     JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod(
         compiler_options, access_flags, method_idx, *dex_file);
-    ScopedNullHandle<mirror::ObjectArray<mirror::Object>> roots;
+    std::vector<Handle<mirror::Object>> roots;
     ArenaSet<ArtMethod*, std::less<ArtMethod*>> cha_single_implementation_list(
         allocator.Adapter(kArenaAllocCHA));
     ArenaStack arena_stack(runtime->GetJitArenaPool());
@@ -1321,19 +1321,6 @@
 
   ScopedArenaVector<uint8_t> stack_map = codegen->BuildStackMaps(code_item);
   size_t number_of_roots = codegen->GetNumberOfJitRoots();
-  // We allocate an object array to ensure the JIT roots that we will collect in EmitJitRoots
-  // will be visible by the GC between EmitLiterals and CommitCode. Once CommitCode is
-  // executed, this array is not needed.
-  Handle<mirror::ObjectArray<mirror::Object>> roots(
-      hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(
-          self, GetClassRoot<mirror::ObjectArray<mirror::Object>>(), number_of_roots)));
-  if (roots == nullptr) {
-    // Out of memory, just clear the exception to avoid any Java exception uncaught problems.
-    MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit);
-    DCHECK(self->IsExceptionPending());
-    self->ClearException();
-    return false;
-  }
   uint8_t* stack_map_data = nullptr;
   uint8_t* roots_data = nullptr;
   uint32_t data_size = code_cache->ReserveData(self,
@@ -1347,7 +1334,14 @@
     return false;
   }
   memcpy(stack_map_data, stack_map.data(), stack_map.size());
-  codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data);
+  std::vector<Handle<mirror::Object>> roots;
+  codegen->EmitJitRoots(code_allocator.GetData(), roots_data, &roots);
+  // The root Handle<>s filled by the codegen reference entries in the VariableSizedHandleScope.
+  DCHECK(std::all_of(roots.begin(),
+                     roots.end(),
+                     [&handles](Handle<mirror::Object> root){
+                       return handles.Contains(root.GetReference());
+                     }));
 
   const void* code = code_cache->CommitCode(
       self,
@@ -1413,7 +1407,8 @@
   return true;
 }
 
-void OptimizingCompiler::GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo info) {
+void OptimizingCompiler::GenerateJitDebugInfo(
+    ArtMethod* method, const debug::MethodDebugInfo& info) {
   const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
   DCHECK(compiler_options.GenerateAnyDebugInfo());
 
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 399a6d8..a8ab6cd 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -174,8 +174,8 @@
 template<> const bool ParallelMoveTest<TestParallelMoveResolverWithSwap>::has_swap = true;
 template<> const bool ParallelMoveTest<TestParallelMoveResolverNoSwap>::has_swap = false;
 
-typedef ::testing::Types<TestParallelMoveResolverWithSwap, TestParallelMoveResolverNoSwap>
-    ParallelMoveResolverTestTypes;
+using ParallelMoveResolverTestTypes =
+    ::testing::Types<TestParallelMoveResolverWithSwap, TestParallelMoveResolverNoSwap>;
 
 TYPED_TEST_CASE(ParallelMoveTest, ParallelMoveResolverTestTypes);
 
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index db6a760..be5304c 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -872,9 +872,9 @@
   // Create an interval with lifetime holes.
   static constexpr size_t ranges1[][2] = {{0, 2}, {4, 6}, {8, 10}};
   LiveInterval* first = BuildInterval(ranges1, arraysize(ranges1), GetScopedAllocator(), -1, one);
-  first->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, false, 8));
-  first->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, false, 7));
-  first->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, false, 6));
+  first->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, 0u, 8));
+  first->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, 0u, 7));
+  first->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, 0u, 6));
 
   locations = new (GetAllocator()) LocationSummary(first->GetDefinedBy(), LocationSummary::kNoCall);
   locations->SetOut(Location::RequiresRegister());
@@ -895,9 +895,9 @@
   // before lifetime position 6 yet.
   static constexpr size_t ranges3[][2] = {{2, 4}, {8, 10}};
   LiveInterval* third = BuildInterval(ranges3, arraysize(ranges3), GetScopedAllocator(), -1, three);
-  third->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, false, 8));
-  third->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, false, 4));
-  third->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, false, 3));
+  third->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, 0u, 8));
+  third->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, 0u, 4));
+  third->uses_.push_front(*new (GetScopedAllocator()) UsePosition(user, 0u, 3));
   locations = new (GetAllocator()) LocationSummary(third->GetDefinedBy(), LocationSummary::kNoCall);
   locations->SetOut(Location::RequiresRegister());
   third = third->SplitAt(3);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 5c2f57e..c864951 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -35,22 +35,6 @@
 
 namespace art {
 
-bool HSharpening::Run() {
-  // We don't care about the order of the blocks here.
-  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
-    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-      HInstruction* instruction = it.Current();
-      if (instruction->IsInvokeStaticOrDirect()) {
-        SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_);
-      }
-      // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder
-      //       here. Rewrite it to avoid the CompilerDriver's reliance on verifier data
-      //       because we know the type better when inlining.
-    }
-  }
-  return true;
-}
-
 static bool IsInBootImage(ArtMethod* method) {
   const std::vector<gc::space::ImageSpace*>& image_spaces =
       Runtime::Current()->GetHeap()->GetBootImageSpaces();
@@ -72,17 +56,14 @@
   return compiler_options.IsImageClass(dex_file.StringByTypeIdx(klass->GetDexTypeIndex()));
 }
 
-void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
-                                              CodeGenerator* codegen) {
-  if (invoke->IsStringInit()) {
-    // Not using the dex cache arrays. But we could still try to use a better dispatch...
-    // TODO: Use direct_method and direct_code for the appropriate StringFactory method.
-    return;
+HInvokeStaticOrDirect::DispatchInfo HSharpening::SharpenInvokeStaticOrDirect(
+    ArtMethod* callee, CodeGenerator* codegen) {
+  if (kIsDebugBuild) {
+    ScopedObjectAccess soa(Thread::Current());  // Required for GetDeclaringClass below.
+    DCHECK(callee != nullptr);
+    DCHECK(!(callee->IsConstructor() && callee->GetDeclaringClass()->IsStringClass()));
   }
 
-  ArtMethod* callee = invoke->GetResolvedMethod();
-  DCHECK(callee != nullptr);
-
   HInvokeStaticOrDirect::MethodLoadKind method_load_kind;
   HInvokeStaticOrDirect::CodePtrLocation code_ptr_location;
   uint64_t method_load_data = 0u;
@@ -141,9 +122,7 @@
   HInvokeStaticOrDirect::DispatchInfo desired_dispatch_info = {
       method_load_kind, code_ptr_location, method_load_data
   };
-  HInvokeStaticOrDirect::DispatchInfo dispatch_info =
-      codegen->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke);
-  invoke->SetDispatchInfo(dispatch_info);
+  return codegen->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, callee);
 }
 
 HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index dc55eea..b818672 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -25,24 +25,13 @@
 class CodeGenerator;
 class DexCompilationUnit;
 
-// Optimization that tries to improve the way we dispatch methods and access types,
-// fields, etc. Besides actual method sharpening based on receiver type (for example
-// virtual->direct), this includes selecting the best available dispatch for
-// invoke-static/-direct based on code generator support.
-class HSharpening : public HOptimization {
+// Utility methods that try to improve the way we dispatch methods, and access
+// types and strings.
+class HSharpening {
  public:
-  HSharpening(HGraph* graph,
-              CodeGenerator* codegen,
-              const char* name = kSharpeningPassName)
-      : HOptimization(graph, name),
-        codegen_(codegen) { }
-
-  bool Run() override;
-
-  static constexpr const char* kSharpeningPassName = "sharpening";
-
-  // Used by Sharpening and InstructionSimplifier.
-  static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, CodeGenerator* codegen);
+  // Used by the builder and InstructionSimplifier.
+  static HInvokeStaticOrDirect::DispatchInfo SharpenInvokeStaticOrDirect(
+      ArtMethod* callee, CodeGenerator* codegen);
 
   // Used by the builder and the inliner.
   static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class,
@@ -61,9 +50,6 @@
                                 CodeGenerator* codegen,
                                 const DexCompilationUnit& dex_compilation_unit,
                                 VariableSizedHandleScope* handles);
-
- private:
-  CodeGenerator* codegen_;
 };
 
 }  // namespace art
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index dda29a1..db96e41 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -440,7 +440,10 @@
   return false;
 }
 
-void SsaBuilder::ReplaceUninitializedStringPhis() {
+// Returns whether the analysis succeeded. If it did not, we are going to bail
+// to interpreter.
+// TODO(ngeoffray): Remove this workaround.
+bool SsaBuilder::ReplaceUninitializedStringPhis() {
   ScopedArenaHashSet<HInstruction*> seen_instructions(
       local_allocator_->Adapter(kArenaAllocGraphBuilder));
   ScopedArenaVector<HInstruction*> worklist(local_allocator_->Adapter(kArenaAllocGraphBuilder));
@@ -467,17 +470,23 @@
         if (found_instance == nullptr) {
           found_instance = current->AsNewInstance();
         } else {
-          DCHECK(found_instance == current);
+          if (found_instance != current) {
+            return false;
+          }
         }
       } else if (current->IsPhi()) {
         // Push all inputs to the worklist. Those should be Phis or NewInstance.
         for (HInstruction* input : current->GetInputs()) {
-          DCHECK(input->IsPhi() || input->IsNewInstance()) << input->DebugName();
+          if (!input->IsPhi() && !input->IsNewInstance()) {
+            return false;
+          }
           worklist.push_back(input);
         }
       } else {
         // The verifier prevents any other DEX uses of the uninitialized string.
-        DCHECK(current->IsEqual() || current->IsNotEqual());
+        if (!current->IsEqual() && !current->IsNotEqual()) {
+          return false;
+        }
         continue;
       }
       current->ReplaceUsesDominatedBy(invoke, invoke);
@@ -487,13 +496,18 @@
       // be Phi, or Equal/NotEqual.
       for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
         HInstruction* user = use.GetUser();
-        DCHECK(user->IsPhi() || user->IsEqual() || user->IsNotEqual()) << user->DebugName();
+        if (!user->IsPhi() && !user->IsEqual() && !user->IsNotEqual()) {
+          return false;
+        }
         worklist.push_back(user);
       }
     } while (!worklist.empty());
     seen_instructions.clear();
-    DCHECK(found_instance != nullptr);
+    if (found_instance == nullptr) {
+      return false;
+    }
   }
+  return true;
 }
 
 void SsaBuilder::RemoveRedundantUninitializedStrings() {
@@ -547,7 +561,9 @@
   // Replace Phis that feed in a String.<init>, as well as their aliases, with
   // the actual String allocation invocation. We do this first, as the phis stored in
   // the data structure might get removed from the graph in later stages during `BuildSsa`.
-  ReplaceUninitializedStringPhis();
+  if (!ReplaceUninitializedStringPhis()) {
+    return kAnalysisSkipped;
+  }
 
   // Propagate types of phis. At this point, phis are typed void in the general
   // case, or float/double/reference if we created an equivalent phi. So we need
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 7655445..bae15ac 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -123,7 +123,7 @@
   HArrayGet* GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget);
 
   void RemoveRedundantUninitializedStrings();
-  void ReplaceUninitializedStringPhis();
+  bool ReplaceUninitializedStringPhis();
 
   HGraph* const graph_;
   Handle<mirror::ClassLoader> class_loader_;
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index c0b6f98..a673e32 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -179,7 +179,7 @@
     return;
   }
 
-  typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
+  using DelayedAdvancePC = DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC;
   const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
   const std::vector<uint8_t>& old_stream = data.first;
   const std::vector<DelayedAdvancePC>& advances = data.second;
@@ -3610,7 +3610,7 @@
     label->LinkTo(branch_id);
   }
   // Reserve space for the branch.
-  while (length--) {
+  for (; length != 0u; --length) {
     Nop();
   }
 }
diff --git a/compiler/utils/mips/assembler_mips32r5_test.cc b/compiler/utils/mips/assembler_mips32r5_test.cc
index bd73c12..98fc44b 100644
--- a/compiler/utils/mips/assembler_mips32r5_test.cc
+++ b/compiler/utils/mips/assembler_mips32r5_test.cc
@@ -38,12 +38,12 @@
                                                    uint32_t,
                                                    mips::VectorRegister> {
  public:
-  typedef AssemblerTest<mips::MipsAssembler,
-                        mips::MipsLabel,
-                        mips::Register,
-                        mips::FRegister,
-                        uint32_t,
-                        mips::VectorRegister> Base;
+  using Base = AssemblerTest<mips::MipsAssembler,
+                             mips::MipsLabel,
+                             mips::Register,
+                             mips::FRegister,
+                             uint32_t,
+                             mips::VectorRegister>;
 
   // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
   // and reimplement it without the verification against `assembly_string`. b/73903608
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index 9637c25..723c489 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -38,12 +38,12 @@
                                                    uint32_t,
                                                    mips::VectorRegister> {
  public:
-  typedef AssemblerTest<mips::MipsAssembler,
-                        mips::MipsLabel,
-                        mips::Register,
-                        mips::FRegister,
-                        uint32_t,
-                        mips::VectorRegister> Base;
+  using Base = AssemblerTest<mips::MipsAssembler,
+                             mips::MipsLabel,
+                             mips::Register,
+                             mips::FRegister,
+                             uint32_t,
+                             mips::VectorRegister>;
 
   // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
   // and reimplement it without the verification against `assembly_string`. b/73903608
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index f137c60..4f8ccee 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -37,11 +37,11 @@
                                                mips::FRegister,
                                                uint32_t> {
  public:
-  typedef AssemblerTest<mips::MipsAssembler,
-                        mips::MipsLabel,
-                        mips::Register,
-                        mips::FRegister,
-                        uint32_t> Base;
+  using Base = AssemblerTest<mips::MipsAssembler,
+                             mips::MipsLabel,
+                             mips::Register,
+                             mips::FRegister,
+                             uint32_t>;
 
   // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
   // and reimplement it without the verification against `assembly_string`. b/73903608
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 5b1c5d9..29d2bed 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -52,7 +52,7 @@
     return;
   }
 
-  typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
+  using DelayedAdvancePC = DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC;
   const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
   const std::vector<uint8_t>& old_stream = data.first;
   const std::vector<DelayedAdvancePC>& advances = data.second;
@@ -2889,7 +2889,7 @@
     label->LinkTo(branch_id);
   }
   // Reserve space for the branch.
-  while (length--) {
+  for (; length != 0u; --length) {
     Nop();
   }
 }
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index 3218ae3..66711c3 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -41,12 +41,12 @@
                                                  uint32_t,
                                                  mips64::VectorRegister> {
  public:
-  typedef AssemblerTest<mips64::Mips64Assembler,
-                        mips64::Mips64Label,
-                        mips64::GpuRegister,
-                        mips64::FpuRegister,
-                        uint32_t,
-                        mips64::VectorRegister> Base;
+  using Base = AssemblerTest<mips64::Mips64Assembler,
+                             mips64::Mips64Label,
+                             mips64::GpuRegister,
+                             mips64::FpuRegister,
+                             uint32_t,
+                             mips64::VectorRegister>;
 
   // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<>
   // and reimplement it without the verification against `assembly_string`. b/73903608
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index b03c40a..ad75174 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -44,11 +44,11 @@
                                               x86::XmmRegister,
                                               x86::Immediate> {
  public:
-  typedef AssemblerTest<x86::X86Assembler,
-                        x86::Address,
-                        x86::Register,
-                        x86::XmmRegister,
-                        x86::Immediate> Base;
+  using Base = AssemblerTest<x86::X86Assembler,
+                             x86::Address,
+                             x86::Register,
+                             x86::XmmRegister,
+                             x86::Immediate>;
 
  protected:
   std::string GetArchitectureString() override {
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 65711e0..fe42f9b 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -137,11 +137,11 @@
                                                  x86_64::XmmRegister,
                                                  x86_64::Immediate> {
  public:
-  typedef AssemblerTest<x86_64::X86_64Assembler,
-                        x86_64::Address,
-                        x86_64::CpuRegister,
-                        x86_64::XmmRegister,
-                        x86_64::Immediate> Base;
+  using Base = AssemblerTest<x86_64::X86_64Assembler,
+                             x86_64::Address,
+                             x86_64::CpuRegister,
+                             x86_64::XmmRegister,
+                             x86_64::Immediate>;
 
  protected:
   // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 523c90b..306b73f 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -424,7 +424,7 @@
     return verifier_deps_->dex_deps_.size();
   }
 
-  size_t HasEachKindOfRecord() {
+  bool HasEachKindOfRecord() {
     bool has_strings = false;
     bool has_assignability = false;
     bool has_classes = false;
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
index ef85fd1..1d578ab 100644
--- a/dex2oat/linker/elf_writer_test.cc
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -164,7 +164,7 @@
     // Patch manually.
     std::vector<uint8_t> expected = initial_data;
     for (uintptr_t location : patch_locations) {
-      typedef __attribute__((__aligned__(1))) uint32_t UnalignedAddress;
+      using UnalignedAddress __attribute__((__aligned__(1))) = uint32_t;
       *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta;
     }
 
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index e2db11f..acd49d5 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -92,10 +92,10 @@
 
 static constexpr bool kOatWriterDebugOatCodeLayout = false;
 
-typedef DexFile::Header __attribute__((aligned(1))) UnalignedDexFileHeader;
+using UnalignedDexFileHeader __attribute__((__aligned__(1))) = DexFile::Header;
 
 const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data) {
-    return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data);
+  return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data);
 }
 
 class ChecksumUpdatingOutputStream : public OutputStream {
@@ -2405,7 +2405,7 @@
   if (static_cast<uint32_t>(new_offset) != expected_file_offset) {
     PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
                 << " Expected: " << expected_file_offset << " File: " << out->GetLocation();
-    return 0;
+    return false;
   }
   DCHECK_OFFSET();
 
diff --git a/dex2oat/linker/x86/relative_patcher_x86_base.cc b/dex2oat/linker/x86/relative_patcher_x86_base.cc
index 6a9690d..07cd724 100644
--- a/dex2oat/linker/x86/relative_patcher_x86_base.cc
+++ b/dex2oat/linker/x86/relative_patcher_x86_base.cc
@@ -50,7 +50,7 @@
   uint32_t displacement = target_offset - patch_offset;
   displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
 
-  typedef __attribute__((__aligned__(1))) int32_t unaligned_int32_t;
+  using unaligned_int32_t __attribute__((__aligned__(1))) = int32_t;
   reinterpret_cast<unaligned_int32_t*>(&(*code)[literal_offset])[0] = displacement;
 }
 
diff --git a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
index 9633564..c80f6a9 100644
--- a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
+++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
@@ -31,7 +31,7 @@
   uint32_t displacement = target_offset - patch_offset;
   displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
 
-  typedef __attribute__((__aligned__(1))) int32_t unaligned_int32_t;
+  using unaligned_int32_t __attribute__((__aligned__(1))) = int32_t;
   reinterpret_cast<unaligned_int32_t*>(&(*code)[patch.LiteralOffset()])[0] = displacement;
 }
 
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index 3e576c8..d15bbda 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -17,12 +17,12 @@
 
 cc_defaults {
     name: "dexdump_defaults",
+    defaults: ["art_defaults"],
     srcs: [
         "dexdump_cfg.cc",
         "dexdump_main.cc",
         "dexdump.cc",
     ],
-    cflags: ["-Wall", "-Werror"],
 }
 
 art_cc_binary {
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 31bc6e3..6b2a1b9 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -69,14 +69,14 @@
 /*
  * Data types that match the definitions in the VM specification.
  */
-typedef uint8_t  u1;
-typedef uint16_t u2;
-typedef uint32_t u4;
-typedef uint64_t u8;
-typedef int8_t   s1;
-typedef int16_t  s2;
-typedef int32_t  s4;
-typedef int64_t  s8;
+using u1 = uint8_t;
+using u2 = uint16_t;
+using u4 = uint32_t;
+using u8 = uint64_t;
+using s1 = int8_t;
+using s2 = int16_t;
+using s4 = int32_t;
+using s8 = int64_t;
 
 /*
  * Basic information about a field or a method.
@@ -331,7 +331,7 @@
  * NULL-terminated.
  */
 static void asciify(char* out, const unsigned char* data, size_t len) {
-  while (len--) {
+  for (; len != 0u; --len) {
     if (*data < 0x20) {
       // Could do more here, but we don't need them yet.
       switch (*data) {
@@ -751,16 +751,6 @@
 }
 
 /*
- * Callback for dumping locals table entry.
- */
-static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) {
-  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
-  fprintf(gOutFile, "        0x%04x - 0x%04x reg=%d %s %s %s\n",
-          entry.start_address_, entry.end_address_, entry.reg_,
-          entry.name_, entry.descriptor_, signature);
-}
-
-/*
  * Helper for dumpInstruction(), which builds the string
  * representation for the index in the given instruction.
  * Returns a pointer to a buffer of sufficient size.
@@ -1198,7 +1188,19 @@
     return false;
   });
   fprintf(gOutFile, "      locals        : \n");
-  accessor.DecodeDebugLocalInfo(is_static, idx, dumpLocalsCb, nullptr);
+  accessor.DecodeDebugLocalInfo(is_static,
+                                idx,
+                                [&](const DexFile::LocalInfo& entry) {
+    const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
+    fprintf(gOutFile,
+            "        0x%04x - 0x%04x reg=%d %s %s %s\n",
+            entry.start_address_,
+            entry.end_address_,
+            entry.reg_,
+            entry.name_,
+            entry.descriptor_,
+            signature);
+  });
 }
 
 /*
@@ -1798,18 +1800,18 @@
   // Iterate over all classes.
   char* package = nullptr;
   const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
-  for (u4 i = 0; i < classDefsSize; i++) {
-    dumpClass(pDexFile, i, &package);
+  for (u4 j = 0; j < classDefsSize; j++) {
+    dumpClass(pDexFile, j, &package);
   }  // for
 
   // Iterate over all method handles.
-  for (u4 i = 0; i < pDexFile->NumMethodHandles(); ++i) {
-    dumpMethodHandle(pDexFile, i);
+  for (u4 j = 0; j < pDexFile->NumMethodHandles(); ++j) {
+    dumpMethodHandle(pDexFile, j);
   }  // for
 
   // Iterate over all call site ids.
-  for (u4 i = 0; i < pDexFile->NumCallSiteIds(); ++i) {
-    dumpCallSite(pDexFile, i);
+  for (u4 j = 0; j < pDexFile->NumCallSiteIds(); ++j) {
+    dumpCallSite(pDexFile, j);
   }  // for
 
   // Free the last package allocated.
diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc
index f4a3866..cf0d113 100644
--- a/dexdump/dexdump_main.cc
+++ b/dexdump/dexdump_main.cc
@@ -37,7 +37,7 @@
 /*
  * Shows usage.
  */
-static void usage(void) {
+static void usage() {
   LOG(ERROR) << "Copyright (C) 2007 The Android Open Source Project\n";
   LOG(ERROR) << gProgName << ": [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-j] [-l layout] [-o outfile]"
                   " dexfile...\n";
@@ -64,7 +64,7 @@
   gOptions.verbose = true;
 
   // Parse all arguments.
-  while (1) {
+  while (true) {
     const int ic = getopt(argc, argv, "acdefghijl:o:");
     if (ic < 0) {
       break;  // done
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 148c0c7..1b8412d 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -247,7 +247,7 @@
  * NULL-terminated.
  */
 static void Asciify(char* out, const unsigned char* data, size_t len) {
-  while (len--) {
+  for (; len != 0u; --len) {
     if (*data < 0x20) {
       // Could do more here, but we don't need them yet.
       switch (*data) {
@@ -1037,17 +1037,6 @@
 }
 
 /*
- * Callback for dumping locals table entry.
- */
-static void DumpLocalsCb(void* context, const DexFile::LocalInfo& entry) {
-  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
-  FILE* out_file = reinterpret_cast<FILE*>(context);
-  fprintf(out_file, "        0x%04x - 0x%04x reg=%d %s %s %s\n",
-          entry.start_address_, entry.end_address_, entry.reg_,
-          entry.name_, entry.descriptor_, signature);
-}
-
-/*
  * Lookup functions.
  */
 static const char* StringDataByIdx(uint32_t idx, dex_ir::Header* header) {
@@ -1140,8 +1129,18 @@
                                         StringDataByTypeIdx(dchecked_integral_cast<uint16_t>(idx),
                                                             this->header_);
                                   },
-                                  DumpLocalsCb,
-                                  out_file_);
+                                  [&](const DexFile::LocalInfo& entry) {
+                                    const char* signature =
+                                        entry.signature_ != nullptr ? entry.signature_ : "";
+                                    fprintf(out_file_,
+                                            "        0x%04x - 0x%04x reg=%d %s %s %s\n",
+                                            entry.start_address_,
+                                            entry.end_address_,
+                                            entry.reg_,
+                                            entry.name_,
+                                            entry.descriptor_,
+                                            signature);
+                                  });
   }
 }
 
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 9f73347..a851cfa 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -42,7 +42,7 @@
 /*
  * Shows usage.
  */
-static void Usage(void) {
+static void Usage() {
   LOG(ERROR) << "Copyright (C) 2016 The Android Open Source Project\n";
   LOG(ERROR) << kProgramName
              << ": [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]"
@@ -85,7 +85,7 @@
   bool want_usage = false;
 
   // Parse all arguments.
-  while (1) {
+  while (true) {
     const int ic = getopt(argc, argv, "abcdefghil:o:p:stuvw:x:");
     if (ic < 0) {
       break;  // done
diff --git a/dexlist/Android.bp b/dexlist/Android.bp
index bd521ac..217a024 100644
--- a/dexlist/Android.bp
+++ b/dexlist/Android.bp
@@ -14,18 +14,14 @@
 
 art_cc_binary {
     name: "dexlist",
+    defaults: ["art_defaults"],
     host_supported: true,
     srcs: ["dexlist.cc"],
-    cflags: ["-Wall", "-Werror"],
     shared_libs: [
         "libdexfile",
         "libartbase",
         "libbase"
     ],
-    // TODO: fix b/72216369 and remove the need for this.
-    include_dirs: [
-        "art/runtime"  // dex utils.
-    ],
 }
 
 art_cc_test {
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index c2514a1..067daa7 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -55,9 +55,9 @@
 /*
  * Data types that match the definitions in the VM specification.
  */
-typedef uint8_t  u1;
-typedef uint32_t u4;
-typedef uint64_t u8;
+using u1 = uint8_t;
+using u4 = uint32_t;
+using u8 = uint64_t;
 
 /*
  * Returns a newly-allocated string for the "dot version" of the class
@@ -197,7 +197,7 @@
 /*
  * Shows usage.
  */
-static void usage(void) {
+static void usage() {
   LOG(ERROR) << "Copyright (C) 2007 The Android Open Source Project\n";
   LOG(ERROR) << gProgName << ": [-m p.c.m] [-o outfile] dexfile...";
   LOG(ERROR) << "";
@@ -212,7 +212,7 @@
   memset(&gOptions, 0, sizeof(gOptions));
 
   // Parse all arguments.
-  while (1) {
+  while (true) {
     const int ic = getopt(argc, argv, "o:m:");
     if (ic < 0) {
       break;  // done
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index c1a6f59..94ea006 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -137,12 +137,12 @@
 void DisassemblerArm::CustomDisassembler::CustomDisassemblerStream::PrintLiteral(LocationType type,
                                                                                  int32_t offset) {
   // Literal offsets are not required to be aligned, so we may need unaligned access.
-  typedef const int16_t unaligned_int16_t __attribute__ ((aligned (1)));
-  typedef const uint16_t unaligned_uint16_t __attribute__ ((aligned (1)));
-  typedef const int32_t unaligned_int32_t __attribute__ ((aligned (1)));
-  typedef const int64_t unaligned_int64_t __attribute__ ((aligned (1)));
-  typedef const float unaligned_float __attribute__ ((aligned (1)));
-  typedef const double unaligned_double __attribute__ ((aligned (1)));
+  using unaligned_int16_t  __attribute__((__aligned__(1))) = const int16_t;
+  using unaligned_uint16_t __attribute__((__aligned__(1))) = const uint16_t;
+  using unaligned_int32_t  __attribute__((__aligned__(1))) = const int32_t;
+  using unaligned_int64_t  __attribute__((__aligned__(1))) = const int64_t;
+  using unaligned_float    __attribute__((__aligned__(1))) = const float;
+  using unaligned_double   __attribute__((__aligned__(1))) = const double;
 
   // Zeros are used for the LocationType values this function does not care about.
   const size_t literal_size[kVst4Location + 1] = {
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 51b3d75..1b2e8d7 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -762,7 +762,8 @@
 
     std::unordered_set<size_t> dirty_members;
     // Examine the members comprising the ArtMethod, computing which members are dirty.
-    for (const std::pair<size_t, MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) {
+    for (const std::pair<const size_t,
+                         MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) {
       const size_t offset = p.first;
       if (memcmp(base_ptr + offset, remote_bytes + offset, p.second.size_) != 0) {
         dirty_members.insert(p.first);
@@ -788,7 +789,8 @@
   void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
     DumpSamplesAndOffsetCount();
     os_ << "      offset to field map:\n";
-    for (const std::pair<size_t, MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) {
+    for (const std::pair<const size_t,
+                         MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) {
       const size_t offset = p.first;
       const size_t size = p.second.size_;
       os_ << StringPrintf("        %zu-%zu: ", offset, offset + size - 1)
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 1b603b5..19f1532 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -29,6 +29,8 @@
         "base/hex_dump.cc",
         "base/logging.cc",
         "base/malloc_arena_pool.cc",
+        "base/membarrier.cc",
+        "base/memfd.cc",
         "base/memory_region.cc",
         "base/mem_map.cc",
         // "base/mem_map_fuchsia.cc", put in target when fuchsia supported by soong
@@ -186,6 +188,8 @@
         "base/indenter_test.cc",
         "base/leb128_test.cc",
         "base/logging_test.cc",
+        "base/memfd_test.cc",
+        "base/membarrier_test.cc",
         "base/memory_region_test.cc",
         "base/mem_map_test.cc",
         "base/safe_copy_test.cc",
diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h
index bc79ff2..2a2a737 100644
--- a/libartbase/base/globals.h
+++ b/libartbase/base/globals.h
@@ -123,9 +123,12 @@
 // True if we allow moving classes.
 static constexpr bool kMovingClasses = !kMarkCompactSupport;
 // If true, enable generational collection when using the Concurrent Copying
-// collector, i.e. use sticky-bit CC for minor collections and (full) CC for
-// major collections.
-#ifdef ART_USE_GENERATIONAL_CC
+// (CC) collector, i.e. use sticky-bit CC for minor collections and (full) CC
+// for major collections.
+//
+// Generational CC collection is currently only compatible with Baker read
+// barriers.
+#if defined(ART_USE_GENERATIONAL_CC) && defined(ART_READ_BARRIER_TYPE_IS_BAKER)
 static constexpr bool kEnableGenerationalConcurrentCopyingCollection = true;
 #else
 static constexpr bool kEnableGenerationalConcurrentCopyingCollection = false;
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index 1bf553d..92551f1 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -692,6 +692,24 @@
                           int tail_prot,
                           std::string* error_msg,
                           bool use_debug_name) {
+  return RemapAtEnd(new_end,
+                    tail_name,
+                    tail_prot,
+                    MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS,
+                    /* fd */ -1,
+                    /* offset */ 0,
+                    error_msg,
+                    use_debug_name);
+}
+
+MemMap MemMap::RemapAtEnd(uint8_t* new_end,
+                          const char* tail_name,
+                          int tail_prot,
+                          int flags,
+                          int fd,
+                          off_t offset,
+                          std::string* error_msg,
+                          bool use_debug_name) {
   DCHECK_GE(new_end, Begin());
   DCHECK_LE(new_end, End());
   DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_);
@@ -715,9 +733,6 @@
   DCHECK_EQ(tail_base_begin + tail_base_size, old_base_end);
   DCHECK_ALIGNED(tail_base_size, kPageSize);
 
-  unique_fd fd;
-  int flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
-
   MEMORY_TOOL_MAKE_UNDEFINED(tail_base_begin, tail_base_size);
   // Note: Do not explicitly unmap the tail region, mmap() with MAP_FIXED automatically
   // removes old mappings for the overlapping region. This makes the operation atomic
@@ -726,13 +741,13 @@
                                                           tail_base_size,
                                                           tail_prot,
                                                           flags,
-                                                          fd.get(),
-                                                          0));
+                                                          fd,
+                                                          offset));
   if (actual == MAP_FAILED) {
     PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
-    *error_msg = StringPrintf("anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0) failed. See process "
+    *error_msg = StringPrintf("map(%p, %zd, 0x%x, 0x%x, %d, 0) failed. See process "
                               "maps in the log.", tail_base_begin, tail_base_size, tail_prot, flags,
-                              fd.get());
+                              fd);
     return Invalid();
   }
   // Update *this.
diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h
index 20eda32..309da27 100644
--- a/libartbase/base/mem_map.h
+++ b/libartbase/base/mem_map.h
@@ -261,6 +261,16 @@
                     std::string* error_msg,
                     bool use_debug_name = true);
 
+  // Unmap the pages of a file at end and remap them to create another memory map.
+  MemMap RemapAtEnd(uint8_t* new_end,
+                    const char* tail_name,
+                    int tail_prot,
+                    int tail_flags,
+                    int fd,
+                    off_t offset,
+                    std::string* error_msg,
+                    bool use_debug_name = true);
+
   // Take ownership of pages at the beginning of the mapping. The mapping must be an
   // anonymous reservation mapping, owning entire pages. The `byte_count` must not
   // exceed the size of this reservation.
diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc
index ab3d18f..bf143d4 100644
--- a/libartbase/base/mem_map_test.cc
+++ b/libartbase/base/mem_map_test.cc
@@ -455,6 +455,53 @@
 }
 #endif
 
+TEST_F(MemMapTest, RemapFileViewAtEnd) {
+  CommonInit();
+  std::string error_msg;
+  ScratchFile scratch_file;
+
+  // Create a scratch file 3 pages large.
+  constexpr size_t kMapSize = 3 * kPageSize;
+  std::unique_ptr<uint8_t[]> data(new uint8_t[kMapSize]());
+  memset(data.get(), 1, kPageSize);
+  memset(&data[0], 0x55, kPageSize);
+  memset(&data[kPageSize], 0x5a, kPageSize);
+  memset(&data[2 * kPageSize], 0xaa, kPageSize);
+  ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], kMapSize));
+
+  MemMap map = MemMap::MapFile(/*byte_count*/kMapSize,
+                               PROT_READ,
+                               MAP_PRIVATE,
+                               scratch_file.GetFd(),
+                               /*start*/0,
+                               /*low_4gb*/true,
+                               scratch_file.GetFilename().c_str(),
+                               &error_msg);
+  ASSERT_TRUE(map.IsValid()) << error_msg;
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(map.Size(), kMapSize);
+  ASSERT_LT(reinterpret_cast<uintptr_t>(map.BaseBegin()), 1ULL << 32);
+  ASSERT_EQ(data[0], *map.Begin());
+  ASSERT_EQ(data[kPageSize], *(map.Begin() + kPageSize));
+  ASSERT_EQ(data[2 * kPageSize], *(map.Begin() + 2 * kPageSize));
+
+  for (size_t offset = 2 * kPageSize; offset > 0; offset -= kPageSize) {
+    MemMap tail = map.RemapAtEnd(map.Begin() + offset,
+                                 "bad_offset_map",
+                                 PROT_READ,
+                                 MAP_PRIVATE | MAP_FIXED,
+                                 scratch_file.GetFd(),
+                                 offset,
+                                 &error_msg);
+    ASSERT_TRUE(tail.IsValid()) << error_msg;
+    ASSERT_TRUE(error_msg.empty());
+    ASSERT_EQ(offset, map.Size());
+    ASSERT_EQ(static_cast<size_t>(kPageSize), tail.Size());
+    ASSERT_EQ(tail.Begin(), map.Begin() + map.Size());
+    ASSERT_EQ(data[offset], *tail.Begin());
+  }
+}
+
 TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) {
   // Some MIPS32 hardware (namely the Creator Ci20 development board)
   // cannot allocate in the 2GB-4GB region.
diff --git a/libartbase/base/membarrier.cc b/libartbase/base/membarrier.cc
new file mode 100644
index 0000000..490dbf3
--- /dev/null
+++ b/libartbase/base/membarrier.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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 "membarrier.h"
+
+#include <errno.h>
+
+#include <sys/syscall.h>
+#include <unistd.h>
+#include "macros.h"
+
+#if defined(__BIONIC__)
+
+#include <atomic>
+#include <android/get_device_api_level.h>
+#include <linux/membarrier.h>
+
+#define CHECK_MEMBARRIER_CMD(art_value, membarrier_value) \
+  static_assert(static_cast<int>(art_value) == membarrier_value, "Bad value for " # art_value)
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kQuery, MEMBARRIER_CMD_QUERY);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kGlobal, MEMBARRIER_CMD_SHARED);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kPrivateExpedited, MEMBARRIER_CMD_PRIVATE_EXPEDITED);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kRegisterPrivateExpedited,
+                     MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kPrivateExpedited, MEMBARRIER_CMD_PRIVATE_EXPEDITED);
+#undef CHECK_MEMBARRIER_CMD
+
+#endif  // __BIONIC
+
+namespace art {
+
+#if defined(__NR_membarrier)
+
+int membarrier(MembarrierCommand command) {
+#if defined(__BIONIC__)
+  // Avoid calling membarrier on older Android versions where membarrier may be barred by secomp
+  // causing the current process to be killed. The probing here could be considered expensive so
+  // endeavour not to repeat too often.
+  static int api_level = android_get_device_api_level();
+  if (api_level < __ANDROID_API_Q__) {
+    errno = ENOSYS;
+    return -1;
+  }
+#endif  // __BIONIC__
+  return syscall(__NR_membarrier, static_cast<int>(command), 0);
+}
+
+#else  // __NR_membarrier
+
+int membarrier(MembarrierCommand command ATTRIBUTE_UNUSED) {
+  // In principle this could be supported on linux, but Android's prebuilt glibc does not include
+  // the system call number defintions (b/111199492).
+  errno = ENOSYS;
+  return -1;
+}
+
+#endif  // __NR_membarrier
+
+}  // namespace art
diff --git a/libartbase/base/membarrier.h b/libartbase/base/membarrier.h
new file mode 100644
index 0000000..f829fc1
--- /dev/null
+++ b/libartbase/base/membarrier.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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_LIBARTBASE_BASE_MEMBARRIER_H_
+#define ART_LIBARTBASE_BASE_MEMBARRIER_H_
+
+namespace art {
+  // Command types for the linux membarrier system call. Different Linux installation may include
+  // different subsets of these commands (at the same codepoints).
+  //
+  // Hardcoding these values is temporary until bionic and prebuilts glibc have an up to date
+  // linux/membarrier.h. The order and values follow the current linux definitions.
+  enum class MembarrierCommand : int  {
+    // MEMBARRIER_CMD_QUERY
+    kQuery = 0,
+    // MEMBARRIER_CMD_GLOBAL
+    kGlobal = (1 << 0),
+    // MEMBARRIER_CMD_GLOBAL_EXPEDITED
+    kGlobalExpedited = (1 << 1),
+    // MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED
+    kRegisterGlobalExpedited = (1 << 2),
+    // MEMBARRIER_CMD_PRIVATE_EXPEDITED
+    kPrivateExpedited = (1 << 3),
+    // MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED
+    kRegisterPrivateExpedited = (1 << 4),
+    // MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE
+    kPrivateExpeditedSyncCore = (1 << 5),
+    // MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE
+    kRegisterPrivateExpeditedSyncCore = (1 << 6)
+  };
+
+  // Call membarrier(2) if available on platform and return result. This method can fail if the
+  // command is not supported by the kernel. The underlying system call is linux specific.
+  int membarrier(MembarrierCommand command);
+
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_MEMBARRIER_H_
diff --git a/libartbase/base/membarrier_test.cc b/libartbase/base/membarrier_test.cc
new file mode 100644
index 0000000..3eedf14
--- /dev/null
+++ b/libartbase/base/membarrier_test.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "membarrier.h"
+
+class ScopedErrnoCleaner {
+ public:
+  ScopedErrnoCleaner() { errno = 0; }
+  ~ScopedErrnoCleaner() { errno = 0; }
+};
+
+bool HasMembarrier(art::MembarrierCommand cmd) {
+  ScopedErrnoCleaner errno_cleaner;
+  int supported_cmds = art::membarrier(art::MembarrierCommand::kQuery);
+  return (supported_cmds > 0) && ((supported_cmds & static_cast<int>(cmd)) != 0);
+}
+
+TEST(membarrier, query) {
+  ScopedErrnoCleaner errno_cleaner;
+  int supported = art::membarrier(art::MembarrierCommand::kQuery);
+  if (errno == 0) {
+    ASSERT_LE(0, supported);
+  } else {
+    ASSERT_TRUE(errno == ENOSYS && supported == -1);
+  }
+}
+
+TEST(membarrier, global_barrier) {
+  if (!HasMembarrier(art::MembarrierCommand::kGlobal)) {
+    GTEST_LOG_(INFO) << "MembarrierCommand::kGlobal not supported, skipping test.";
+    return;
+  }
+  ASSERT_EQ(0, art::membarrier(art::MembarrierCommand::kGlobal));
+}
+
+static const char* MembarrierCommandToName(art::MembarrierCommand cmd) {
+#define CASE_VALUE(x) case (x): return #x;
+  switch (cmd) {
+    CASE_VALUE(art::MembarrierCommand::kQuery);
+    CASE_VALUE(art::MembarrierCommand::kGlobal);
+    CASE_VALUE(art::MembarrierCommand::kGlobalExpedited);
+    CASE_VALUE(art::MembarrierCommand::kRegisterGlobalExpedited);
+    CASE_VALUE(art::MembarrierCommand::kPrivateExpedited);
+    CASE_VALUE(art::MembarrierCommand::kRegisterPrivateExpedited);
+    CASE_VALUE(art::MembarrierCommand::kPrivateExpeditedSyncCore);
+    CASE_VALUE(art::MembarrierCommand::kRegisterPrivateExpeditedSyncCore);
+  }
+}
+
+static void TestRegisterAndBarrierCommands(art::MembarrierCommand membarrier_cmd_register,
+                                           art::MembarrierCommand membarrier_cmd_barrier) {
+  if (!HasMembarrier(membarrier_cmd_register)) {
+    GTEST_LOG_(INFO) << MembarrierCommandToName(membarrier_cmd_register)
+        << " not supported, skipping test.";
+    return;
+  }
+  if (!HasMembarrier(membarrier_cmd_barrier)) {
+    GTEST_LOG_(INFO) << MembarrierCommandToName(membarrier_cmd_barrier)
+        << " not supported, skipping test.";
+    return;
+  }
+
+  ScopedErrnoCleaner errno_cleaner;
+
+  // Check barrier use without prior registration.
+  if (membarrier_cmd_register == art::MembarrierCommand::kRegisterGlobalExpedited) {
+    // Global barrier use is always okay.
+    ASSERT_EQ(0, art::membarrier(membarrier_cmd_barrier));
+  } else {
+    // Private barrier should fail.
+    ASSERT_EQ(-1, art::membarrier(membarrier_cmd_barrier));
+    ASSERT_EQ(EPERM, errno);
+    errno = 0;
+  }
+
+  // Check registration for barrier succeeds.
+  ASSERT_EQ(0, art::membarrier(membarrier_cmd_register));
+
+  // Check barrier use after registration succeeds.
+  ASSERT_EQ(0, art::membarrier(membarrier_cmd_barrier));
+}
+
+TEST(membarrier, global_expedited) {
+  TestRegisterAndBarrierCommands(art::MembarrierCommand::kRegisterGlobalExpedited,
+                                 art::MembarrierCommand::kGlobalExpedited);
+}
+
+TEST(membarrier, private_expedited) {
+  TestRegisterAndBarrierCommands(art::MembarrierCommand::kRegisterPrivateExpedited,
+                                 art::MembarrierCommand::kPrivateExpedited);
+}
+
+TEST(membarrier, private_expedited_sync_core) {
+  TestRegisterAndBarrierCommands(art::MembarrierCommand::kRegisterPrivateExpeditedSyncCore,
+                                 art::MembarrierCommand::kPrivateExpeditedSyncCore);
+}
diff --git a/libartbase/base/memfd.cc b/libartbase/base/memfd.cc
new file mode 100644
index 0000000..1afcd7b
--- /dev/null
+++ b/libartbase/base/memfd.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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 "memfd.h"
+
+#include <errno.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "macros.h"
+
+// When building for linux host, glibc in prebuilts does not include memfd_create system call
+// number. As a temporary testing measure, we add the definition here.
+#if defined(__linux__) && !defined(__NR_memfd_create)
+#if defined(__x86_64__)
+#define __NR_memfd_create 319
+#elif defined(__i386__)
+#define __NR_memfd_create 356
+#endif  // defined(__i386__)
+#endif  // defined(__linux__) && !defined(__NR_memfd_create)
+
+namespace art {
+
+#if defined(__NR_memfd_create)
+
+int memfd_create(const char* name, unsigned int flags) {
+  return syscall(__NR_memfd_create, name, flags);
+}
+
+#else  // __NR_memfd_create
+
+int memfd_create(const char* name ATTRIBUTE_UNUSED, unsigned int flags ATTRIBUTE_UNUSED) {
+  errno = ENOSYS;
+  return -1;
+}
+
+#endif  // __NR_memfd_create
+
+}  // namespace art
diff --git a/libartbase/base/memfd.h b/libartbase/base/memfd.h
new file mode 100644
index 0000000..4367198
--- /dev/null
+++ b/libartbase/base/memfd.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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_LIBARTBASE_BASE_MEMFD_H_
+#define ART_LIBARTBASE_BASE_MEMFD_H_
+
+namespace art {
+
+  // Call memfd(2) if available on platform and return result.
+int memfd_create(const char* name, unsigned int flags);
+
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_MEMFD_H_
diff --git a/libartbase/base/memfd_test.cc b/libartbase/base/memfd_test.cc
new file mode 100644
index 0000000..1edf3a1
--- /dev/null
+++ b/libartbase/base/memfd_test.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "memfd.h"
+
+TEST(memfd, basic) {
+  errno = 0;
+  int fd = art::memfd_create("memfd_create_test", 0);
+  if (fd < 0) {
+    ASSERT_EQ(ENOSYS, errno);
+    GTEST_LOG_(INFO) << "memfd_create not supported, skipping test.";
+    return;
+  }
+  ASSERT_TRUE(close(fd) == 0 || errno != EBADF);
+}
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index 74cc5b9..0f172fd 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -24,6 +24,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <fstream>
 #include <memory>
 
 #include "android-base/file.h"
@@ -213,42 +214,25 @@
   }
 }
 
-bool FlushInstructionPipeline() {
-  // membarrier(2) is only supported for target builds (b/111199492).
-#if defined(__BIONIC__)
-  static constexpr int kSyncCoreMask =
-      MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE |
-      MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE;
-  static bool have_probed = false;
-  static bool have_sync_core = false;
+std::string GetProcessStatus(const char* key) {
+  // Build search pattern of key and separator.
+  std::string pattern(key);
+  pattern.push_back(':');
 
-  if (UNLIKELY(!have_probed)) {
-    // Probe membarrier(2) commands supported by kernel.
-    int commands = syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0);
-    if (commands >= 0) {
-      have_sync_core = (commands & kSyncCoreMask) == kSyncCoreMask;
-      if (have_sync_core) {
-        // Register with kernel that we'll be using the private expedited sync core command.
-        CheckedCall(syscall,
-                    "membarrier register sync core",
-                    __NR_membarrier,
-                    MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
-                    0);
+  // Search for status lines starting with pattern.
+  std::ifstream fs("/proc/self/status");
+  std::string line;
+  while (std::getline(fs, line)) {
+    if (strncmp(pattern.c_str(), line.c_str(), pattern.size()) == 0) {
+      // Skip whitespace in matching line (if any).
+      size_t pos = line.find_first_not_of(" \t", pattern.size());
+      if (UNLIKELY(pos == std::string::npos)) {
+        break;
       }
+      return std::string(line, pos);
     }
-    have_probed = true;
   }
-
-  if (have_sync_core) {
-    CheckedCall(syscall,
-                "membarrier sync core",
-                __NR_membarrier,
-                MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE,
-                0);
-    return true;
-  }
-#endif  // defined(__BIONIC__)
-  return false;
+  return "<unknown>";
 }
 
 }  // namespace art
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index d85960d..9c71055 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -162,9 +162,6 @@
   __builtin___clear_cache(reinterpret_cast<char*>(begin), reinterpret_cast<char*>(end));
 }
 
-// Flush instruction pipeline. Returns true on success, false if feature is unsupported.
-bool FlushInstructionPipeline();
-
 template <typename T>
 constexpr PointerSize ConvertToPointerSize(T any) {
   if (any == 4 || any == 8) {
@@ -219,6 +216,11 @@
   }
 }
 
+// Lookup value for a given key in /proc/self/status. Keys and values are separated by a ':' in
+// the status file. Returns value found on success and "<unknown>" if the key is not found or
+// there is an I/O error.
+std::string GetProcessStatus(const char* key);
+
 }  // namespace art
 
 #endif  // ART_LIBARTBASE_BASE_UTILS_H_
diff --git a/libartbase/base/utils_test.cc b/libartbase/base/utils_test.cc
index 892d1fd..9bd50c3 100644
--- a/libartbase/base/utils_test.cc
+++ b/libartbase/base/utils_test.cc
@@ -126,4 +126,12 @@
   EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
 }
 
+TEST_F(UtilsTest, GetProcessStatus) {
+  EXPECT_EQ("utils_test", GetProcessStatus("Name"));
+  EXPECT_EQ("R (running)", GetProcessStatus("State"));
+  EXPECT_EQ("<unknown>", GetProcessStatus("tate"));
+  EXPECT_EQ("<unknown>", GetProcessStatus("e"));
+  EXPECT_EQ("<unknown>", GetProcessStatus("Dummy"));
+}
+
 }  // namespace art
diff --git a/libdexfile/dex/code_item_accessors-inl.h b/libdexfile/dex/code_item_accessors-inl.h
index c7e876e..bbf2224 100644
--- a/libdexfile/dex/code_item_accessors-inl.h
+++ b/libdexfile/dex/code_item_accessors-inl.h
@@ -184,19 +184,18 @@
   CodeItemDataAccessor::Init(code_item);
 }
 
-template<typename NewLocalCallback>
-inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static,
-                                                            uint32_t method_idx,
-                                                            NewLocalCallback new_local,
-                                                            void* context) const {
+template<typename NewLocalVisitor>
+inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(
+    bool is_static,
+    uint32_t method_idx,
+    const NewLocalVisitor& new_local) const {
   return dex_file_->DecodeDebugLocalInfo(RegistersSize(),
                                          InsSize(),
                                          InsnsSizeInCodeUnits(),
                                          DebugInfoOffset(),
                                          is_static,
                                          method_idx,
-                                         new_local,
-                                         context);
+                                         new_local);
 }
 
 template <typename Visitor>
diff --git a/libdexfile/dex/code_item_accessors.h b/libdexfile/dex/code_item_accessors.h
index c2aa23c..c307c9f 100644
--- a/libdexfile/dex/code_item_accessors.h
+++ b/libdexfile/dex/code_item_accessors.h
@@ -151,11 +151,10 @@
     return debug_info_offset_;
   }
 
-  template<typename NewLocalCallback>
+  template<typename NewLocalVisitor>
   bool DecodeDebugLocalInfo(bool is_static,
                             uint32_t method_idx,
-                            NewLocalCallback new_local,
-                            void* context) const;
+                            const NewLocalVisitor& new_local) const;
 
   // Visit each parameter in the debug information. Returns the line number.
   // The argument of the Visitor is dex::StringIndex.
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index eca9ee9..eae7efc 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -215,10 +215,9 @@
                                    uint16_t registers_size,
                                    uint16_t ins_size,
                                    uint16_t insns_size_in_code_units,
-                                   IndexToStringData index_to_string_data,
-                                   TypeIndexToStringData type_index_to_string_data,
-                                   NewLocalCallback new_local_callback,
-                                   void* context) {
+                                   const IndexToStringData& index_to_string_data,
+                                   const TypeIndexToStringData& type_index_to_string_data,
+                                   const NewLocalCallback& new_local_callback) {
   if (stream == nullptr) {
     return false;
   }
@@ -278,7 +277,7 @@
         for (uint16_t reg = 0; reg < registers_size; reg++) {
           if (local_in_reg[reg].is_live_) {
             local_in_reg[reg].end_address_ = insns_size_in_code_units;
-            new_local_callback(context, local_in_reg[reg]);
+            new_local_callback(local_in_reg[reg]);
           }
         }
         return true;
@@ -307,7 +306,7 @@
         // Emit what was previously there, if anything
         if (local_in_reg[reg].is_live_) {
           local_in_reg[reg].end_address_ = address;
-          new_local_callback(context, local_in_reg[reg]);
+          new_local_callback(local_in_reg[reg]);
         }
 
         local_in_reg[reg].name_ = index_to_string_data(name_idx);
@@ -329,7 +328,7 @@
         // closed register is sloppy, but harmless if no further action is taken.
         if (local_in_reg[reg].is_live_) {
           local_in_reg[reg].end_address_ = address;
-          new_local_callback(context, local_in_reg[reg]);
+          new_local_callback(local_in_reg[reg]);
           local_in_reg[reg].is_live_ = false;
         }
         break;
@@ -369,8 +368,7 @@
                                    uint32_t debug_info_offset,
                                    bool is_static,
                                    uint32_t method_idx,
-                                   NewLocalCallback new_local_callback,
-                                   void* context) const {
+                                   const NewLocalCallback& new_local_callback) const {
   const uint8_t* const stream = GetDebugInfoStream(debug_info_offset);
   if (stream == nullptr) {
     return false;
@@ -396,8 +394,7 @@
                                 return StringByTypeIdx(dex::TypeIndex(
                                     dchecked_integral_cast<uint16_t>(idx)));
                               },
-                              new_local_callback,
-                              context);
+                              new_local_callback);
 }
 
 template<typename DexDebugNewPosition, typename IndexToStringData>
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 3e4a481..30d8b6d 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -874,10 +874,9 @@
                                    uint16_t registers_size,
                                    uint16_t ins_size,
                                    uint16_t insns_size_in_code_units,
-                                   IndexToStringData index_to_string_data,
-                                   TypeIndexToStringData type_index_to_string_data,
-                                   NewLocalCallback new_local,
-                                   void* context);
+                                   const IndexToStringData& index_to_string_data,
+                                   const TypeIndexToStringData& type_index_to_string_data,
+                                   const NewLocalCallback& new_local) NO_THREAD_SAFETY_ANALYSIS;
   template<typename NewLocalCallback>
   bool DecodeDebugLocalInfo(uint32_t registers_size,
                             uint32_t ins_size,
@@ -885,8 +884,7 @@
                             uint32_t debug_info_offset,
                             bool is_static,
                             uint32_t method_idx,
-                            NewLocalCallback new_local,
-                            void* context) const;
+                            const NewLocalCallback& new_local) const;
 
   // Returns false if there is no debugging information or if it cannot be decoded.
   template<typename DexDebugNewPosition, typename IndexToStringData>
diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc
index 400c32b..4aafc66 100644
--- a/libdexfile/dex/dex_file_loader.cc
+++ b/libdexfile/dex/dex_file_loader.cc
@@ -25,10 +25,6 @@
 #include "standard_dex_file.h"
 #include "ziparchive/zip_archive.h"
 
-// system/core/zip_archive definitions.
-struct ZipEntry;
-typedef void* ZipArchiveHandle;
-
 namespace art {
 
 namespace {
diff --git a/libdexfile/dex/dex_file_loader_test.cc b/libdexfile/dex/dex_file_loader_test.cc
index 5bb01dd..5378617 100644
--- a/libdexfile/dex/dex_file_loader_test.cc
+++ b/libdexfile/dex/dex_file_loader_test.cc
@@ -480,10 +480,6 @@
   EXPECT_EQ(raw->StringByTypeIdx(idx), nullptr);
 }
 
-static void Callback(void* context ATTRIBUTE_UNUSED,
-                     const DexFile::LocalInfo& entry ATTRIBUTE_UNUSED) {
-}
-
 TEST_F(DexFileLoaderTest, OpenDexDebugInfoLocalNullType) {
   std::vector<uint8_t> dex_bytes;
   std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64(kRawDexDebugInfoLocalNullType,
@@ -496,7 +492,7 @@
   const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def,
                                                                                 kMethodIdx));
   CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx);
-  ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr));
+  ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, VoidFunctor()));
 }
 
 }  // namespace art
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index fd011c8..f273c84 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -866,7 +866,7 @@
 bool DexFileVerifier::CheckEncodedArray() {
   DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
 
-  while (size--) {
+  for (; size != 0u; --size) {
     if (!CheckEncodedValue()) {
       failure_reason_ = StringPrintf("Bad encoded_array value: %s", failure_reason_.c_str());
       return false;
@@ -1304,7 +1304,7 @@
   }
 
   uint32_t last_addr = 0;
-  while (try_items_size--) {
+  for (; try_items_size != 0u; --try_items_size) {
     if (UNLIKELY(try_items->start_addr_ < last_addr)) {
       ErrorStringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_);
       return false;
@@ -1884,7 +1884,7 @@
   ptr_ = begin_;
 
   // Check the items listed in the map.
-  while (count--) {
+  for (; count != 0u; --count) {
     const size_t current_offset = offset;
     uint32_t section_offset = item->offset_;
     uint32_t section_count = item->size_;
@@ -2554,7 +2554,7 @@
   const DexFile::AnnotationSetRefItem* item = list->list_;
   uint32_t count = list->size_;
 
-  while (count--) {
+  for (; count != 0u; --count) {
     if (item->annotations_off_ != 0 &&
         !CheckOffsetToTypeMap(item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
       return false;
@@ -2839,7 +2839,7 @@
   uint32_t count = map->size_;
 
   // Cross check the items listed in the map.
-  while (count--) {
+  for (; count != 0u; --count) {
     uint32_t section_offset = item->offset_;
     uint32_t section_count = item->size_;
     DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index 1aaeabd..369615d 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -62,6 +62,7 @@
     kLightGreylist,
     kDarkGreylist,
     kBlacklist,
+    kNoList,
   };
 
   static ALWAYS_INLINE ApiList DecodeFromDex(uint32_t dex_access_flags) {
@@ -159,6 +160,9 @@
     case HiddenApiAccessFlags::kBlacklist:
       os << "blacklist";
       break;
+    case HiddenApiAccessFlags::kNoList:
+      os << "no list";
+      break;
   }
   return os;
 }
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index 9b70e62..f5e08da 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -23,12 +23,15 @@
 #include <unistd.h>
 #include <zlib.h>
 
+#include <algorithm>
 #include <cerrno>
 #include <climits>
 #include <cstdlib>
+#include <iostream>
+#include <numeric>
+#include <random>
 #include <string>
 #include <vector>
-#include <iostream>
 
 #include "android-base/file.h"
 
@@ -1872,43 +1875,42 @@
     uint16_t method_percentage,
     uint16_t class_percentage,
     uint32_t random_seed) {
-  std::srand(random_seed);
   ProfileCompilationInfo info;
+  std::default_random_engine rng(random_seed);
+  auto create_shuffled_range = [&rng](uint32_t take, uint32_t out_of) {
+    CHECK_LE(take, out_of);
+    std::vector<uint32_t> vec(out_of);
+    std::iota(vec.begin(), vec.end(), 0u);
+    std::shuffle(vec.begin(), vec.end(), rng);
+    vec.erase(vec.begin() + take, vec.end());
+    std::sort(vec.begin(), vec.end());
+    return vec;
+  };
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     const std::string& location = dex_file->GetLocation();
     uint32_t checksum = dex_file->GetLocationChecksum();
 
     uint32_t number_of_classes = dex_file->NumClassDefs();
     uint32_t classes_required_in_profile = (number_of_classes * class_percentage) / 100;
-    uint32_t class_start_index = rand() % number_of_classes;
-    for (uint32_t i = 0; i < number_of_classes && classes_required_in_profile; ++i) {
-      if (number_of_classes - i == classes_required_in_profile ||
-          std::rand() % (number_of_classes - i - classes_required_in_profile) == 0) {
-        uint32_t class_index = (i + class_start_index) % number_of_classes;
-        info.AddClassIndex(location,
-                           checksum,
-                           dex_file->GetClassDef(class_index).class_idx_,
-                           dex_file->NumMethodIds());
-        classes_required_in_profile--;
-      }
+    for (uint32_t class_index : create_shuffled_range(classes_required_in_profile,
+                                                      number_of_classes)) {
+      info.AddClassIndex(location,
+                         checksum,
+                         dex_file->GetClassDef(class_index).class_idx_,
+                         dex_file->NumMethodIds());
     }
 
     uint32_t number_of_methods = dex_file->NumMethodIds();
     uint32_t methods_required_in_profile = (number_of_methods * method_percentage) / 100;
-    uint32_t method_start_index = rand() % number_of_methods;
-    for (uint32_t i = 0; i < number_of_methods && methods_required_in_profile; ++i) {
-      if (number_of_methods - i == methods_required_in_profile ||
-          std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
-        uint32_t method_index = (method_start_index + i) % number_of_methods;
-        // Alternate between startup and post startup.
-        uint32_t flags = MethodHotness::kFlagHot;
-        flags |= ((method_index & 1) != 0)
-            ? MethodHotness::kFlagPostStartup
-            : MethodHotness::kFlagStartup;
-        info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
-                            MethodReference(dex_file.get(), method_index));
-        methods_required_in_profile--;
-      }
+    for (uint32_t method_index : create_shuffled_range(methods_required_in_profile,
+                                                       number_of_methods)) {
+      // Alternate between startup and post startup.
+      uint32_t flags = MethodHotness::kFlagHot;
+      flags |= ((method_index & 1) != 0)
+                   ? MethodHotness::kFlagPostStartup
+                   : MethodHotness::kFlagStartup;
+      info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
+                          MethodReference(dex_file.get(), method_index));
     }
   }
   return info.Save(fd);
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 707fc1c..d30ec31 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -419,7 +419,7 @@
     return instruction_set_;
   }
 
-  typedef std::vector<std::unique_ptr<const DexFile>> DexFileUniqV;
+  using DexFileUniqV = std::vector<std::unique_ptr<const DexFile>>;
 
   bool Dump(std::ostream& os) {
     bool success = true;
@@ -2480,7 +2480,7 @@
       size_t bytes;
       size_t count;
     };
-    typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable;
+    using SizeAndCountTable = SafeMap<std::string, SizeAndCount>;
     SizeAndCountTable sizes_and_counts;
 
     void Update(const char* descriptor, size_t object_bytes_in) {
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 2ca87fd..aedec27 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -51,17 +51,6 @@
       dex_file->CalculateChecksum();
 }
 
-static void UnhideApis(const art::DexFile& target_dex_file) {
-  for (art::ClassAccessor accessor : target_dex_file.GetClasses()) {
-    for (const art::ClassAccessor::Field& field : accessor.GetFields()) {
-      field.UnHideAccessFlags();
-    }
-    for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
-      method.UnHideAccessFlags();
-    }
-  }
-}
-
 static const art::VdexFile* GetVdex(const art::DexFile& original_dex_file) {
   const art::OatDexFile* oat_dex = original_dex_file.GetOatDexFile();
   if (oat_dex == nullptr) {
@@ -80,7 +69,7 @@
   if (vdex != nullptr) {
     vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true);
   }
-  UnhideApis(new_dex_file);
+  new_dex_file.UnhideApis();
 }
 
 static void DCheckVerifyDexFile(const art::DexFile& dex) {
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 742a9fa..2958941 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -206,76 +206,59 @@
     return ERR(ABSENT_INFORMATION);
   }
 
-  struct LocalVariableContext {
-    explicit LocalVariableContext(jvmtiEnv* jenv) : env_(jenv), variables_(), err_(OK) {}
+  std::vector<jvmtiLocalVariableEntry> variables;
+  jvmtiError err = OK;
 
-    static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
-      reinterpret_cast<LocalVariableContext*>(raw_ctx)->Insert(entry);
+  auto release = [&](jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) {
+    jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables.size();
+    if (err != OK ||
+        (err = env->Allocate(table_size,
+                              reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) {
+      for (jvmtiLocalVariableEntry& e : variables) {
+        env->Deallocate(reinterpret_cast<unsigned char*>(e.name));
+        env->Deallocate(reinterpret_cast<unsigned char*>(e.signature));
+        env->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature));
+      }
+      return err;
     }
-
-    void Insert(const art::DexFile::LocalInfo& entry) {
-      if (err_ != OK) {
-        return;
-      }
-      JvmtiUniquePtr<char[]> name_str = CopyString(env_, entry.name_, &err_);
-      if (err_ != OK) {
-        return;
-      }
-      JvmtiUniquePtr<char[]> sig_str = CopyString(env_, entry.descriptor_, &err_);
-      if (err_ != OK) {
-        return;
-      }
-      JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env_, entry.signature_, &err_);
-      if (err_ != OK) {
-        return;
-      }
-      variables_.push_back({
-        .start_location = static_cast<jlocation>(entry.start_address_),
-        .length = static_cast<jint>(entry.end_address_ - entry.start_address_),
-        .name = name_str.release(),
-        .signature = sig_str.release(),
-        .generic_signature = generic_sig_str.release(),
-        .slot = entry.reg_,
-      });
-    }
-
-    jvmtiError Release(jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) {
-      jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables_.size();
-      if (err_ != OK ||
-          (err_ = env_->Allocate(table_size,
-                                 reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) {
-        Cleanup();
-        return err_;
-      } else {
-        *out_entry_count_ptr = variables_.size();
-        memcpy(*out_table_ptr, variables_.data(), table_size);
-        return OK;
-      }
-    }
-
-    void Cleanup() {
-      for (jvmtiLocalVariableEntry& e : variables_) {
-        env_->Deallocate(reinterpret_cast<unsigned char*>(e.name));
-        env_->Deallocate(reinterpret_cast<unsigned char*>(e.signature));
-        env_->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature));
-      }
-    }
-
-    jvmtiEnv* env_;
-    std::vector<jvmtiLocalVariableEntry> variables_;
-    jvmtiError err_;
+    *out_entry_count_ptr = variables.size();
+    memcpy(*out_table_ptr, variables.data(), table_size);
+    return OK;
   };
 
-  LocalVariableContext context(env);
+  auto visitor = [&](const art::DexFile::LocalInfo& entry) {
+    if (err != OK) {
+      return;
+    }
+    JvmtiUniquePtr<char[]> name_str = CopyString(env, entry.name_, &err);
+    if (err != OK) {
+      return;
+    }
+    JvmtiUniquePtr<char[]> sig_str = CopyString(env, entry.descriptor_, &err);
+    if (err != OK) {
+      return;
+    }
+    JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env, entry.signature_, &err);
+    if (err != OK) {
+      return;
+    }
+    variables.push_back({
+      .start_location = static_cast<jlocation>(entry.start_address_),
+      .length = static_cast<jint>(entry.end_address_ - entry.start_address_),
+      .name = name_str.release(),
+      .signature = sig_str.release(),
+      .generic_signature = generic_sig_str.release(),
+      .slot = entry.reg_,
+    });
+  };
+
   if (!accessor.DecodeDebugLocalInfo(art_method->IsStatic(),
                                      art_method->GetDexMethodIndex(),
-                                     LocalVariableContext::Callback,
-                                     &context)) {
+                                     visitor)) {
     // Something went wrong with decoding the debug information. It might as well not be there.
     return ERR(ABSENT_INFORMATION);
-  } else {
-    return context.Release(entry_count_ptr, table_ptr);
   }
+  return release(entry_count_ptr, table_ptr);
 }
 
 jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
@@ -614,55 +597,25 @@
     if (!accessor.HasCodeItem()) {
       return ERR(OPAQUE_FRAME);
     }
-
-    struct GetLocalVariableInfoContext {
-      explicit GetLocalVariableInfoContext(jint slot,
-                                          uint32_t pc,
-                                          std::string* out_descriptor,
-                                          art::Primitive::Type* out_type)
-          : found_(false), jslot_(slot), pc_(pc), descriptor_(out_descriptor), type_(out_type) {
-        *descriptor_ = "";
-        *type_ = art::Primitive::kPrimVoid;
+    bool found = false;
+    *type = art::Primitive::kPrimVoid;
+    descriptor->clear();
+    auto visitor = [&](const art::DexFile::LocalInfo& entry) {
+      if (!found &&
+          entry.start_address_ <= dex_pc &&
+          entry.end_address_ > dex_pc &&
+          entry.reg_ == slot_) {
+        found = true;
+        *type = art::Primitive::GetType(entry.descriptor_[0]);
+        *descriptor = entry.descriptor_;
       }
-
-      static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
-        reinterpret_cast<GetLocalVariableInfoContext*>(raw_ctx)->Handle(entry);
-      }
-
-      void Handle(const art::DexFile::LocalInfo& entry) {
-        if (found_) {
-          return;
-        } else if (entry.start_address_ <= pc_ &&
-                   entry.end_address_ > pc_ &&
-                   entry.reg_ == jslot_) {
-          found_ = true;
-          *type_ = art::Primitive::GetType(entry.descriptor_[0]);
-          *descriptor_ = entry.descriptor_;
-        }
-        return;
-      }
-
-      bool found_;
-      jint jslot_;
-      uint32_t pc_;
-      std::string* descriptor_;
-      art::Primitive::Type* type_;
     };
-
-    GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type);
-    if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(),
-                                        accessor.InsSize(),
-                                        accessor.InsnsSizeInCodeUnits(),
-                                        accessor.DebugInfoOffset(),
-                                        method->IsStatic(),
-                                        method->GetDexMethodIndex(),
-                                        GetLocalVariableInfoContext::Callback,
-                                        &context) || !context.found_) {
+    if (!accessor.DecodeDebugLocalInfo(method->IsStatic(), method->GetDexMethodIndex(), visitor) ||
+        !found) {
       // Something went wrong with decoding the debug information. It might as well not be there.
       return ERR(INVALID_SLOT);
-    } else {
-      return OK;
     }
+    return OK;
   }
 
   jvmtiError result_;
diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc
index 6492b96..79ae987 100644
--- a/patchoat/patchoat_test.cc
+++ b/patchoat/patchoat_test.cc
@@ -405,7 +405,7 @@
   std::vector<std::string> patchoat_image_shortened_basenames(patchoat_image_basenames.size());
   for (size_t i = 0; i < patchoat_image_basenames.size(); i++) {
     patchoat_image_shortened_basenames[i] =
-        patchoat_image_basenames[i].substr(patchoat_image_basenames[i].find_last_of("@") + 1);
+        patchoat_image_basenames[i].substr(patchoat_image_basenames[i].find_last_of('@') + 1);
   }
   ASSERT_EQ(dex2oat_image_basenames, patchoat_image_shortened_basenames);
 
@@ -515,16 +515,16 @@
     std::vector<std::string> rel_shortened_basenames(rel_basenames.size());
     std::vector<std::string> relocated_image_shortened_basenames(relocated_image_basenames.size());
     for (size_t i = 0; i < rel_basenames.size(); i++) {
-      rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of("@") + 1);
+      rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of('@') + 1);
       rel_shortened_basenames[i] =
-          rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find("."));
+          rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find('.'));
     }
     for (size_t i = 0; i < relocated_image_basenames.size(); i++) {
       relocated_image_shortened_basenames[i] =
-          relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of("@") + 1);
+          relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of('@') + 1);
       relocated_image_shortened_basenames[i] =
           relocated_image_shortened_basenames[i].substr(
-              0, relocated_image_shortened_basenames[i].find("."));
+              0, relocated_image_shortened_basenames[i].find('.'));
     }
     ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames);
   }
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 286b686..f9707d3 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -116,9 +116,9 @@
   void SetupBasicProfile(const std::string& id,
                          uint32_t checksum,
                          uint16_t number_of_methods,
-                         const std::vector<uint32_t> hot_methods,
-                         const std::vector<uint32_t> startup_methods,
-                         const std::vector<uint32_t> post_startup_methods,
+                         const std::vector<uint32_t>& hot_methods,
+                         const std::vector<uint32_t>& startup_methods,
+                         const std::vector<uint32_t>& post_startup_methods,
                          const ScratchFile& profile,
                          ProfileCompilationInfo* info) {
     std::string dex_location = "location1" + id;
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 28b2912..b2ddff3 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -227,18 +227,15 @@
       // No mutexes have been created yet during at startup.
       return;
     }
-    typedef std::set<BaseMutex*>::const_iterator It;
     os << "(Contended)\n";
-    for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) {
-      BaseMutex* mutex = *it;
+    for (const BaseMutex* mutex : *all_mutexes) {
       if (mutex->HasEverContended()) {
         mutex->Dump(os);
         os << "\n";
       }
     }
     os << "(Never contented)\n";
-    for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) {
-      BaseMutex* mutex = *it;
+    for (const BaseMutex* mutex : *all_mutexes) {
       if (!mutex->HasEverContended()) {
         mutex->Dump(os);
         os << "\n";
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index befeea4..bcc3a22 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -307,7 +307,7 @@
     return lhs.size < rhs.size || (lhs.size == rhs.size && lhs.start_offset > rhs.start_offset);
   }
 };
-typedef std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator> FieldGaps;
+using FieldGaps = std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator>;
 
 // Adds largest aligned gaps to queue of gaps.
 static void AddFieldGap(uint32_t gap_start, uint32_t gap_end, FieldGaps* gaps) {
@@ -2301,7 +2301,7 @@
   return klass;
 }
 
-typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
+using ClassPathEntry = std::pair<const DexFile*, const DexFile::ClassDef*>;
 
 // Search a collection of DexFiles for a descriptor
 ClassPathEntry FindInClassPath(const char* descriptor,
@@ -6448,7 +6448,7 @@
 // iftable must be large enough to hold all interfaces without changing its size.
 static size_t FillIfTable(ObjPtr<mirror::IfTable> iftable,
                           size_t super_ifcount,
-                          std::vector<ObjPtr<mirror::Class>> to_process)
+                          const std::vector<ObjPtr<mirror::Class>>& to_process)
     REQUIRES(Roles::uninterruptible_)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   // This is the set of all class's already in the iftable. Used to make checking if a class has
@@ -8474,7 +8474,7 @@
   target_method->GetShorty(&shorty_length);
   int32_t num_params = static_cast<int32_t>(shorty_length + receiver_count - 1);
 
-  StackHandleScope<7> hs(self);
+  StackHandleScope<5> hs(self);
   ObjPtr<mirror::Class> array_of_class = GetClassRoot<mirror::ObjectArray<mirror::Class>>(this);
   Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
       mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
@@ -8483,20 +8483,25 @@
     return nullptr;
   }
 
+  const DexFile* dex_file = referrer->GetDexFile();
+  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_handle.field_or_method_idx_);
   int32_t index = 0;
   if (receiver_count != 0) {
-    // Insert receiver
-    method_params->Set(index++, target_method->GetDeclaringClass());
+    // Insert receiver. Use the class identified in the method handle rather than the declaring
+    // class of the resolved method which may be super class or default interface method
+    // (b/115964401).
+    ObjPtr<mirror::Class> receiver_class = LookupResolvedType(method_id.class_idx_, referrer);
+    // receiver_class should have been resolved when resolving the target method.
+    DCHECK(receiver_class != nullptr);
+    method_params->Set(index++, receiver_class);
   }
-  DexFileParameterIterator it(*target_method->GetDexFile(), target_method->GetPrototype());
-  Handle<mirror::DexCache> target_method_dex_cache(hs.NewHandle(target_method->GetDexCache()));
-  Handle<mirror::ClassLoader> target_method_class_loader(hs.NewHandle(target_method->GetClassLoader()));
+
+  const DexFile::ProtoId& proto_id = dex_file->GetProtoId(method_id.proto_idx_);
+  DexFileParameterIterator it(*dex_file, proto_id);
   while (it.HasNext()) {
     DCHECK_LT(index, num_params);
     const dex::TypeIndex type_idx = it.GetTypeIdx();
-    ObjPtr<mirror::Class> klass = ResolveType(type_idx,
-                                              target_method_dex_cache,
-                                              target_method_class_loader);
+    ObjPtr<mirror::Class> klass = ResolveType(type_idx, referrer);
     if (nullptr == klass) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
@@ -8505,7 +8510,8 @@
     it.Next();
   }
 
-  Handle<mirror::Class> return_type = hs.NewHandle(target_method->ResolveReturnType());
+  Handle<mirror::Class> return_type =
+      hs.NewHandle(ResolveType(proto_id.return_type_idx_, referrer));
   if (UNLIKELY(return_type.IsNull())) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7b4fa6e..b108920 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1700,37 +1700,6 @@
 
 void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool with_generic,
                               JDWP::ExpandBuf* pReply) {
-  struct DebugCallbackContext {
-    ArtMethod* method;
-    JDWP::ExpandBuf* pReply;
-    size_t variable_count;
-    bool with_generic;
-
-    static void Callback(void* context, const DexFile::LocalInfo& entry)
-        REQUIRES_SHARED(Locks::mutator_lock_) {
-      DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
-
-      uint16_t slot = entry.reg_;
-      VLOG(jdwp) << StringPrintf("    %2zd: %d(%d) '%s' '%s' '%s' actual slot=%d mangled slot=%d",
-                                 pContext->variable_count, entry.start_address_,
-                                 entry.end_address_ - entry.start_address_,
-                                 entry.name_, entry.descriptor_, entry.signature_, slot,
-                                 MangleSlot(slot, pContext->method));
-
-      slot = MangleSlot(slot, pContext->method);
-
-      expandBufAdd8BE(pContext->pReply, entry.start_address_);
-      expandBufAddUtf8String(pContext->pReply, entry.name_);
-      expandBufAddUtf8String(pContext->pReply, entry.descriptor_);
-      if (pContext->with_generic) {
-        expandBufAddUtf8String(pContext->pReply, entry.signature_);
-      }
-      expandBufAdd4BE(pContext->pReply, entry.end_address_- entry.start_address_);
-      expandBufAdd4BE(pContext->pReply, slot);
-
-      ++pContext->variable_count;
-    }
-  };
   ArtMethod* m = FromMethodId(method_id);
   CodeItemDebugInfoAccessor accessor(m->DexInstructionDebugInfo());
 
@@ -1742,24 +1711,39 @@
   size_t variable_count_offset = expandBufGetLength(pReply);
   expandBufAdd4BE(pReply, 0);
 
-  DebugCallbackContext context;
-  context.method = m;
-  context.pReply = pReply;
-  context.variable_count = 0;
-  context.with_generic = with_generic;
+  size_t variable_count = 0;
 
   if (accessor.HasCodeItem()) {
-    m->GetDexFile()->DecodeDebugLocalInfo(accessor.RegistersSize(),
-                                          accessor.InsSize(),
-                                          accessor.InsnsSizeInCodeUnits(),
-                                          accessor.DebugInfoOffset(),
-                                          m->IsStatic(),
-                                          m->GetDexMethodIndex(),
-                                          DebugCallbackContext::Callback,
-                                          &context);
+    accessor.DecodeDebugLocalInfo(m->IsStatic(),
+                                  m->GetDexMethodIndex(),
+                                  [&](const DexFile::LocalInfo& entry)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      uint16_t slot = entry.reg_;
+      VLOG(jdwp) << StringPrintf("    %2zd: %d(%d) '%s' '%s' '%s' actual slot=%d mangled slot=%d",
+                                 variable_count,
+                                 entry.start_address_,
+                                 entry.end_address_ - entry.start_address_,
+                                 entry.name_,
+                                 entry.descriptor_, entry.signature_,
+                                 slot,
+                                 MangleSlot(slot, m));
+
+      slot = MangleSlot(slot, m);
+
+      expandBufAdd8BE(pReply, entry.start_address_);
+      expandBufAddUtf8String(pReply, entry.name_);
+      expandBufAddUtf8String(pReply, entry.descriptor_);
+      if (with_generic) {
+        expandBufAddUtf8String(pReply, entry.signature_);
+      }
+      expandBufAdd4BE(pReply, entry.end_address_- entry.start_address_);
+      expandBufAdd4BE(pReply, slot);
+
+      ++variable_count;
+    });
   }
 
-  JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count);
+  JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, variable_count);
 }
 
 void Dbg::OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return_value,
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 51cfd43..b50a430 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -209,7 +209,7 @@
     case DexFile::kDexAnnotationArray:
     {
       uint32_t size = DecodeUnsignedLeb128(&annotation);
-      while (size--) {
+      for (; size != 0u; --size) {
         if (!SkipAnnotationValue(dex_file, &annotation)) {
           return false;
         }
@@ -221,7 +221,7 @@
     {
       DecodeUnsignedLeb128(&annotation);  // unused type_index
       uint32_t size = DecodeUnsignedLeb128(&annotation);
-      while (size--) {
+      for (; size != 0u; --size) {
         DecodeUnsignedLeb128(&annotation);  // unused element_name_index
         if (!SkipAnnotationValue(dex_file, &annotation)) {
           return false;
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index e7715c4..ce742fe 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1417,7 +1417,7 @@
 void ElfFileImpl<ElfTypes>::ApplyOatPatches(
     const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta,
     uint8_t* to_patch, const uint8_t* to_patch_end) {
-  typedef __attribute__((__aligned__(1))) Elf_Addr UnalignedAddress;
+  using UnalignedAddress __attribute__((__aligned__(1))) = Elf_Addr;
   while (patches < patches_end) {
     to_patch += DecodeUnsignedLeb128(&patches);
     DCHECK_LE(patches, patches_end) << "Unexpected end of patch list.";
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 9f82b04..ae9acd6d 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2611,15 +2611,15 @@
     if (ReadBarrier::kEnableToSpaceInvariantChecks) {
       AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object_);
     }
-    CHECK_EQ(byte_size, (java_lang_Object_->GetObjectSize<kVerifyNone, kWithoutReadBarrier>()));
+    CHECK_EQ(byte_size, java_lang_Object_->GetObjectSize<kVerifyNone>());
     dummy_obj->SetClass(java_lang_Object_);
     CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone>()));
   } else {
     // Use an int array.
     dummy_obj->SetClass(int_array_class);
-    CHECK((dummy_obj->IsArrayInstance<kVerifyNone, kWithoutReadBarrier>()));
+    CHECK(dummy_obj->IsArrayInstance<kVerifyNone>());
     int32_t length = (byte_size - data_offset) / component_size;
-    mirror::Array* dummy_arr = dummy_obj->AsArray<kVerifyNone, kWithoutReadBarrier>();
+    mirror::Array* dummy_arr = dummy_obj->AsArray<kVerifyNone>();
     dummy_arr->SetLength(length);
     CHECK_EQ(dummy_arr->GetLength(), length)
         << "byte_size=" << byte_size << " length=" << length
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index b6877f5..ee4a0f4 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1029,9 +1029,7 @@
       if (obj->IsClass<kVerifyNone>()) {
         mirror::Class* as_klass = obj->AsClass<kVerifyNone>();
         FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_);
-        as_klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(as_klass,
-                                                                        pointer_size_,
-                                                                        visitor);
+        as_klass->FixupNativePointers<kVerifyNone>(as_klass, pointer_size_, visitor);
         // Deal with the pointer arrays. Use the helper function since multiple classes can reference
         // the same arrays.
         mirror::PointerArray* const vtable = as_klass->GetVTable<kVerifyNone, kWithoutReadBarrier>();
@@ -1247,7 +1245,7 @@
       CHECK_EQ(image_header.GetImageBegin(), target_base);
       // Fix up dex cache DexFile pointers.
       auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)->
-          AsObjectArray<mirror::DexCache, kVerifyNone, kWithoutReadBarrier>();
+          AsObjectArray<mirror::DexCache, kVerifyNone>();
       for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
         mirror::DexCache* dex_cache = dex_caches->Get<kVerifyNone, kWithoutReadBarrier>(i);
         // Fix up dex cache pointers.
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index c200776..5af1dd3 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -35,8 +35,10 @@
 // will not try to allocate a new region from the beginning of the
 // region space, but from the last allocated region. This allocation
 // strategy reduces region reuse and should help catch some GC bugs
-// earlier.
-static constexpr bool kCyclicRegionAllocation = true;
+// earlier. However, cyclic region allocation can also create memory
+// fragmentation at the region level (see b/33795328); therefore, we
+// only enable it in debug mode.
+static constexpr bool kCyclicRegionAllocation = kIsDebugBuild;
 
 // A space that consists of equal-sized regions.
 class RegionSpace final : public ContinuousMemMapAllocSpace {
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index 5d234ea..2c31c65 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -58,8 +58,8 @@
     oss << " klass=" << klass;
     if (IsValidClass(klass)) {
       oss << "(" << klass->PrettyClass() << ")";
-      if (klass->IsArrayClass<kVerifyNone, kWithoutReadBarrier>()) {
-        oss << " length=" << obj->AsArray<kVerifyNone, kWithoutReadBarrier>()->GetLength();
+      if (klass->IsArrayClass<kVerifyNone>()) {
+        oss << " length=" << obj->AsArray<kVerifyNone>()->GetLength();
       }
     } else {
       oss << " <invalid address>";
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index e8a47d1..9467c4c 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -148,11 +148,11 @@
   hprof_basic_long = 11,
 };
 
-typedef uint32_t HprofStringId;
-typedef uint32_t HprofClassObjectId;
-typedef uint32_t HprofClassSerialNumber;
-typedef uint32_t HprofStackTraceSerialNumber;
-typedef uint32_t HprofStackFrameId;
+using HprofStringId = uint32_t;
+using HprofClassObjectId = uint32_t;
+using HprofClassSerialNumber = uint32_t;
+using HprofStackTraceSerialNumber = uint32_t;
+using HprofStackFrameId = uint32_t;
 static constexpr HprofStackTraceSerialNumber kHprofNullStackTrace = 0;
 
 class EndianOutput {
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 8ab4a9b..d205225 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -21,6 +21,7 @@
 #include "base/utils.h"
 #include "jni/java_vm_ext.h"
 #include "jni/jni_internal.h"
+#include "mirror/object-inl.h"
 #include "nth_caller_visitor.h"
 #include "reference_table.h"
 #include "runtime.h"
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 048c6e4..df66061 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -56,7 +56,7 @@
   ScopedObjectAccessUnchecked soa(self);
   if (method->IsStatic()) {
     if (shorty == "L") {
-      typedef jobject (fntype)(JNIEnv*, jclass);
+      using fntype = jobject(JNIEnv*, jclass);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
@@ -67,35 +67,35 @@
       }
       result->SetL(soa.Decode<mirror::Object>(jresult));
     } else if (shorty == "V") {
-      typedef void (fntype)(JNIEnv*, jclass);
+      using fntype = void(JNIEnv*, jclass);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       fn(soa.Env(), klass.get());
     } else if (shorty == "Z") {
-      typedef jboolean (fntype)(JNIEnv*, jclass);
+      using fntype = jboolean(JNIEnv*, jclass);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetZ(fn(soa.Env(), klass.get()));
     } else if (shorty == "BI") {
-      typedef jbyte (fntype)(JNIEnv*, jclass, jint);
+      using fntype = jbyte(JNIEnv*, jclass, jint);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetB(fn(soa.Env(), klass.get(), args[0]));
     } else if (shorty == "II") {
-      typedef jint (fntype)(JNIEnv*, jclass, jint);
+      using fntype = jint(JNIEnv*, jclass, jint);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetI(fn(soa.Env(), klass.get(), args[0]));
     } else if (shorty == "LL") {
-      typedef jobject (fntype)(JNIEnv*, jclass, jobject);
+      using fntype = jobject(JNIEnv*, jclass, jobject);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
@@ -108,14 +108,14 @@
       }
       result->SetL(soa.Decode<mirror::Object>(jresult));
     } else if (shorty == "IIZ") {
-      typedef jint (fntype)(JNIEnv*, jclass, jint, jboolean);
+      using fntype = jint(JNIEnv*, jclass, jint, jboolean);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetI(fn(soa.Env(), klass.get(), args[0], args[1]));
     } else if (shorty == "ILI") {
-      typedef jint (fntype)(JNIEnv*, jclass, jobject, jint);
+      using fntype = jint(JNIEnv*, jclass, jobject, jint);
       fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(
           method->GetEntryPointFromJni()));
       ScopedLocalRef<jclass> klass(soa.Env(),
@@ -125,7 +125,7 @@
       ScopedThreadStateChange tsc(self, kNative);
       result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1]));
     } else if (shorty == "SIZ") {
-      typedef jshort (fntype)(JNIEnv*, jclass, jint, jboolean);
+      using fntype = jshort(JNIEnv*, jclass, jint, jboolean);
       fntype* const fn =
           reinterpret_cast<fntype*>(const_cast<void*>(method->GetEntryPointFromJni()));
       ScopedLocalRef<jclass> klass(soa.Env(),
@@ -133,14 +133,14 @@
       ScopedThreadStateChange tsc(self, kNative);
       result->SetS(fn(soa.Env(), klass.get(), args[0], args[1]));
     } else if (shorty == "VIZ") {
-      typedef void (fntype)(JNIEnv*, jclass, jint, jboolean);
+      using fntype = void(JNIEnv*, jclass, jint, jboolean);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       fn(soa.Env(), klass.get(), args[0], args[1]);
     } else if (shorty == "ZLL") {
-      typedef jboolean (fntype)(JNIEnv*, jclass, jobject, jobject);
+      using fntype = jboolean(JNIEnv*, jclass, jobject, jobject);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
@@ -151,7 +151,7 @@
       ScopedThreadStateChange tsc(self, kNative);
       result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get()));
     } else if (shorty == "ZILL") {
-      typedef jboolean (fntype)(JNIEnv*, jclass, jint, jobject, jobject);
+      using fntype = jboolean(JNIEnv*, jclass, jint, jobject, jobject);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
@@ -162,7 +162,7 @@
       ScopedThreadStateChange tsc(self, kNative);
       result->SetZ(fn(soa.Env(), klass.get(), args[0], arg1.get(), arg2.get()));
     } else if (shorty == "VILII") {
-      typedef void (fntype)(JNIEnv*, jclass, jint, jobject, jint, jint);
+      using fntype = void(JNIEnv*, jclass, jint, jobject, jint, jint);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
@@ -171,7 +171,7 @@
       ScopedThreadStateChange tsc(self, kNative);
       fn(soa.Env(), klass.get(), args[0], arg1.get(), args[2], args[3]);
     } else if (shorty == "VLILII") {
-      typedef void (fntype)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint);
+      using fntype = void(JNIEnv*, jclass, jobject, jint, jobject, jint, jint);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
@@ -187,7 +187,7 @@
     }
   } else {
     if (shorty == "L") {
-      typedef jobject (fntype)(JNIEnv*, jobject);
+      using fntype = jobject(JNIEnv*, jobject);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
@@ -198,14 +198,14 @@
       }
       result->SetL(soa.Decode<mirror::Object>(jresult));
     } else if (shorty == "V") {
-      typedef void (fntype)(JNIEnv*, jobject);
+      using fntype = void(JNIEnv*, jobject);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
       ScopedThreadStateChange tsc(self, kNative);
       fn(soa.Env(), rcvr.get());
     } else if (shorty == "LL") {
-      typedef jobject (fntype)(JNIEnv*, jobject, jobject);
+      using fntype = jobject(JNIEnv*, jobject, jobject);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
@@ -219,7 +219,7 @@
       result->SetL(soa.Decode<mirror::Object>(jresult));
       ScopedThreadStateChange tsc(self, kNative);
     } else if (shorty == "III") {
-      typedef jint (fntype)(JNIEnv*, jobject, jint, jint);
+      using fntype = jint(JNIEnv*, jobject, jint, jint);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index b324b4c..a607b48 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -121,56 +121,15 @@
 bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
             const Instruction* inst, uint16_t inst_data, JValue* result);
 
-// Handles streamlined non-range invoke static, direct and virtual instructions originating in
-// mterp. Access checks and instrumentation other than jit profiling are not supported, but does
-// support interpreter intrinsics if applicable.
-// Returns true on success, otherwise throws an exception and returns false.
-template<InvokeType type>
-static inline bool DoFastInvoke(Thread* self,
-                                ShadowFrame& shadow_frame,
-                                const Instruction* inst,
-                                uint16_t inst_data,
-                                JValue* result) {
-  const uint32_t method_idx = inst->VRegB_35c();
-  const uint32_t vregC = inst->VRegC_35c();
-  ObjPtr<mirror::Object> receiver = (type == kStatic)
-      ? nullptr
-      : shadow_frame.GetVRegReference(vregC);
-  ArtMethod* sf_method = shadow_frame.GetMethod();
-  ArtMethod* const called_method = FindMethodFromCode<type, false>(
-      method_idx, &receiver, sf_method, self);
-  // The shadow frame should already be pushed, so we don't need to update it.
-  if (UNLIKELY(called_method == nullptr)) {
-    CHECK(self->IsExceptionPending());
-    result->SetJ(0);
-    return false;
-  } else if (UNLIKELY(!called_method->IsInvokable())) {
-    called_method->ThrowInvocationTimeError();
-    result->SetJ(0);
-    return false;
-  } else {
-    jit::Jit* jit = Runtime::Current()->GetJit();
-    if (jit != nullptr && type == kVirtual) {
-      jit->InvokeVirtualOrInterface(receiver, sf_method, shadow_frame.GetDexPC(), called_method);
-    }
-    if (called_method->IsIntrinsic()) {
-      if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data,
-                               shadow_frame.GetResultRegister())) {
-        return !self->IsExceptionPending();
-      }
-    }
-    return DoCall<false, false>(called_method, self, shadow_frame, inst, inst_data, result);
-  }
-}
-
 // Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
 // Returns true on success, otherwise throws an exception and returns false.
-template<InvokeType type, bool is_range, bool do_access_check>
-static inline bool DoInvoke(Thread* self,
-                            ShadowFrame& shadow_frame,
-                            const Instruction* inst,
-                            uint16_t inst_data,
-                            JValue* result) {
+template<InvokeType type, bool is_range, bool do_access_check, bool fast_invoke = false>
+static ALWAYS_INLINE bool DoInvoke(Thread* self,
+                                   ShadowFrame& shadow_frame,
+                                   const Instruction* inst,
+                                   uint16_t inst_data,
+                                   JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   // Make sure to check for async exceptions before anything else.
   if (UNLIKELY(self->ObserveAsyncException())) {
     return false;
@@ -196,12 +155,24 @@
     if (jit != nullptr && (type == kVirtual || type == kInterface)) {
       jit->InvokeVirtualOrInterface(receiver, sf_method, shadow_frame.GetDexPC(), called_method);
     }
-    // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
-    if (type == kVirtual || type == kInterface) {
-      instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-      if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
-        instrumentation->InvokeVirtualOrInterface(
-            self, receiver.Ptr(), sf_method, shadow_frame.GetDexPC(), called_method);
+    // The fast invoke is used from mterp for some invoke variants.
+    // The non-fast version is used from switch interpreter and it might not support intrinsics.
+    // TODO: Unify both paths.
+    if (fast_invoke) {
+      if (called_method->IsIntrinsic()) {
+        if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data,
+                                 shadow_frame.GetResultRegister())) {
+          return !self->IsExceptionPending();
+        }
+      }
+    } else {
+      // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
+      if (type == kVirtual || type == kInterface) {
+        instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+        if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
+          instrumentation->InvokeVirtualOrInterface(
+              self, receiver.Ptr(), sf_method, shadow_frame.GetDexPC(), called_method);
+        }
       }
     }
     return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data,
@@ -277,7 +248,8 @@
 template<bool is_range>
 static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
                                         const Instruction* inst, uint16_t inst_data,
-                                        JValue* result) {
+                                        JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
   ObjPtr<mirror::Object> const receiver = shadow_frame.GetVRegReference(vregC);
   if (UNLIKELY(receiver == nullptr)) {
@@ -601,52 +573,6 @@
                                     uint16_t this_obj_vreg,
                                     JValue result);
 
-// Explicitly instantiate all DoInvoke functions.
-#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check)                      \
-  template REQUIRES_SHARED(Locks::mutator_lock_)                                           \
-  bool DoInvoke<_type, _is_range, _do_check>(Thread* self,                                 \
-                                             ShadowFrame& shadow_frame,                    \
-                                             const Instruction* inst, uint16_t inst_data,  \
-                                             JValue* result)
-
-#define EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(_type)       \
-  EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false);  \
-  EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, true);   \
-  EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false);   \
-  EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true);
-
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kStatic)      // invoke-static/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kDirect)      // invoke-direct/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kVirtual)     // invoke-virtual/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kSuper)       // invoke-super/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface)   // invoke-interface/range.
-#undef EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL
-#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL
-
-// Explicitly instantiate all DoFastInvoke functions.
-#define EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(_type)                     \
-  template REQUIRES_SHARED(Locks::mutator_lock_)                         \
-  bool DoFastInvoke<_type>(Thread* self,                                 \
-                           ShadowFrame& shadow_frame,                    \
-                           const Instruction* inst, uint16_t inst_data,  \
-                           JValue* result)
-
-EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kStatic);     // invoke-static
-EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kDirect);     // invoke-direct
-EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kVirtual);    // invoke-virtual
-#undef EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL
-
-// Explicitly instantiate all DoInvokeVirtualQuick functions.
-#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range)                    \
-  template REQUIRES_SHARED(Locks::mutator_lock_)                                     \
-  bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame,      \
-                                       const Instruction* inst, uint16_t inst_data,  \
-                                       JValue* result)
-
-EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false);  // invoke-virtual-quick.
-EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true);   // invoke-virtual-quick-range.
-#undef EXPLICIT_INSTANTIATION_DO_INVOKE_VIRTUAL_QUICK
-
 }  // namespace interpreter
 }  // namespace art
 
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 65c1aa8..a7423c8 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -172,7 +172,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoFastInvoke<kVirtual>(
+  return DoInvoke<kVirtual, /*is_range*/ false, /*access_check*/ false, /*fast_invoke*/ true>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -183,7 +183,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoInvoke<kSuper, false, false>(
+  return DoInvoke<kSuper, /*is_range*/ false, /*access_check*/ false>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -194,7 +194,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoInvoke<kInterface, false, false>(
+  return DoInvoke<kInterface, /*is_range*/ false, /*access_check*/ false>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -205,7 +205,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoFastInvoke<kDirect>(
+  return DoInvoke<kDirect, /*is_range*/ false, /*access_check*/ false, /*fast_invoke*/ true>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -216,7 +216,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoFastInvoke<kStatic>(
+  return DoInvoke<kStatic, /*is_range*/ false, /*access_check*/ false, /*fast_invoke*/ true>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -249,7 +249,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoInvoke<kVirtual, true, false>(
+  return DoInvoke<kVirtual, /*is_range*/ true, /*access_check*/ false>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -260,7 +260,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoInvoke<kSuper, true, false>(
+  return DoInvoke<kSuper, /*is_range*/ true, /*access_check*/ false>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -271,7 +271,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoInvoke<kInterface, true, false>(
+  return DoInvoke<kInterface, /*is_range*/ true, /*access_check*/ false>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -282,7 +282,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoInvoke<kDirect, true, false>(
+  return DoInvoke<kDirect, /*is_range*/ true, /*access_check*/ false>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
@@ -293,7 +293,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue* result_register = shadow_frame->GetResultRegister();
   const Instruction* inst = Instruction::At(dex_pc_ptr);
-  return DoInvoke<kStatic, true, false>(
+  return DoInvoke<kStatic, /*is_range*/ true, /*access_check*/ false>(
       self, *shadow_frame, inst, inst_data, result_register);
 }
 
diff --git a/runtime/interpreter/safe_math.h b/runtime/interpreter/safe_math.h
index 78b3539..06f046a 100644
--- a/runtime/interpreter/safe_math.h
+++ b/runtime/interpreter/safe_math.h
@@ -41,19 +41,19 @@
   return static_cast<biggest_T>(Op<unsigned_biggest_T>()(val1, val2));
 }
 
-// Perform signed a signed add on 'a' and 'b' with defined wrapping behavior.
+// Perform a signed add on 'a' and 'b' with defined wrapping behavior.
 template<typename T1, typename T2>
 static inline typename select_bigger<T1, T2>::type SafeAdd(T1 a, T2 b) {
   return SafeMath<std::plus>(a, b);
 }
 
-// Perform signed a signed substract on 'a' and 'b' with defined wrapping behavior.
+// Perform a signed substract on 'a' and 'b' with defined wrapping behavior.
 template<typename T1, typename T2>
 static inline typename select_bigger<T1, T2>::type SafeSub(T1 a, T2 b) {
   return SafeMath<std::minus>(a, b);
 }
 
-// Perform signed a signed multiply on 'a' and 'b' with defined wrapping behavior.
+// Perform a signed multiply on 'a' and 'b' with defined wrapping behavior.
 template<typename T1, typename T2>
 static inline typename select_bigger<T1, T2>::type SafeMul(T1 a, T2 b) {
   return SafeMath<std::multiplies>(a, b);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index d4b51af..4cd3782 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1180,19 +1180,19 @@
     }
 
     case Primitive::kPrimShort: {
-      typedef int16_t unaligned_short __attribute__ ((aligned (1)));
+      using unaligned_short __attribute__((__aligned__(1))) = int16_t;
       result->SetS(*reinterpret_cast<unaligned_short*>(static_cast<intptr_t>(address)));
       return;
     }
 
     case Primitive::kPrimInt: {
-      typedef int32_t unaligned_int __attribute__ ((aligned (1)));
+      using unaligned_int __attribute__((__aligned__(1))) = int32_t;
       result->SetI(*reinterpret_cast<unaligned_int*>(static_cast<intptr_t>(address)));
       return;
     }
 
     case Primitive::kPrimLong: {
-      typedef int64_t unaligned_long __attribute__ ((aligned (1)));
+      using unaligned_long __attribute__((__aligned__(1))) = int64_t;
       result->SetJ(*reinterpret_cast<unaligned_long*>(static_cast<intptr_t>(address)));
       return;
     }
@@ -1919,11 +1919,16 @@
   result->SetI(Primitive::ComponentSize(primitive_type));
 }
 
-typedef void (*InvokeHandler)(Thread* self, ShadowFrame* shadow_frame, JValue* result,
-    size_t arg_size);
+using InvokeHandler = void(*)(Thread* self,
+                              ShadowFrame* shadow_frame,
+                              JValue* result,
+                              size_t arg_size);
 
-typedef void (*JNIHandler)(Thread* self, ArtMethod* method, mirror::Object* receiver,
-    uint32_t* args, JValue* result);
+using JNIHandler = void(*)(Thread* self,
+                           ArtMethod* method,
+                           mirror::Object* receiver,
+                           uint32_t* args,
+                           JValue* result);
 
 static bool tables_initialized_ = false;
 static std::unordered_map<std::string, InvokeHandler> invoke_handlers_;
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 200fc5b..bd2705d 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -261,7 +261,7 @@
 
     UnstartedMemoryPeekShort(self, tmp.get(), &result, 0);
 
-    typedef int16_t unaligned_short __attribute__ ((aligned (1)));
+    using unaligned_short __attribute__((__aligned__(1))) = int16_t;
     const unaligned_short* short_ptr = reinterpret_cast<const unaligned_short*>(base_ptr + i);
     EXPECT_EQ(result.GetS(), *short_ptr);
   }
@@ -284,7 +284,7 @@
 
     UnstartedMemoryPeekInt(self, tmp.get(), &result, 0);
 
-    typedef int32_t unaligned_int __attribute__ ((aligned (1)));
+    using unaligned_int __attribute__((__aligned__(1))) = int32_t;
     const unaligned_int* int_ptr = reinterpret_cast<const unaligned_int*>(base_ptr + i);
     EXPECT_EQ(result.GetI(), *int_ptr);
   }
@@ -307,7 +307,7 @@
 
     UnstartedMemoryPeekLong(self, tmp.get(), &result, 0);
 
-    typedef int64_t unaligned_long __attribute__ ((aligned (1)));
+    using unaligned_long __attribute__((__aligned__(1))) = int64_t;
     const unaligned_long* long_ptr = reinterpret_cast<const unaligned_long*>(base_ptr + i);
     EXPECT_EQ(result.GetJ(), *long_ptr);
   }
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index e6043c6..9245f1e 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -346,7 +346,7 @@
   if (!HaveFullPacket()) {
     /* read some more, looping until we have data */
     errno = 0;
-    while (1) {
+    while (true) {
       int selCount;
       fd_set readfds;
       int maxfd = -1;
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 1e61ba0..0a54e38 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1432,7 +1432,7 @@
 /*
  * Handler map decl.
  */
-typedef JdwpError (*JdwpRequestHandler)(JdwpState* state, Request* request, ExpandBuf* reply);
+using JdwpRequestHandler = JdwpError(*)(JdwpState* state, Request* request, ExpandBuf* reply);
 
 struct JdwpHandlerMap {
   uint8_t cmdSet;
diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 29fa160..b8b0e16 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -383,7 +383,7 @@
   if (!HaveFullPacket()) {
     /* read some more, looping until we have data */
     errno = 0;
-    while (1) {
+    while (true) {
       int selCount;
       fd_set readfds;
       int maxfd = -1;
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index 63fb22c..6cd719a 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -77,11 +77,11 @@
 
 namespace art {
 extern "C" {
-  typedef enum {
+  enum JITAction {
     JIT_NOACTION = 0,
     JIT_REGISTER_FN,
     JIT_UNREGISTER_FN
-  } JITAction;
+  };
 
   struct JITCodeEntry {
     // Atomic to ensure the reader can always iterate over the linked list
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index bcbdc3b..1119317 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -18,11 +18,15 @@
 
 #include <sstream>
 
+#include "android-base/unique_fd.h"
+
 #include "arch/context.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "base/histogram-inl.h"
 #include "base/logging.h"  // For VLOG.
+#include "base/membarrier.h"
+#include "base/memfd.h"
 #include "base/mem_map.h"
 #include "base/quasi_atomic.h"
 #include "base/stl_util.h"
@@ -51,16 +55,32 @@
 #include "thread-current-inl.h"
 #include "thread_list.h"
 
+using android::base::unique_fd;
+
 namespace art {
 namespace jit {
 
-static constexpr int kProtCode = PROT_READ | PROT_EXEC;
-static constexpr int kProtData = PROT_READ | PROT_WRITE;
-static constexpr int kProtProfile = PROT_READ;
-
 static constexpr size_t kCodeSizeLogThreshold = 50 * KB;
 static constexpr size_t kStackMapSizeLogThreshold = 50 * KB;
 
+static constexpr int kProtR = PROT_READ;
+static constexpr int kProtRW = PROT_READ | PROT_WRITE;
+static constexpr int kProtRWX = PROT_READ | PROT_WRITE | PROT_EXEC;
+static constexpr int kProtRX = PROT_READ | PROT_EXEC;
+
+namespace {
+
+// Translate an address belonging to one memory map into an address in a second. This is useful
+// when there are two virtual memory ranges for the same physical memory range.
+template <typename T>
+T* TranslateAddress(T* src_ptr, const MemMap& src, const MemMap& dst) {
+  CHECK(src.HasAddress(src_ptr));
+  uint8_t* const raw_src_ptr = reinterpret_cast<uint8_t*>(src_ptr);
+  return reinterpret_cast<T*>(raw_src_ptr - src.Begin() + dst.Begin());
+}
+
+}  // namespace
+
 class JitCodeCache::JniStubKey {
  public:
   explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -187,14 +207,43 @@
     return nullptr;
   }
 
-  // Decide how we should map the code and data sections.
-  // If we use the code cache just for profiling we do not need to map the code section as
-  // executable.
-  // NOTE 1: this is yet another workaround to bypass strict SElinux policies in order to be able
-  //         to profile system server.
-  // NOTE 2: We could just not create the code section at all but we will need to
-  //         special case too many cases.
-  int memmap_flags_prot_code = used_only_for_profile_data ? kProtProfile : kProtCode;
+  // Register for membarrier expedited sync core if JIT will be generating code.
+  if (!used_only_for_profile_data) {
+    if (art::membarrier(art::MembarrierCommand::kRegisterPrivateExpeditedSyncCore) != 0) {
+      // MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE ensures that CPU instruction pipelines are
+      // flushed and it's used when adding code to the JIT. The memory used by the new code may
+      // have just been released and, in theory, the old code could still be in a pipeline.
+      VLOG(jit) << "Kernel does not support membarrier sync-core";
+    }
+  }
+
+  // File descriptor enabling dual-view mapping of code section.
+  unique_fd mem_fd;
+
+  // Bionic supports memfd_create, but the call may fail on older kernels.
+  mem_fd = unique_fd(art::memfd_create("/jit-cache", /* flags */ 0));
+  if (mem_fd.get() < 0) {
+    VLOG(jit) << "Failed to initialize dual view JIT. memfd_create() error: "
+              << strerror(errno);
+  }
+
+  if (mem_fd.get() >= 0 && ftruncate(mem_fd, max_capacity) != 0) {
+    std::ostringstream oss;
+    oss << "Failed to initialize memory file: " << strerror(errno);
+    *error_msg = oss.str();
+    return nullptr;
+  }
+
+  // Data cache will be half of the initial allocation.
+  // Code cache will be the other half of the initial allocation.
+  // TODO: Make this variable?
+
+  // Align both capacities to page size, as that's the unit mspaces use.
+  initial_capacity = RoundDown(initial_capacity, 2 * kPageSize);
+  max_capacity = RoundDown(max_capacity, 2 * kPageSize);
+  const size_t data_capacity = max_capacity / 2;
+  const size_t exec_capacity = used_only_for_profile_data ? 0 : max_capacity - data_capacity;
+  DCHECK_LE(data_capacity + exec_capacity, max_capacity);
 
   std::string error_str;
   // Map name specific for android_os_Debug.cpp accounting.
@@ -202,71 +251,149 @@
   // We could do PC-relative addressing to avoid this problem, but that
   // would require reserving code and data area before submitting, which
   // means more windows for the code memory to be RWX.
-  MemMap data_map = MemMap::MapAnonymous(
-      "data-code-cache",
-      /* addr */ nullptr,
-      max_capacity,
-      kProtData,
-      /* low_4gb */ true,
-      /* reuse */ false,
-      /* reservation */ nullptr,
-      &error_str);
-  if (!data_map.IsValid()) {
+  int base_flags;
+  MemMap data_pages;
+  if (mem_fd.get() >= 0) {
+    // Dual view of JIT code cache case. Create an initial mapping of data pages large enough
+    // for data and non-writable view of JIT code pages. We use the memory file descriptor to
+    // enable dual mapping - we'll create a second mapping using the descriptor below. The
+    // mappings will look like:
+    //
+    //       VA                  PA
+    //
+    //       +---------------+
+    //       | non exec code |\
+    //       +---------------+ \
+    //       :               :\ \
+    //       +---------------+.\.+---------------+
+    //       |  exec code    |  \|     code      |
+    //       +---------------+...+---------------+
+    //       |      data     |   |     data      |
+    //       +---------------+...+---------------+
+    //
+    // In this configuration code updates are written to the non-executable view of the code
+    // cache, and the executable view of the code cache has fixed RX memory protections.
+    //
+    // This memory needs to be mapped shared as the code portions will have two mappings.
+    base_flags = MAP_SHARED;
+    data_pages = MemMap::MapFile(
+        data_capacity + exec_capacity,
+        kProtRW,
+        base_flags,
+        mem_fd,
+        /* start */ 0,
+        /* low_4gb */ true,
+        "data-code-cache",
+        &error_str);
+  } else {
+    // Single view of JIT code cache case. Create an initial mapping of data pages large enough
+    // for data and JIT code pages. The mappings will look like:
+    //
+    //       VA                  PA
+    //
+    //       +---------------+...+---------------+
+    //       |  exec code    |   |     code      |
+    //       +---------------+...+---------------+
+    //       |      data     |   |     data      |
+    //       +---------------+...+---------------+
+    //
+    // In this configuration code updates are written to the executable view of the code cache,
+    // and the executable view of the code cache transitions RX to RWX for the update and then
+    // back to RX after the update.
+    base_flags = MAP_PRIVATE | MAP_ANON;
+    data_pages = MemMap::MapAnonymous(
+        "data-code-cache",
+        /* addr */ nullptr,
+        data_capacity + exec_capacity,
+        kProtRW,
+        /* low_4gb */ true,
+        /* reuse */ false,
+        /* reservation */ nullptr,
+        &error_str);
+  }
+
+  if (!data_pages.IsValid()) {
     std::ostringstream oss;
     oss << "Failed to create read write cache: " << error_str << " size=" << max_capacity;
     *error_msg = oss.str();
     return nullptr;
   }
 
-  // Align both capacities to page size, as that's the unit mspaces use.
-  initial_capacity = RoundDown(initial_capacity, 2 * kPageSize);
-  max_capacity = RoundDown(max_capacity, 2 * kPageSize);
+  MemMap exec_pages;
+  MemMap non_exec_pages;
+  if (exec_capacity > 0) {
+    uint8_t* const divider = data_pages.Begin() + data_capacity;
+    // Set initial permission for executable view to catch any SELinux permission problems early
+    // (for processes that cannot map WX pages). Otherwise, this region does not need to be
+    // executable as there is no code in the cache yet.
+    exec_pages = data_pages.RemapAtEnd(divider,
+                                       "jit-code-cache",
+                                       kProtRX,
+                                       base_flags | MAP_FIXED,
+                                       mem_fd.get(),
+                                       (mem_fd.get() >= 0) ? data_capacity : 0,
+                                       &error_str);
+    if (!exec_pages.IsValid()) {
+      std::ostringstream oss;
+      oss << "Failed to create read execute code cache: " << error_str << " size=" << max_capacity;
+      *error_msg = oss.str();
+      return nullptr;
+    }
 
-  // Data cache is 1 / 2 of the map.
-  // TODO: Make this variable?
-  size_t data_size = max_capacity / 2;
-  size_t code_size = max_capacity - data_size;
-  DCHECK_EQ(code_size + data_size, max_capacity);
-  uint8_t* divider = data_map.Begin() + data_size;
-
-  MemMap code_map = data_map.RemapAtEnd(
-      divider, "jit-code-cache", memmap_flags_prot_code | PROT_WRITE, &error_str);
-  if (!code_map.IsValid()) {
-    std::ostringstream oss;
-    oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
-    *error_msg = oss.str();
-    return nullptr;
+    if (mem_fd.get() >= 0) {
+      // For dual view, create the secondary view of code memory used for updating code. This view
+      // is never executable.
+      non_exec_pages = MemMap::MapFile(exec_capacity,
+                                       kProtR,
+                                       base_flags,
+                                       mem_fd,
+                                       /* start */ data_capacity,
+                                       /* low_4GB */ false,
+                                       "jit-code-cache-rw",
+                                       &error_str);
+      if (!exec_pages.IsValid()) {
+        std::ostringstream oss;
+        oss << "Failed to create read write code cache: " << error_str << " size=" << max_capacity;
+        *error_msg = oss.str();
+        return nullptr;
+      }
+    }
+  } else {
+    // Profiling only. No memory for code required.
+    DCHECK(used_only_for_profile_data);
   }
-  DCHECK_EQ(code_map.Begin(), divider);
-  data_size = initial_capacity / 2;
-  code_size = initial_capacity - data_size;
-  DCHECK_EQ(code_size + data_size, initial_capacity);
+
+  const size_t initial_data_capacity = initial_capacity / 2;
+  const size_t initial_exec_capacity =
+      (exec_capacity == 0) ? 0 : (initial_capacity - initial_data_capacity);
+
   return new JitCodeCache(
-      std::move(code_map),
-      std::move(data_map),
-      code_size,
-      data_size,
+      std::move(data_pages),
+      std::move(exec_pages),
+      std::move(non_exec_pages),
+      initial_data_capacity,
+      initial_exec_capacity,
       max_capacity,
-      garbage_collect_code,
-      memmap_flags_prot_code);
+      garbage_collect_code);
 }
 
-JitCodeCache::JitCodeCache(MemMap&& code_map,
-                           MemMap&& data_map,
-                           size_t initial_code_capacity,
+JitCodeCache::JitCodeCache(MemMap&& data_pages,
+                           MemMap&& exec_pages,
+                           MemMap&& non_exec_pages,
                            size_t initial_data_capacity,
+                           size_t initial_exec_capacity,
                            size_t max_capacity,
-                           bool garbage_collect_code,
-                           int memmap_flags_prot_code)
+                           bool garbage_collect_code)
     : lock_("Jit code cache", kJitCodeCacheLock),
       lock_cond_("Jit code cache condition variable", lock_),
       collection_in_progress_(false),
-      code_map_(std::move(code_map)),
-      data_map_(std::move(data_map)),
+      data_pages_(std::move(data_pages)),
+      exec_pages_(std::move(exec_pages)),
+      non_exec_pages_(std::move(non_exec_pages)),
       max_capacity_(max_capacity),
-      current_capacity_(initial_code_capacity + initial_data_capacity),
-      code_end_(initial_code_capacity),
+      current_capacity_(initial_exec_capacity + initial_data_capacity),
       data_end_(initial_data_capacity),
+      exec_end_(initial_exec_capacity),
       last_collection_increased_code_cache_(false),
       garbage_collect_code_(garbage_collect_code),
       used_memory_for_data_(0),
@@ -278,40 +405,46 @@
       histogram_code_memory_use_("Memory used for compiled code", 16),
       histogram_profiling_info_memory_use_("Memory used for profiling info", 16),
       is_weak_access_enabled_(true),
-      inline_cache_cond_("Jit inline cache condition variable", lock_),
-      memmap_flags_prot_code_(memmap_flags_prot_code) {
+      inline_cache_cond_("Jit inline cache condition variable", lock_) {
 
-  DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
-  code_mspace_ = create_mspace_with_base(code_map_.Begin(), code_end_, false /*locked*/);
-  data_mspace_ = create_mspace_with_base(data_map_.Begin(), data_end_, false /*locked*/);
+  DCHECK_GE(max_capacity, initial_exec_capacity + initial_data_capacity);
 
-  if (code_mspace_ == nullptr || data_mspace_ == nullptr) {
-    PLOG(FATAL) << "create_mspace_with_base failed";
+  // Initialize the data heap
+  data_mspace_ = create_mspace_with_base(data_pages_.Begin(), data_end_, false /*locked*/);
+  CHECK(data_mspace_ != nullptr) << "create_mspace_with_base (data) failed";
+
+  // Initialize the code heap
+  MemMap* code_heap = nullptr;
+  if (non_exec_pages_.IsValid()) {
+    code_heap = &non_exec_pages_;
+  } else if (exec_pages_.IsValid()) {
+    code_heap = &exec_pages_;
   }
-
-  SetFootprintLimit(current_capacity_);
-
-  CheckedCall(mprotect,
-              "mprotect jit code cache",
-              code_map_.Begin(),
-              code_map_.Size(),
-              memmap_flags_prot_code_);
-  CheckedCall(mprotect,
-              "mprotect jit data cache",
-              data_map_.Begin(),
-              data_map_.Size(),
-              kProtData);
+  if (code_heap != nullptr) {
+    // Make all pages reserved for the code heap writable. The mspace allocator, that manages the
+    // heap, will take and initialize pages in create_mspace_with_base().
+    CheckedCall(mprotect, "create code heap", code_heap->Begin(), code_heap->Size(), kProtRW);
+    exec_mspace_ = create_mspace_with_base(code_heap->Begin(), exec_end_, false /*locked*/);
+    CHECK(exec_mspace_ != nullptr) << "create_mspace_with_base (exec) failed";
+    SetFootprintLimit(current_capacity_);
+    // Protect pages containing heap metadata. Updates to the code heap toggle write permission to
+    // perform the update and there are no other times write access is required.
+    CheckedCall(mprotect, "protect code heap", code_heap->Begin(), code_heap->Size(), kProtR);
+  } else {
+    exec_mspace_ = nullptr;
+    SetFootprintLimit(current_capacity_);
+  }
 
   VLOG(jit) << "Created jit code cache: initial data size="
             << PrettySize(initial_data_capacity)
             << ", initial code size="
-            << PrettySize(initial_code_capacity);
+            << PrettySize(initial_exec_capacity);
 }
 
 JitCodeCache::~JitCodeCache() {}
 
 bool JitCodeCache::ContainsPc(const void* ptr) const {
-  return code_map_.Begin() <= ptr && ptr < code_map_.End();
+  return exec_pages_.Begin() <= ptr && ptr < exec_pages_.End();
 }
 
 bool JitCodeCache::WillExecuteJitCode(ArtMethod* method) {
@@ -379,22 +512,20 @@
       : ScopedTrace("ScopedCodeCacheWrite"),
         code_cache_(code_cache) {
     ScopedTrace trace("mprotect all");
-    CheckedCall(
-        mprotect,
-        "make code writable",
-        code_cache_->code_map_.Begin(),
-        code_cache_->code_map_.Size(),
-        code_cache_->memmap_flags_prot_code_ | PROT_WRITE);
+    const MemMap* const updatable_pages = code_cache_->GetUpdatableCodeMapping();
+    if (updatable_pages != nullptr) {
+      int prot = code_cache_->HasDualCodeMapping() ? kProtRW : kProtRWX;
+      CheckedCall(mprotect, "Cache +W", updatable_pages->Begin(), updatable_pages->Size(), prot);
+    }
   }
 
   ~ScopedCodeCacheWrite() {
     ScopedTrace trace("mprotect code");
-    CheckedCall(
-        mprotect,
-        "make code protected",
-        code_cache_->code_map_.Begin(),
-        code_cache_->code_map_.Size(),
-        code_cache_->memmap_flags_prot_code_);
+    const MemMap* const updatable_pages = code_cache_->GetUpdatableCodeMapping();
+    if (updatable_pages != nullptr) {
+      int prot = code_cache_->HasDualCodeMapping() ? kProtR : kProtRX;
+      CheckedCall(mprotect, "Cache -W", updatable_pages->Begin(), updatable_pages->Size(), prot);
+    }
   }
 
  private:
@@ -411,7 +542,7 @@
                                   size_t code_size,
                                   size_t data_size,
                                   bool osr,
-                                  Handle<mirror::ObjectArray<mirror::Object>> roots,
+                                  const std::vector<Handle<mirror::Object>>& roots,
                                   bool has_should_deoptimize_flag,
                                   const ArenaSet<ArtMethod*>& cha_single_implementation_list) {
   uint8_t* result = CommitCodeInternal(self,
@@ -477,18 +608,16 @@
   return stack_map_data - ComputeRootTableSize(GetNumberOfRoots(stack_map_data));
 }
 
-static void DCheckRootsAreValid(Handle<mirror::ObjectArray<mirror::Object>> roots)
+static void DCheckRootsAreValid(const std::vector<Handle<mirror::Object>>& roots)
     REQUIRES(!Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
   if (!kIsDebugBuild) {
     return;
   }
-  const uint32_t length = roots->GetLength();
   // Put all roots in `roots_data`.
-  for (uint32_t i = 0; i < length; ++i) {
-    ObjPtr<mirror::Object> object = roots->Get(i);
+  for (Handle<mirror::Object> object : roots) {
     // Ensure the string is strongly interned. b/32995596
     if (object->IsString()) {
-      ObjPtr<mirror::String> str = ObjPtr<mirror::String>::DownCast(object);
+      ObjPtr<mirror::String> str = object->AsString();
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr);
     }
@@ -496,12 +625,12 @@
 }
 
 void JitCodeCache::FillRootTable(uint8_t* roots_data,
-                                 Handle<mirror::ObjectArray<mirror::Object>> roots) {
+                                 const std::vector<Handle<mirror::Object>>& roots) {
   GcRoot<mirror::Object>* gc_roots = reinterpret_cast<GcRoot<mirror::Object>*>(roots_data);
-  const uint32_t length = roots->GetLength();
+  const uint32_t length = roots.size();
   // Put all roots in `roots_data`.
   for (uint32_t i = 0; i < length; ++i) {
-    ObjPtr<mirror::Object> object = roots->Get(i);
+    ObjPtr<mirror::Object> object = roots[i].Get();
     gc_roots[i] = GcRoot<mirror::Object>(object);
   }
 }
@@ -598,7 +727,13 @@
   if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) {
     FreeData(GetRootTable(code_ptr));
   }  // else this is a JNI stub without any data.
-  FreeCode(reinterpret_cast<uint8_t*>(allocation));
+
+  uint8_t* code_allocation = reinterpret_cast<uint8_t*>(allocation);
+  if (HasDualCodeMapping()) {
+    code_allocation = TranslateAddress(code_allocation, exec_pages_, non_exec_pages_);
+  }
+
+  FreeCode(code_allocation);
 }
 
 void JitCodeCache::FreeAllMethodHeaders(
@@ -749,6 +884,16 @@
   }
 }
 
+const MemMap* JitCodeCache::GetUpdatableCodeMapping() const {
+  if (HasDualCodeMapping()) {
+    return &non_exec_pages_;
+  } else if (HasCodeMapping()) {
+    return &exec_pages_;
+  } else {
+    return nullptr;
+  }
+}
+
 uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
                                           ArtMethod* method,
                                           uint8_t* stack_map,
@@ -757,7 +902,7 @@
                                           size_t code_size,
                                           size_t data_size,
                                           bool osr,
-                                          Handle<mirror::ObjectArray<mirror::Object>> roots,
+                                          const std::vector<Handle<mirror::Object>>& roots,
                                           bool has_should_deoptimize_flag,
                                           const ArenaSet<ArtMethod*>&
                                               cha_single_implementation_list) {
@@ -769,31 +914,52 @@
     DCheckRootsAreValid(roots);
   }
 
-  size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
-  // Ensure the header ends up at expected instruction alignment.
-  size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
-  size_t total_size = header_size + code_size;
-
   OatQuickMethodHeader* method_header = nullptr;
   uint8_t* code_ptr = nullptr;
-  uint8_t* memory = nullptr;
+
   MutexLock mu(self, lock_);
   // We need to make sure that there will be no jit-gcs going on and wait for any ongoing one to
   // finish.
   WaitForPotentialCollectionToCompleteRunnable(self);
   {
     ScopedCodeCacheWrite scc(this);
-    memory = AllocateCode(total_size);
-    if (memory == nullptr) {
+
+    size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+    // Ensure the header ends up at expected instruction alignment.
+    size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
+    size_t total_size = header_size + code_size;
+
+    // AllocateCode allocates memory in non-executable region for alignment header and code. The
+    // header size may include alignment padding.
+    uint8_t* nox_memory = AllocateCode(total_size);
+    if (nox_memory == nullptr) {
       return nullptr;
     }
-    code_ptr = memory + header_size;
 
+    // code_ptr points to non-executable code.
+    code_ptr = nox_memory + header_size;
     std::copy(code, code + code_size, code_ptr);
     method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+
+    // From here code_ptr points to executable code.
+    if (non_exec_pages_.IsValid()) {
+      code_ptr = TranslateAddress(code_ptr, non_exec_pages_, exec_pages_);
+    }
+
     new (method_header) OatQuickMethodHeader(
         (stack_map != nullptr) ? code_ptr - stack_map : 0u,
         code_size);
+
+    DCHECK(!Runtime::Current()->IsAotCompiler());
+    if (has_should_deoptimize_flag) {
+      method_header->SetHasShouldDeoptimizeFlag();
+    }
+
+    // Update method_header pointer to executable code region.
+    if (non_exec_pages_.IsValid()) {
+      method_header = TranslateAddress(method_header, non_exec_pages_, exec_pages_);
+    }
+
     // Flush caches before we remove write permission because some ARMv8 Qualcomm kernels may
     // trigger a segfault if a page fault occurs when requesting a cache maintenance operation.
     // This is a kernel bug that we need to work around until affected devices (e.g. Nexus 5X and
@@ -809,17 +975,14 @@
     // shootdown (incidentally invalidating the CPU pipelines by sending an IPI to all cores to
     // notify them of the TLB invalidation). Some architectures, notably ARM and ARM64, have
     // hardware support that broadcasts TLB invalidations and so their kernels have no software
-    // based TLB shootdown. FlushInstructionPipeline() is a wrapper around the Linux
-    // membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED) syscall which does the appropriate flushing.
-    FlushInstructionPipeline();
-
-    DCHECK(!Runtime::Current()->IsAotCompiler());
-    if (has_should_deoptimize_flag) {
-      method_header->SetHasShouldDeoptimizeFlag();
-    }
+    // based TLB shootdown. The sync-core flavor of membarrier was introduced in Linux 4.16 to
+    // address this (see mbarrier(2)). The membarrier here will fail on prior kernels and on
+    // platforms lacking the appropriate support.
+    art::membarrier(art::MembarrierCommand::kPrivateExpeditedSyncCore);
 
     number_of_compilations_++;
   }
+
   // We need to update the entry point in the runnable state for the instrumentation.
   {
     // The following needs to be guarded by cha_lock_ also. Otherwise it's possible that the
@@ -1164,9 +1327,9 @@
   DCHECK(IsAlignedParam(per_space_footprint, kPageSize));
   DCHECK_EQ(per_space_footprint * 2, new_footprint);
   mspace_set_footprint_limit(data_mspace_, per_space_footprint);
-  {
+  if (HasCodeMapping()) {
     ScopedCodeCacheWrite scc(this);
-    mspace_set_footprint_limit(code_mspace_, per_space_footprint);
+    mspace_set_footprint_limit(exec_mspace_, per_space_footprint);
   }
 }
 
@@ -1241,8 +1404,8 @@
       number_of_collections_++;
       live_bitmap_.reset(CodeCacheBitmap::Create(
           "code-cache-bitmap",
-          reinterpret_cast<uintptr_t>(code_map_.Begin()),
-          reinterpret_cast<uintptr_t>(code_map_.Begin() + current_capacity_ / 2)));
+          reinterpret_cast<uintptr_t>(exec_pages_.Begin()),
+          reinterpret_cast<uintptr_t>(exec_pages_.Begin() + current_capacity_ / 2)));
       collection_in_progress_ = true;
     }
   }
@@ -1611,15 +1774,17 @@
 // NO_THREAD_SAFETY_ANALYSIS as this is called from mspace code, at which point the lock
 // is already held.
 void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_SAFETY_ANALYSIS {
-  if (code_mspace_ == mspace) {
-    size_t result = code_end_;
-    code_end_ += increment;
-    return reinterpret_cast<void*>(result + code_map_.Begin());
+  if (mspace == exec_mspace_) {
+    DCHECK(exec_mspace_ != nullptr);
+    const MemMap* const code_pages = GetUpdatableCodeMapping();
+    void* result = code_pages->Begin() + exec_end_;
+    exec_end_ += increment;
+    return result;
   } else {
     DCHECK_EQ(data_mspace_, mspace);
-    size_t result = data_end_;
+    void* result = data_pages_.Begin() + data_end_;
     data_end_ += increment;
-    return reinterpret_cast<void*>(result + data_map_.Begin());
+    return result;
   }
 }
 
@@ -1846,7 +2011,7 @@
 uint8_t* JitCodeCache::AllocateCode(size_t code_size) {
   size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
   uint8_t* result = reinterpret_cast<uint8_t*>(
-      mspace_memalign(code_mspace_, alignment, code_size));
+      mspace_memalign(exec_mspace_, alignment, code_size));
   size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
   // Ensure the header ends up at expected instruction alignment.
   DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(result + header_size), alignment);
@@ -1856,7 +2021,7 @@
 
 void JitCodeCache::FreeCode(uint8_t* code) {
   used_memory_for_code_ -= mspace_usable_size(code);
-  mspace_free(code_mspace_, code);
+  mspace_free(exec_mspace_, code);
 }
 
 uint8_t* JitCodeCache::AllocateData(size_t data_size) {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index a4a0f8f..76ad8db 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -141,7 +141,7 @@
                       size_t code_size,
                       size_t data_size,
                       bool osr,
-                      Handle<mirror::ObjectArray<mirror::Object>> roots,
+                      const std::vector<Handle<mirror::Object>>& roots,
                       bool has_should_deoptimize_flag,
                       const ArenaSet<ArtMethod*>& cha_single_implementation_list)
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -223,7 +223,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool OwnsSpace(const void* mspace) const NO_THREAD_SAFETY_ANALYSIS {
-    return mspace == code_mspace_ || mspace == data_mspace_;
+    return mspace == data_mspace_ || mspace == exec_mspace_;
   }
 
   void* MoreCore(const void* mspace, intptr_t increment);
@@ -279,13 +279,13 @@
 
  private:
   // Take ownership of maps.
-  JitCodeCache(MemMap&& code_map,
-               MemMap&& data_map,
-               size_t initial_code_capacity,
+  JitCodeCache(MemMap&& data_pages,
+               MemMap&& exec_pages,
+               MemMap&& non_exec_pages,
                size_t initial_data_capacity,
+               size_t initial_exec_capacity,
                size_t max_capacity,
-               bool garbage_collect_code,
-               int memmap_flags_prot_code);
+               bool garbage_collect_code);
 
   // Internal version of 'CommitCode' that will not retry if the
   // allocation fails. Return null if the allocation fails.
@@ -297,14 +297,14 @@
                               size_t code_size,
                               size_t data_size,
                               bool osr,
-                              Handle<mirror::ObjectArray<mirror::Object>> roots,
+                              const std::vector<Handle<mirror::Object>>& roots,
                               bool has_should_deoptimize_flag,
                               const ArenaSet<ArtMethod*>& cha_single_implementation_list)
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Adds the given roots to the roots_data. Only a member for annotalysis.
-  void FillRootTable(uint8_t* roots_data, Handle<mirror::ObjectArray<mirror::Object>> roots)
+  void FillRootTable(uint8_t* roots_data, const std::vector<Handle<mirror::Object>>& roots)
       REQUIRES(lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -381,6 +381,16 @@
   uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
   void FreeData(uint8_t* data) REQUIRES(lock_);
 
+  bool HasDualCodeMapping() const {
+    return non_exec_pages_.IsValid();
+  }
+
+  bool HasCodeMapping() const {
+    return exec_pages_.IsValid();
+  }
+
+  const MemMap* GetUpdatableCodeMapping() const;
+
   bool IsWeakAccessEnabled(Thread* self) const;
   void WaitUntilInlineCacheAccessible(Thread* self)
       REQUIRES(!lock_)
@@ -395,14 +405,17 @@
   ConditionVariable lock_cond_ GUARDED_BY(lock_);
   // Whether there is a code cache collection in progress.
   bool collection_in_progress_ GUARDED_BY(lock_);
-  // Mem map which holds code.
-  MemMap code_map_;
   // Mem map which holds data (stack maps and profiling info).
-  MemMap data_map_;
-  // The opaque mspace for allocating code.
-  void* code_mspace_ GUARDED_BY(lock_);
+  MemMap data_pages_;
+  // Mem map which holds code and has executable permission.
+  MemMap exec_pages_;
+  // Mem map which holds code with non executable permission. Only valid for dual view JIT when
+  // this is the non-executable view of code used to write updates.
+  MemMap non_exec_pages_;
   // The opaque mspace for allocating data.
   void* data_mspace_ GUARDED_BY(lock_);
+  // The opaque mspace for allocating code.
+  void* exec_mspace_ GUARDED_BY(lock_);
   // Bitmap for collecting code and data.
   std::unique_ptr<CodeCacheBitmap> live_bitmap_;
   // Holds compiled code associated with the shorty for a JNI stub.
@@ -420,12 +433,12 @@
   // The current capacity in bytes of the code cache.
   size_t current_capacity_ GUARDED_BY(lock_);
 
-  // The current footprint in bytes of the code portion of the code cache.
-  size_t code_end_ GUARDED_BY(lock_);
-
   // The current footprint in bytes of the data portion of the code cache.
   size_t data_end_ GUARDED_BY(lock_);
 
+  // The current footprint in bytes of the code portion of the code cache.
+  size_t exec_end_ GUARDED_BY(lock_);
+
   // Whether the last collection round increased the code cache.
   bool last_collection_increased_code_cache_ GUARDED_BY(lock_);
 
@@ -464,9 +477,6 @@
   // Condition to wait on for accessing inline caches.
   ConditionVariable inline_cache_cond_ GUARDED_BY(lock_);
 
-  // Mapping flags for the code section.
-  const int memmap_flags_prot_code_;
-
   friend class art::JitJniStubTestHelper;
   friend class ScopedCodeCacheWrite;
 
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index 42406cf..6769368 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -333,7 +333,7 @@
     }
     ScopedThreadSuspension sts(self, kNative);
     // Do this without holding the jni libraries lock to prevent possible deadlocks.
-    typedef void (*JNI_OnUnloadFn)(JavaVM*, void*);
+    using JNI_OnUnloadFn = void(*)(JavaVM*, void*);
     for (auto library : unload_libraries) {
       void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
       if (sym == nullptr) {
@@ -1026,7 +1026,7 @@
     self->SetClassLoaderOverride(class_loader);
 
     VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
-    typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
+    using JNI_OnLoadFn = int(*)(JavaVM*, void*);
     JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
     int version = (*jni_on_load)(this, nullptr);
 
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 8bdd561..a31a9144 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -189,8 +189,9 @@
   T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kVerifyNone>
   void** ElementAddress(size_t index, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK_LT(index, static_cast<size_t>(GetLength()));
+    DCHECK_LT(index, static_cast<size_t>(GetLength<kVerifyFlags>()));
     return reinterpret_cast<void**>(reinterpret_cast<uint8_t*>(this) +
                                     Array::DataOffset(static_cast<size_t>(ptr_size)).Uint32Value() +
                                     static_cast<size_t>(ptr_size) * index);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index d3f8921..df70fab 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -42,17 +42,17 @@
 namespace art {
 namespace mirror {
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline uint32_t Class::GetObjectSize() {
   // Note: Extra parentheses to avoid the comma being interpreted as macro parameter separator.
-  DCHECK((!IsVariableSize<kVerifyFlags, kReadBarrierOption>())) << "class=" << PrettyTypeOf();
+  DCHECK((!IsVariableSize<kVerifyFlags>())) << "class=" << PrettyTypeOf();
   return GetField32(ObjectSizeOffset());
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline uint32_t Class::GetObjectSizeAllocFastPath() {
   // Note: Extra parentheses to avoid the comma being interpreted as macro parameter separator.
-  DCHECK((!IsVariableSize<kVerifyFlags, kReadBarrierOption>())) << "class=" << PrettyTypeOf();
+  DCHECK((!IsVariableSize<kVerifyFlags>())) << "class=" << PrettyTypeOf();
   return GetField32(ObjectSizeAllocFastPathOffset());
 }
 
@@ -251,18 +251,14 @@
                                           uint32_t num_direct,
                                           uint32_t num_virtual) {
   DCHECK_LE(num_direct + num_virtual, (new_methods == nullptr) ? 0 : new_methods->size());
-  SetMethodsPtrInternal(new_methods);
+  SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, methods_),
+                    static_cast<uint64_t>(reinterpret_cast<uintptr_t>(new_methods)));
   SetFieldShort<false>(OFFSET_OF_OBJECT_MEMBER(Class, copied_methods_offset_),
                     dchecked_integral_cast<uint16_t>(num_direct + num_virtual));
   SetFieldShort<false>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_offset_),
                        dchecked_integral_cast<uint16_t>(num_direct));
 }
 
-inline void Class::SetMethodsPtrInternal(LengthPrefixedArray<ArtMethod>* new_methods) {
-  SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, methods_),
-                    static_cast<uint64_t>(reinterpret_cast<uintptr_t>(new_methods)));
-}
-
 template<VerifyObjectFlags kVerifyFlags>
 inline ArtMethod* Class::GetVirtualMethod(size_t i, PointerSize pointer_size) {
   CheckPointerSize(pointer_size);
@@ -304,7 +300,7 @@
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline int32_t Class::GetVTableLength() {
-  if (ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>()) {
+  if (ShouldHaveEmbeddedVTable<kVerifyFlags>()) {
     return GetEmbeddedVTableLength();
   }
   return GetVTable<kVerifyFlags, kReadBarrierOption>() != nullptr ?
@@ -313,7 +309,7 @@
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline ArtMethod* Class::GetVTableEntry(uint32_t i, PointerSize pointer_size) {
-  if (ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>()) {
+  if (ShouldHaveEmbeddedVTable<kVerifyFlags>()) {
     return GetEmbeddedVTableEntry(i, pointer_size);
   }
   auto* vtable = GetVTable<kVerifyFlags, kReadBarrierOption>();
@@ -322,8 +318,9 @@
       i, pointer_size);
 }
 
+template<VerifyObjectFlags kVerifyFlags>
 inline int32_t Class::GetEmbeddedVTableLength() {
-  return GetField32(MemberOffset(EmbeddedVTableLengthOffset()));
+  return GetField32<kVerifyFlags>(MemberOffset(EmbeddedVTableLengthOffset()));
 }
 
 inline void Class::SetEmbeddedVTableLength(int32_t len) {
@@ -374,13 +371,13 @@
   return false;
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Class::IsVariableSize() {
   // Classes, arrays, and strings vary in size, and so the object_size_ field cannot
   // be used to Get their instance size
   return IsClassClass<kVerifyFlags>() ||
-         IsArrayClass<kVerifyFlags, kReadBarrierOption>() ||
-         IsStringClass();
+         IsArrayClass<kVerifyFlags>() ||
+         IsStringClass<kVerifyFlags>();
 }
 
 inline void Class::SetObjectSize(uint32_t new_object_size) {
@@ -647,19 +644,18 @@
 inline MemberOffset Class::GetFirstReferenceInstanceFieldOffset() {
   ObjPtr<Class> super_class = GetSuperClass<kVerifyFlags, kReadBarrierOption>();
   return (super_class != nullptr)
-      ? MemberOffset(RoundUp(super_class->GetObjectSize<kVerifyFlags, kReadBarrierOption>(),
-                             kHeapReferenceSize))
+      ? MemberOffset(RoundUp(super_class->GetObjectSize<kVerifyFlags>(), kHeapReferenceSize))
       : ClassOffset();
 }
 
-template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template <VerifyObjectFlags kVerifyFlags>
 inline MemberOffset Class::GetFirstReferenceStaticFieldOffset(PointerSize pointer_size) {
-  DCHECK(IsResolved());
+  DCHECK(IsResolved<kVerifyFlags>());
   uint32_t base = sizeof(Class);  // Static fields come after the class.
-  if (ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>()) {
+  if (ShouldHaveEmbeddedVTable<kVerifyFlags>()) {
     // Static fields come after the embedded tables.
     base = Class::ComputeClassSize(
-        true, GetEmbeddedVTableLength(), 0, 0, 0, 0, 0, pointer_size);
+        true, GetEmbeddedVTableLength<kVerifyFlags>(), 0, 0, 0, 0, 0, pointer_size);
   }
   return MemberOffset(base);
 }
@@ -857,8 +853,7 @@
 inline bool Class::IsClassClass() {
   // OK to look at from-space copies since java.lang.Class.class is not movable.
   // See b/114413743
-  ObjPtr<Class> java_lang_Class = GetClass<kVerifyFlags, kWithoutReadBarrier>()->
-      template GetClass<kVerifyFlags, kWithoutReadBarrier>();
+  ObjPtr<Class> java_lang_Class = GetClass<kVerifyFlags, kWithoutReadBarrier>();
   return this == java_lang_Class;
 }
 
@@ -1005,7 +1000,6 @@
 }
 
 inline MemberOffset Class::EmbeddedVTableOffset(PointerSize pointer_size) {
-  CheckPointerSize(pointer_size);
   return MemberOffset(ImtPtrOffset(pointer_size).Uint32Value() + static_cast<size_t>(pointer_size));
 }
 
@@ -1018,15 +1012,18 @@
   return GetFieldObject<Class, kVerifyFlags, kReadBarrierOption>(ComponentTypeOffset());
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Class::IsArrayClass() {
-  return GetComponentType<kVerifyFlags, kReadBarrierOption>() != nullptr;
+  // We do not need a read barrier for comparing with null.
+  return GetComponentType<kVerifyFlags, kWithoutReadBarrier>() != nullptr;
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Class::IsObjectArrayClass() {
-  ObjPtr<Class> const component_type = GetComponentType<kVerifyFlags, kReadBarrierOption>();
-  return component_type != nullptr && !component_type->IsPrimitive();
+  // We do not need a read barrier here as the primitive type is constant,
+  // both from-space and to-space component type classes shall yield the same result.
+  ObjPtr<Class> const component_type = GetComponentType<kVerifyFlags, kWithoutReadBarrier>();
+  return component_type != nullptr && !component_type->IsPrimitive<kVerifyFlags>();
 }
 
 inline bool Class::IsAssignableFrom(ObjPtr<Class> src) {
@@ -1068,49 +1065,42 @@
   return arr != nullptr ? arr->size() : 0u;
 }
 
-template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, typename Visitor>
+template <typename T, VerifyObjectFlags kVerifyFlags, typename Visitor>
+inline void Class::FixupNativePointer(
+    Class* dest, PointerSize pointer_size, const Visitor& visitor, MemberOffset member_offset) {
+  void** address =
+      reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(dest) + member_offset.Uint32Value());
+  T old_value = GetFieldPtrWithSize<T, kVerifyFlags>(member_offset, pointer_size);
+  T new_value = visitor(old_value, address);
+  if (old_value != new_value) {
+    dest->SetFieldPtrWithSize</* kTransactionActive */ false,
+                              /* kCheckTransaction */ true,
+                              kVerifyNone>(member_offset, new_value, pointer_size);
+  }
+}
+
+template <VerifyObjectFlags kVerifyFlags, typename Visitor>
 inline void Class::FixupNativePointers(Class* dest,
                                        PointerSize pointer_size,
                                        const Visitor& visitor) {
-  auto dest_address_fn = [dest](MemberOffset offset) {
-    return reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(dest) + offset.Uint32Value());
-  };
   // Update the field arrays.
-  LengthPrefixedArray<ArtField>* const sfields = GetSFieldsPtr();
-  void** sfields_dest_address = dest_address_fn(OFFSET_OF_OBJECT_MEMBER(Class, sfields_));
-  LengthPrefixedArray<ArtField>* const new_sfields = visitor(sfields, sfields_dest_address);
-  if (sfields != new_sfields) {
-    dest->SetSFieldsPtrUnchecked(new_sfields);
-  }
-  LengthPrefixedArray<ArtField>* const ifields = GetIFieldsPtr();
-  void** ifields_dest_address = dest_address_fn(OFFSET_OF_OBJECT_MEMBER(Class, ifields_));
-  LengthPrefixedArray<ArtField>* const new_ifields = visitor(ifields, ifields_dest_address);
-  if (ifields != new_ifields) {
-    dest->SetIFieldsPtrUnchecked(new_ifields);
-  }
+  FixupNativePointer<LengthPrefixedArray<ArtField>*, kVerifyFlags>(
+      dest, pointer_size, visitor, OFFSET_OF_OBJECT_MEMBER(Class, sfields_));
+  FixupNativePointer<LengthPrefixedArray<ArtField>*, kVerifyFlags>(
+      dest, pointer_size, visitor, OFFSET_OF_OBJECT_MEMBER(Class, ifields_));
   // Update method array.
-  LengthPrefixedArray<ArtMethod>* methods = GetMethodsPtr();
-  void** methods_dest_address = dest_address_fn(OFFSET_OF_OBJECT_MEMBER(Class, methods_));
-  LengthPrefixedArray<ArtMethod>* new_methods = visitor(methods, methods_dest_address);
-  if (methods != new_methods) {
-    dest->SetMethodsPtrInternal(new_methods);
-  }
+  FixupNativePointer<LengthPrefixedArray<ArtMethod>*, kVerifyFlags>(
+      dest, pointer_size, visitor, OFFSET_OF_OBJECT_MEMBER(Class, methods_));
   // Fix up embedded tables.
-  if (!IsTemp() && ShouldHaveEmbeddedVTable<kVerifyNone, kReadBarrierOption>()) {
-    for (int32_t i = 0, count = GetEmbeddedVTableLength(); i < count; ++i) {
-      ArtMethod* method = GetEmbeddedVTableEntry(i, pointer_size);
-      void** method_dest_addr = dest_address_fn(EmbeddedVTableEntryOffset(i, pointer_size));
-      ArtMethod* new_method = visitor(method, method_dest_addr);
-      if (method != new_method) {
-        dest->SetEmbeddedVTableEntryUnchecked(i, new_method, pointer_size);
-      }
+  if (!IsTemp<kVerifyNone>() && ShouldHaveEmbeddedVTable<kVerifyNone>()) {
+    for (int32_t i = 0, count = GetEmbeddedVTableLength<kVerifyFlags>(); i < count; ++i) {
+      FixupNativePointer<ArtMethod*, kVerifyFlags>(
+          dest, pointer_size, visitor, EmbeddedVTableEntryOffset(i, pointer_size));
     }
   }
-  if (!IsTemp() && ShouldHaveImt<kVerifyNone, kReadBarrierOption>()) {
-    ImTable* imt = GetImt(pointer_size);
-    void** imt_dest_addr = dest_address_fn(ImtPtrOffset(pointer_size));
-    ImTable* new_imt = visitor(imt, imt_dest_addr);
-    dest->SetImt(new_imt, pointer_size);
+  if (!IsTemp<kVerifyNone>() && ShouldHaveImt<kVerifyNone>()) {
+    FixupNativePointer<ImTable*, kVerifyFlags>(
+        dest, pointer_size, visitor, ImtPtrOffset(pointer_size));
   }
 }
 
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 4015bd2..f640d3b 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -191,8 +191,9 @@
   }
 
   // Returns true if the class is an interface.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool IsInterface() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccInterface) != 0;
+    return (GetAccessFlags<kVerifyFlags>() & kAccInterface) != 0;
   }
 
   // Returns true if the class is declared public.
@@ -235,24 +236,27 @@
     SetAccessFlags(flags | kAccClassIsFinalizable);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool IsStringClass() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return (GetClassFlags() & kClassFlagString) != 0;
+    return (GetClassFlags<kVerifyFlags>() & kClassFlagString) != 0;
   }
 
   ALWAYS_INLINE void SetStringClass() REQUIRES_SHARED(Locks::mutator_lock_) {
     SetClassFlags(kClassFlagString | kClassFlagNoReferenceFields);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool IsClassLoaderClass() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetClassFlags() == kClassFlagClassLoader;
+    return GetClassFlags<kVerifyFlags>() == kClassFlagClassLoader;
   }
 
   ALWAYS_INLINE void SetClassLoaderClass() REQUIRES_SHARED(Locks::mutator_lock_) {
     SetClassFlags(kClassFlagClassLoader);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool IsDexCacheClass() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return (GetClassFlags() & kClassFlagDexCache) != 0;
+    return (GetClassFlags<kVerifyFlags>() & kClassFlagDexCache) != 0;
   }
 
   ALWAYS_INLINE void SetDexCacheClass() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -260,8 +264,9 @@
   }
 
   // Returns true if the class is abstract.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool IsAbstract() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccAbstract) != 0;
+    return (GetAccessFlags<kVerifyFlags>() & kAccAbstract) != 0;
   }
 
   // Returns true if the class is an annotation.
@@ -324,11 +329,12 @@
 
   // Returns true if this class is the placeholder and should retire and
   // be replaced with a class with the right size for embedded imt/vtable.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsTemp() REQUIRES_SHARED(Locks::mutator_lock_) {
-    ClassStatus s = GetStatus();
+    ClassStatus s = GetStatus<kVerifyFlags>();
     return s < ClassStatus::kResolving &&
            s != ClassStatus::kErrorResolved &&
-           ShouldHaveEmbeddedVTable();
+           ShouldHaveEmbeddedVTable<kVerifyFlags>();
   }
 
   String* GetName() REQUIRES_SHARED(Locks::mutator_lock_);  // Returns the cached name.
@@ -426,8 +432,7 @@
   // Depth of class from java.lang.Object
   uint32_t Depth() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsArrayClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -468,15 +473,15 @@
     return !IsPrimitive() && !IsInterface() && !IsAbstract() && !IsArrayClass();
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsInstantiable() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return (!IsPrimitive() && !IsInterface() && !IsAbstract()) ||
-        (IsAbstract() && IsArrayClass<kVerifyFlags, kReadBarrierOption>());
+    return (!IsPrimitive<kVerifyFlags>() &&
+            !IsInterface<kVerifyFlags>() &&
+            !IsAbstract<kVerifyFlags>()) ||
+        (IsAbstract<kVerifyFlags>() && IsArrayClass<kVerifyFlags>());
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool IsObjectArrayClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -503,8 +508,7 @@
   ObjPtr<Object> AllocNonMovableObject(Thread* self)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool IsVariableSize() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -543,8 +547,7 @@
     return ComputeClassSize(false, 0, 0, 0, 0, 0, 0, pointer_size);
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t GetObjectSize() REQUIRES_SHARED(Locks::mutator_lock_);
   static MemberOffset ObjectSizeOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Class, object_size_);
@@ -557,8 +560,7 @@
 
   void SetObjectSizeAllocFastPath(uint32_t new_object_size) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t GetObjectSizeAllocFastPath() REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetObjectSizeWithoutChecks(uint32_t new_object_size)
@@ -796,16 +798,14 @@
                 static_cast<size_t>(pointer_size)));
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool ShouldHaveImt() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>();
+    return ShouldHaveEmbeddedVTable<kVerifyFlags>();
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool ShouldHaveEmbeddedVTable() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return IsInstantiable<kVerifyFlags, kReadBarrierOption>();
+    return IsInstantiable<kVerifyFlags>();
   }
 
   bool HasVTable() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -821,6 +821,7 @@
   ArtMethod* GetVTableEntry(uint32_t i, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   int32_t GetEmbeddedVTableLength() REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetEmbeddedVTableLength(int32_t len) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -976,9 +977,10 @@
 
   // Returns the number of instance fields containing reference types. Does not count fields in any
   // super classes.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t NumReferenceInstanceFields() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(IsResolved());
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_));
+    DCHECK(IsResolved<kVerifyFlags>());
+    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_));
   }
 
   uint32_t NumReferenceInstanceFieldsDuringLinking() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1004,9 +1006,10 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns the number of static fields containing reference types.
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   uint32_t NumReferenceStaticFields() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(IsResolved());
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_));
+    DCHECK(IsResolved<kVerifyFlags>());
+    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_));
   }
 
   uint32_t NumReferenceStaticFieldsDuringLinking() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1020,8 +1023,7 @@
   }
 
   // Get the offset of the first reference static field. Other reference static fields follow.
-  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   MemberOffset GetFirstReferenceStaticFieldOffset(PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -1261,14 +1263,14 @@
   // the corresponding entry in dest if visitor(obj) != obj to prevent dirty memory. Dest should be
   // initialized to a copy of *this to prevent issues. Does not visit the ArtMethod and ArtField
   // roots.
-  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-            ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
-            typename Visitor>
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename Visitor>
   void FixupNativePointers(Class* dest, PointerSize pointer_size, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
-  ALWAYS_INLINE void SetMethodsPtrInternal(LengthPrefixedArray<ArtMethod>* new_methods)
+  template <typename T, VerifyObjectFlags kVerifyFlags, typename Visitor>
+  void FixupNativePointer(
+      Class* dest, PointerSize pointer_size, const Visitor& visitor, MemberOffset member_offset)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   ALWAYS_INLINE static ArraySlice<ArtMethod> GetMethodsSliceRangeUnchecked(
diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h
index 39c8ee0..64b4e74 100644
--- a/runtime/mirror/class_loader-inl.h
+++ b/runtime/mirror/class_loader-inl.h
@@ -33,7 +33,7 @@
   VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
   if (kVisitClasses) {
     // Visit classes loaded after.
-    ClassTable* const class_table = GetClassTable();
+    ClassTable* const class_table = GetClassTable<kVerifyFlags>();
     if (class_table != nullptr) {
       class_table->VisitRoots(visitor);
     }
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index f25f18f..e3cb12f 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -44,9 +44,10 @@
     return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, parent_));
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ClassTable* GetClassTable() REQUIRES_SHARED(Locks::mutator_lock_) {
     return reinterpret_cast<ClassTable*>(
-        GetField64(OFFSET_OF_OBJECT_MEMBER(ClassLoader, class_table_)));
+        GetField64<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, class_table_)));
   }
 
   void SetClassTable(ClassTable* class_table) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index bbe15ac..6efb747 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -310,16 +310,17 @@
   // Visit arrays after.
   if (kVisitNativeRoots) {
     VisitDexCachePairs<String, kReadBarrierOption, Visitor>(
-        GetStrings(), NumStrings(), visitor);
+        GetStrings<kVerifyFlags>(), NumStrings<kVerifyFlags>(), visitor);
 
     VisitDexCachePairs<Class, kReadBarrierOption, Visitor>(
-        GetResolvedTypes(), NumResolvedTypes(), visitor);
+        GetResolvedTypes<kVerifyFlags>(), NumResolvedTypes<kVerifyFlags>(), visitor);
 
     VisitDexCachePairs<MethodType, kReadBarrierOption, Visitor>(
-        GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor);
+        GetResolvedMethodTypes<kVerifyFlags>(), NumResolvedMethodTypes<kVerifyFlags>(), visitor);
 
-    GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites();
-    for (size_t i = 0, num_call_sites = NumResolvedCallSites(); i != num_call_sites; ++i) {
+    GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites<kVerifyFlags>();
+    size_t num_call_sites = NumResolvedCallSites<kVerifyFlags>();
+    for (size_t i = 0; i != num_call_sites; ++i) {
       visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
     }
   }
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 8401b66..22ccd20 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -326,16 +326,18 @@
   ObjPtr<CallSite> SetResolvedCallSite(uint32_t call_site_idx, ObjPtr<CallSite> resolved)
       REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
+    return GetFieldPtr64<StringDexCacheType*, kVerifyFlags>(StringsOffset());
   }
 
   void SetStrings(StringDexCacheType* strings) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     SetFieldPtr<false>(StringsOffset(), strings);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldPtr<TypeDexCacheType*>(ResolvedTypesOffset());
+    return GetFieldPtr<TypeDexCacheType*, kVerifyFlags>(ResolvedTypesOffset());
   }
 
   void SetResolvedTypes(TypeDexCacheType* resolved_types)
@@ -364,9 +366,10 @@
     SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   MethodTypeDexCacheType* GetResolvedMethodTypes()
       ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldPtr64<MethodTypeDexCacheType*>(ResolvedMethodTypesOffset());
+    return GetFieldPtr64<MethodTypeDexCacheType*, kVerifyFlags>(ResolvedMethodTypesOffset());
   }
 
   void SetResolvedMethodTypes(MethodTypeDexCacheType* resolved_method_types)
@@ -375,10 +378,11 @@
     SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   GcRoot<CallSite>* GetResolvedCallSites()
       ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldPtr<GcRoot<CallSite>*>(ResolvedCallSitesOffset());
+    return GetFieldPtr<GcRoot<CallSite>*, kVerifyFlags>(ResolvedCallSitesOffset());
   }
 
   void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
@@ -387,28 +391,34 @@
     SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetField32(NumStringsOffset());
+    return GetField32<kVerifyFlags>(NumStringsOffset());
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t NumResolvedTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetField32(NumResolvedTypesOffset());
+    return GetField32<kVerifyFlags>(NumResolvedTypesOffset());
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t NumResolvedMethods() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetField32(NumResolvedMethodsOffset());
+    return GetField32<kVerifyFlags>(NumResolvedMethodsOffset());
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t NumResolvedFields() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetField32(NumResolvedFieldsOffset());
+    return GetField32<kVerifyFlags>(NumResolvedFieldsOffset());
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t NumResolvedMethodTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetField32(NumResolvedMethodTypesOffset());
+    return GetField32<kVerifyFlags>(NumResolvedMethodTypesOffset());
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetField32(NumResolvedCallSitesOffset());
+    return GetField32<kVerifyFlags>(NumResolvedCallSitesOffset());
   }
 
   const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h
index 9e3c9af..3d4c5a7 100644
--- a/runtime/mirror/iftable.h
+++ b/runtime/mirror/iftable.h
@@ -39,9 +39,15 @@
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  PointerArray* GetMethodArrayOrNull(int32_t i) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return down_cast<PointerArray*>(
+        Get<kVerifyFlags, kReadBarrierOption>((i * kMax) + kMethodArray));
+  }
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   PointerArray* GetMethodArray(int32_t i) REQUIRES_SHARED(Locks::mutator_lock_) {
-    auto* method_array = down_cast<PointerArray*>(Get<kVerifyFlags, kReadBarrierOption>(
-        (i * kMax) + kMethodArray));
+    PointerArray* method_array = GetMethodArrayOrNull<kVerifyFlags, kReadBarrierOption>(i);
     DCHECK(method_array != nullptr);
     return method_array;
   }
@@ -49,9 +55,8 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   size_t GetMethodArrayCount(int32_t i) REQUIRES_SHARED(Locks::mutator_lock_) {
-    auto* method_array = down_cast<PointerArray*>(
-        Get<kVerifyFlags, kReadBarrierOption>((i * kMax) + kMethodArray));
-    return method_array == nullptr ? 0u : method_array->GetLength();
+    PointerArray* method_array = GetMethodArrayOrNull<kVerifyFlags, kReadBarrierOption>(i);
+    return method_array == nullptr ? 0u : method_array->GetLength<kVerifyFlags>();
   }
 
   void SetMethodArray(int32_t i, ObjPtr<PointerArray> arr) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 1b03956..99a0d92 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -142,7 +142,7 @@
   // OK to look at from-space copies since java.lang.Class.class is not movable.
   // See b/114413743
   ObjPtr<Class> klass = GetClass<kVerifyFlags, kWithoutReadBarrier>();
-  ObjPtr<Class> java_lang_Class = klass->template GetClass<kVerifyFlags, kWithoutReadBarrier>();
+  ObjPtr<Class> java_lang_Class = klass->GetClass<kVerifyFlags, kWithoutReadBarrier>();
   return klass == java_lang_Class;
 }
 
@@ -152,24 +152,27 @@
   return down_cast<Class*>(this);
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsObjectArray() {
+  // We do not need a read barrier here as the primitive type is constant,
+  // both from-space and to-space component type classes shall yield the same result.
   constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  return IsArrayInstance<kVerifyFlags, kReadBarrierOption>() &&
-      !GetClass<kNewFlags, kReadBarrierOption>()->
-          template GetComponentType<kNewFlags, kReadBarrierOption>()->IsPrimitive();
+  return IsArrayInstance<kVerifyFlags>() &&
+      !GetClass<kNewFlags, kWithoutReadBarrier>()->
+          template GetComponentType<kNewFlags, kWithoutReadBarrier>()->IsPrimitive();
 }
 
-template<class T, VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<class T, VerifyObjectFlags kVerifyFlags>
 inline ObjectArray<T>* Object::AsObjectArray() {
-  DCHECK((IsObjectArray<kVerifyFlags, kReadBarrierOption>()));
+  DCHECK((IsObjectArray<kVerifyFlags>()));
   return down_cast<ObjectArray<T>*>(this);
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsArrayInstance() {
-  return GetClass<kVerifyFlags, kReadBarrierOption>()->
-      template IsArrayClass<kVerifyFlags, kReadBarrierOption>();
+  // We do not need a read barrier here, both from-space and to-space version of the class
+  // shall return the same result from IsArrayClass().
+  return GetClass<kVerifyFlags, kWithoutReadBarrier>()->template IsArrayClass<kVerifyFlags>();
 }
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
@@ -183,9 +186,9 @@
   return down_cast<Reference*>(this);
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline Array* Object::AsArray() {
-  DCHECK((IsArrayInstance<kVerifyFlags, kReadBarrierOption>()));
+  DCHECK((IsArrayInstance<kVerifyFlags>()));
   return down_cast<Array*>(this);
 }
 
@@ -349,14 +352,14 @@
   static constexpr ReadBarrierOption kRBO = kWithoutReadBarrier;
   size_t result;
   constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
-  if (IsArrayInstance<kVerifyFlags, kRBO>()) {
-    result = AsArray<kNewFlags, kRBO>()->template SizeOf<kNewFlags, kRBO>();
+  if (IsArrayInstance<kVerifyFlags>()) {
+    result = AsArray<kNewFlags>()->template SizeOf<kNewFlags, kRBO>();
   } else if (IsClass<kNewFlags>()) {
     result = AsClass<kNewFlags>()->template SizeOf<kNewFlags, kRBO>();
   } else if (GetClass<kNewFlags, kRBO>()->IsStringClass()) {
     result = AsString<kNewFlags, kRBO>()->template SizeOf<kNewFlags>();
   } else {
-    result = GetClass<kNewFlags, kRBO>()->template GetObjectSize<kNewFlags, kRBO>();
+    result = GetClass<kNewFlags, kRBO>()->template GetObjectSize<kNewFlags>();
   }
   DCHECK_GE(result, sizeof(Object)) << " class=" << Class::PrettyClass(GetClass<kNewFlags, kRBO>());
   return result;
@@ -880,7 +883,7 @@
       // Presumably GC can happen when we are cross compiling, it should not cause performance
       // problems to do pointer size logic.
       MemberOffset field_offset = kIsStatic
-          ? klass->GetFirstReferenceStaticFieldOffset<kVerifyFlags, kReadBarrierOption>(
+          ? klass->GetFirstReferenceStaticFieldOffset<kVerifyFlags>(
               Runtime::Current()->GetClassLinker()->GetImagePointerSize())
           : klass->GetFirstReferenceInstanceFieldOffset<kVerifyFlags, kReadBarrierOption>();
       for (size_t i = 0u; i < num_reference_fields; ++i) {
@@ -903,13 +906,13 @@
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, typename Visitor>
 inline void Object::VisitStaticFieldsReferences(ObjPtr<Class> klass, const Visitor& visitor) {
-  DCHECK(!klass->IsTemp());
+  DCHECK(!klass->IsTemp<kVerifyFlags>());
   klass->VisitFieldsReferences<true, kVerifyFlags, kReadBarrierOption>(0, visitor);
 }
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline bool Object::IsClassLoader() {
-  return GetClass<kVerifyFlags, kReadBarrierOption>()->IsClassLoaderClass();
+  return GetClass<kVerifyFlags, kReadBarrierOption>()->template IsClassLoaderClass<kVerifyFlags>();
 }
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
@@ -920,7 +923,7 @@
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline bool Object::IsDexCache() {
-  return GetClass<kVerifyFlags, kReadBarrierOption>()->IsDexCacheClass();
+  return GetClass<kVerifyFlags, kReadBarrierOption>()->template IsDexCacheClass<kVerifyFlags>();
 }
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
diff --git a/runtime/mirror/object-refvisitor-inl.h b/runtime/mirror/object-refvisitor-inl.h
index bd23971..748f03b 100644
--- a/runtime/mirror/object-refvisitor-inl.h
+++ b/runtime/mirror/object-refvisitor-inl.h
@@ -37,23 +37,23 @@
   visitor(this, ClassOffset(), false);
   const uint32_t class_flags = klass->GetClassFlags<kVerifyNone>();
   if (LIKELY(class_flags == kClassFlagNormal)) {
-    DCHECK((!klass->IsVariableSize<kVerifyFlags, kReadBarrierOption>()));
+    DCHECK((!klass->IsVariableSize<kVerifyFlags>()));
     VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
     DCHECK((!klass->IsClassClass<kVerifyFlags>()));
-    DCHECK(!klass->IsStringClass());
-    DCHECK(!klass->IsClassLoaderClass());
-    DCHECK((!klass->IsArrayClass<kVerifyFlags, kReadBarrierOption>()));
+    DCHECK(!klass->IsStringClass<kVerifyFlags>());
+    DCHECK(!klass->IsClassLoaderClass<kVerifyFlags>());
+    DCHECK((!klass->IsArrayClass<kVerifyFlags>()));
   } else {
     if ((class_flags & kClassFlagNoReferenceFields) == 0) {
-      DCHECK(!klass->IsStringClass());
+      DCHECK(!klass->IsStringClass<kVerifyFlags>());
       if (class_flags == kClassFlagClass) {
         DCHECK((klass->IsClassClass<kVerifyFlags>()));
         ObjPtr<Class> as_klass = AsClass<kVerifyNone>();
         as_klass->VisitReferences<kVisitNativeRoots, kVerifyFlags, kReadBarrierOption>(klass,
                                                                                        visitor);
       } else if (class_flags == kClassFlagObjectArray) {
-        DCHECK((klass->IsObjectArrayClass<kVerifyFlags, kReadBarrierOption>()));
-        AsObjectArray<mirror::Object, kVerifyNone, kReadBarrierOption>()->VisitReferences(visitor);
+        DCHECK((klass->IsObjectArrayClass<kVerifyFlags>()));
+        AsObjectArray<mirror::Object, kVerifyNone>()->VisitReferences(visitor);
       } else if ((class_flags & kClassFlagReference) != 0) {
         VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
         ref_visitor(klass, AsReference<kVerifyFlags, kReadBarrierOption>());
@@ -70,14 +70,15 @@
       }
     } else if (kIsDebugBuild) {
       CHECK((!klass->IsClassClass<kVerifyFlags>()));
-      CHECK((!klass->IsObjectArrayClass<kVerifyFlags, kReadBarrierOption>()));
+      CHECK((!klass->IsObjectArrayClass<kVerifyFlags>()));
       // String still has instance fields for reflection purposes but these don't exist in
       // actual string instances.
-      if (!klass->IsStringClass()) {
+      if (!klass->IsStringClass<kVerifyFlags>()) {
         size_t total_reference_instance_fields = 0;
         ObjPtr<Class> super_class = klass;
         do {
-          total_reference_instance_fields += super_class->NumReferenceInstanceFields();
+          total_reference_instance_fields +=
+              super_class->NumReferenceInstanceFields<kVerifyFlags>();
           super_class = super_class->GetSuperClass<kVerifyFlags, kReadBarrierOption>();
         } while (super_class != nullptr);
         // The only reference field should be the object's class. This field is handled at the
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 48ce5c1..8fe9923 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -174,12 +174,9 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Class* AsClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsObjectArray() REQUIRES_SHARED(Locks::mutator_lock_);
-  template<class T,
-           VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ObjectArray<T>* AsObjectArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -196,11 +193,9 @@
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   DexCache* AsDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsArrayInstance() REQUIRES_SHARED(Locks::mutator_lock_);
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Array* AsArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 1d2f47f..7d101bf 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -67,7 +67,7 @@
 
 template<class T> template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline T* ObjectArray<T>::Get(int32_t i) {
-  if (!CheckIsValidIndex(i)) {
+  if (!CheckIsValidIndex<kVerifyFlags>(i)) {
     DCHECK(Thread::Current()->IsExceptionPending());
     return nullptr;
   }
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 0b615a6..cf6543f 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -204,7 +204,7 @@
 template<typename ArrayT>
 void TestPrimitiveArray(ClassLinker* cl) {
   ScopedObjectAccess soa(Thread::Current());
-  typedef typename ArrayT::ElementType T;
+  using T = typename ArrayT::ElementType;
 
   StackHandleScope<2> hs(soa.Self());
   Handle<ArrayT> a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2));
@@ -252,9 +252,9 @@
 }
 
 TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) {
-  typedef DoubleArray ArrayT;
+  using ArrayT = DoubleArray;
   ScopedObjectAccess soa(Thread::Current());
-  typedef typename ArrayT::ElementType T;
+  using T = typename ArrayT::ElementType;
 
   StackHandleScope<2> hs(soa.Self());
   Handle<ArrayT> a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2));
@@ -283,9 +283,9 @@
 }
 
 TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) {
-  typedef FloatArray ArrayT;
+  using ArrayT = FloatArray;
   ScopedObjectAccess soa(Thread::Current());
-  typedef typename ArrayT::ElementType T;
+  using T = typename ArrayT::ElementType;
 
   StackHandleScope<2> hs(soa.Self());
   Handle<ArrayT> a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2));
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index 864e1ea..ba99a07 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -691,7 +691,7 @@
 template <typename T>
 class FieldAccessViaAccessor {
  public:
-  typedef Object::Accessor<T> Accessor;
+  using Accessor = Object::Accessor<T>;
 
   // Apply an Accessor to get a field in an object.
   static void Get(ObjPtr<Object> obj,
@@ -1033,7 +1033,7 @@
                                                                CASMode::kStrong,
                                                                std::memory_order_seq_cst);
       }
-      StoreResult(cas_result, result);
+      StoreResult(static_cast<uint8_t>(cas_result), result);
       break;
     }
     case VarHandle::AccessMode::kWeakCompareAndSet:
@@ -1058,7 +1058,7 @@
             CASMode::kWeak,
             std::memory_order_seq_cst);
       }
-      StoreResult(cas_result, result);
+      StoreResult(static_cast<uint8_t>(cas_result), result);
       break;
     }
     case VarHandle::AccessMode::kCompareAndExchange:
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 0e61940..b18a048 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -325,7 +325,7 @@
   Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env));
 }
 
-typedef std::map<std::string, ObjPtr<mirror::String>> StringTable;
+using StringTable = std::map<std::string, ObjPtr<mirror::String>>;
 
 class PreloadDexCachesStringsVisitor : public SingleRootVisitor {
  public:
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 0579b6e..5b96509 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -410,7 +410,7 @@
     return false;
   }
   static_assert(std::is_trivial<T>::value, "T must be a trivial type");
-  typedef __attribute__((__aligned__(1))) T unaligned_type;
+  using unaligned_type __attribute__((__aligned__(1))) = T;
   *value = *reinterpret_cast<const unaligned_type*>(*oat);
   *oat += sizeof(T);
   return true;
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 1f0b265..92d2d44 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -151,7 +151,7 @@
 }
 
 std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles(
-    std::vector<gc::space::ImageSpace*> spaces) {
+    const std::vector<gc::space::ImageSpace*>& spaces) {
   std::vector<const OatFile*> oat_files;
   for (gc::space::ImageSpace* space : spaces) {
     oat_files.push_back(RegisterOatFile(space->ReleaseOatFile()));
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 4132b25..7d96a7a 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -73,7 +73,8 @@
 
   // Returns the oat files for the images, registers the oat files.
   // Takes ownership of the imagespace's underlying oat files.
-  std::vector<const OatFile*> RegisterImageOatFiles(std::vector<gc::space::ImageSpace*> spaces)
+  std::vector<const OatFile*> RegisterImageOatFiles(
+      const std::vector<gc::space::ImageSpace*>& spaces)
       REQUIRES(!Locks::oat_file_manager_lock_);
 
   // Finds or creates the oat file holding dex_location. Then loads and returns
diff --git a/runtime/scoped_thread_state_change.cc b/runtime/scoped_thread_state_change.cc
index edbce05..ae833b4 100644
--- a/runtime/scoped_thread_state_change.cc
+++ b/runtime/scoped_thread_state_change.cc
@@ -20,6 +20,7 @@
 
 #include "base/casts.h"
 #include "jni/java_vm_ext.h"
+#include "mirror/object-inl.h"
 #include "obj_ptr-inl.h"
 #include "runtime-inl.h"
 
diff --git a/runtime/subtype_check_info_test.cc b/runtime/subtype_check_info_test.cc
index 5323093..9bd135e 100644
--- a/runtime/subtype_check_info_test.cc
+++ b/runtime/subtype_check_info_test.cc
@@ -131,7 +131,7 @@
 
   // Create an SubtypeCheckInfo with the same depth, but with everything else reset.
   // Returns: SubtypeCheckInfo in the Uninitialized state.
-  static SubtypeCheckInfo CopyCleared(SubtypeCheckInfo sc) {
+  static SubtypeCheckInfo CopyCleared(const SubtypeCheckInfo& sc) {
     SubtypeCheckInfo cleared_copy{};
     cleared_copy.depth_ = sc.depth_;
     DCHECK_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState());
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 8a8f537..ae7a1a7 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3147,8 +3147,10 @@
 }
 
 void Thread::ThrowOutOfMemoryError(const char* msg) {
-  LOG(WARNING) << StringPrintf("Throwing OutOfMemoryError \"%s\"%s",
-      msg, (tls32_.throwing_OutOfMemoryError ? " (recursive case)" : ""));
+  LOG(WARNING) << "Throwing OutOfMemoryError "
+               << '"' << msg << '"'
+               << " (VmSize " << GetProcessStatus("VmSize")
+               << (tls32_.throwing_OutOfMemoryError ? ", recursive case)" : ")");
   if (!tls32_.throwing_OutOfMemoryError) {
     tls32_.throwing_OutOfMemoryError = true;
     ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 7e48bae..0e8d318 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -1124,7 +1124,7 @@
 
 void Trace::DumpThreadList(std::ostream& os) {
   Thread* self = Thread::Current();
-  for (auto it : exited_threads_) {
+  for (const auto& it : exited_threads_) {
     os << it.first << "\t" << it.second << "\n";
   }
   Locks::thread_list_lock_->AssertNotHeld(self);
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index c9766bc..1e5b2bb 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -320,7 +320,7 @@
 
 void Transaction::VisitObjectLogs(RootVisitor* visitor) {
   // List of moving roots.
-  typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair;
+  using ObjectPair = std::pair<mirror::Object*, mirror::Object*>;
   std::list<ObjectPair> moving_roots;
 
   // Visit roots.
@@ -348,7 +348,7 @@
 
 void Transaction::VisitArrayLogs(RootVisitor* visitor) {
   // List of moving roots.
-  typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
+  using ArrayPair = std::pair<mirror::Array*, mirror::Array*>;
   std::list<ArrayPair> moving_roots;
 
   for (auto& it : array_logs_) {
diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc
index 53e1e40..bb99787 100644
--- a/sigchainlib/sigchain_test.cc
+++ b/sigchainlib/sigchain_test.cc
@@ -38,7 +38,7 @@
 #include "sigchain.h"
 
 #if !defined(__BIONIC__)
-typedef sigset_t sigset64_t;
+using sigset64_t = sigset_t;
 
 static int sigemptyset64(sigset64_t* set) {
   return sigemptyset(set);
diff --git a/simulator/code_simulator_container.cc b/simulator/code_simulator_container.cc
index 3206bc7..dc553df 100644
--- a/simulator/code_simulator_container.cc
+++ b/simulator/code_simulator_container.cc
@@ -34,13 +34,13 @@
   if (libart_simulator_handle_ == nullptr) {
     VLOG(simulator) << "Could not load " << libart_simulator_so_name << ": " << dlerror();
   } else {
-    typedef CodeSimulator* (*create_code_simulator_ptr_)(InstructionSet target_isa);
-    create_code_simulator_ptr_ create_code_simulator_ =
-        reinterpret_cast<create_code_simulator_ptr_>(
+    using CreateCodeSimulatorPtr = CodeSimulator*(*)(InstructionSet);
+    CreateCodeSimulatorPtr create_code_simulator =
+        reinterpret_cast<CreateCodeSimulatorPtr>(
             dlsym(libart_simulator_handle_, "CreateCodeSimulator"));
-    DCHECK(create_code_simulator_ != nullptr) << "Fail to find symbol of CreateCodeSimulator: "
+    DCHECK(create_code_simulator != nullptr) << "Fail to find symbol of CreateCodeSimulator: "
         << dlerror();
-    simulator_ = create_code_simulator_(target_isa);
+    simulator_ = create_code_simulator(target_isa);
   }
 }
 
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index 33a8f5b..540e6ce 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -62,7 +62,7 @@
   int attach_result = jvm->AttachCurrentThread(&env, &args);
   CHECK_EQ(attach_result, 0);
 
-  typedef void (*Fn)(JNIEnv*);
+  using Fn = void(*)(JNIEnv*);
   Fn fn = reinterpret_cast<Fn>(arg);
   fn(env);
 
@@ -704,7 +704,7 @@
   }
 
  private:
-  void TestCalls(const char* declaring_class, std::vector<const char*> methods) {
+  void TestCalls(const char* declaring_class, const std::vector<const char*>& methods) {
     jmethodID new_method = env_->GetMethodID(concrete_class_, "<init>", "()V");
     jobject obj = env_->NewObject(concrete_class_, new_method);
     CHECK(!env_->ExceptionCheck());
diff --git a/test/089-many-methods/check b/test/089-many-methods/check
index 1f71e8e..e09a291 100755
--- a/test/089-many-methods/check
+++ b/test/089-many-methods/check
@@ -14,5 +14,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-grep Error "$2" > "$2.tmp"
-diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
+EXPECTED_ERROR="Cannot fit requested classes in a single dex"
+if ! grep -q "$EXPECTED_ERROR" "$2"; then
+  exit 1
+else
+  exit 0
+fi
diff --git a/test/089-many-methods/expected.txt b/test/089-many-methods/expected.txt
index bb6ba3c..b75bde4 100644
--- a/test/089-many-methods/expected.txt
+++ b/test/089-many-methods/expected.txt
@@ -1 +1 @@
-Error: Cannot fit requested classes in a single dex file (# fields: 131000 > 65536)
+See the 'check' script for the expectation!
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index a74f763..cc7e806 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -45,7 +45,7 @@
 
 static jint trampoline_JNI_OnLoad(JavaVM* vm, void* reserved) {
   JNIEnv* env = nullptr;
-  typedef jint (*FnPtr_t)(JavaVM*, void*);
+  using FnPtr_t = jint(*)(JavaVM*, void*);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("JNI_OnLoad")->fnPtr);
 
   vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
@@ -91,9 +91,8 @@
   return fnPtr(vm, reserved);
 }
 
-static void trampoline_Java_Main_testFindClassOnAttachedNativeThread(JNIEnv* env,
-                                                                     jclass klass) {
-  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+static void trampoline_Java_Main_testFindClassOnAttachedNativeThread(JNIEnv* env, jclass klass) {
+  using FnPtr_t = void(*)(JNIEnv*, jclass);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
     (find_native_bridge_method("testFindClassOnAttachedNativeThread")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
@@ -102,7 +101,7 @@
 
 static void trampoline_Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv* env,
                                                                            jclass klass) {
-  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  using FnPtr_t = void(*)(JNIEnv*, jclass);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
     (find_native_bridge_method("testFindFieldOnAttachedNativeThreadNative")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
@@ -111,7 +110,7 @@
 
 static void trampoline_Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
                                                                           jclass klass) {
-  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  using FnPtr_t = void(*)(JNIEnv*, jclass);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
     (find_native_bridge_method("testCallStaticVoidMethodOnSubClassNative")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
@@ -119,7 +118,7 @@
 }
 
 static jobject trampoline_Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass klass) {
-  typedef jobject (*FnPtr_t)(JNIEnv*, jclass);
+  using FnPtr_t = jobject(*)(JNIEnv*, jclass);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
     (find_native_bridge_method("testGetMirandaMethodNative")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
@@ -127,7 +126,7 @@
 }
 
 static void trampoline_Java_Main_testNewStringObject(JNIEnv* env, jclass klass) {
-  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  using FnPtr_t = void(*)(JNIEnv*, jclass);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
     (find_native_bridge_method("testNewStringObject")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
@@ -135,7 +134,7 @@
 }
 
 static void trampoline_Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass klass) {
-  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  using FnPtr_t = void(*)(JNIEnv*, jclass);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
     (find_native_bridge_method("testZeroLengthByteBuffers")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
@@ -145,8 +144,8 @@
 static jbyte trampoline_Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2,
                                              jbyte b3, jbyte b4, jbyte b5, jbyte b6,
                                              jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
-  typedef jbyte (*FnPtr_t)(JNIEnv*, jclass, jbyte, jbyte, jbyte, jbyte, jbyte,
-                           jbyte, jbyte, jbyte, jbyte, jbyte);
+  using FnPtr_t = jbyte(*)(JNIEnv*, jclass, jbyte, jbyte, jbyte, jbyte, jbyte, jbyte, jbyte, jbyte,
+                           jbyte, jbyte);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("byteMethod")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
   return fnPtr(env, klass, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10);
@@ -155,8 +154,8 @@
 static jshort trampoline_Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2,
                                                jshort s3, jshort s4, jshort s5, jshort s6,
                                                jshort s7, jshort s8, jshort s9, jshort s10) {
-  typedef jshort (*FnPtr_t)(JNIEnv*, jclass, jshort, jshort, jshort, jshort, jshort,
-                            jshort, jshort, jshort, jshort, jshort);
+  using FnPtr_t = jshort(*)(JNIEnv*, jclass, jshort, jshort, jshort, jshort, jshort, jshort, jshort,
+                            jshort, jshort, jshort);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("shortMethod")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
   return fnPtr(env, klass, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10);
@@ -166,7 +165,7 @@
                                                    jboolean b2, jboolean b3, jboolean b4,
                                                    jboolean b5, jboolean b6, jboolean b7,
                                                    jboolean b8, jboolean b9, jboolean b10) {
-  typedef jboolean (*FnPtr_t)(JNIEnv*, jclass, jboolean, jboolean, jboolean, jboolean, jboolean,
+  using FnPtr_t = jboolean(*)(JNIEnv*, jclass, jboolean, jboolean, jboolean, jboolean, jboolean,
                               jboolean, jboolean, jboolean, jboolean, jboolean);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("booleanMethod")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
@@ -176,8 +175,8 @@
 static jchar trampoline_Java_Main_charMethod(JNIEnv* env, jclass klass, jchar c1, jchar c2,
                                              jchar c3, jchar c4, jchar c5, jchar c6,
                                              jchar c7, jchar c8, jchar c9, jchar c10) {
-  typedef jchar (*FnPtr_t)(JNIEnv*, jclass, jchar, jchar, jchar, jchar, jchar,
-                           jchar, jchar, jchar, jchar, jchar);
+  using FnPtr_t = jchar(*)(JNIEnv*, jclass, jchar, jchar, jchar, jchar, jchar, jchar, jchar, jchar,
+                           jchar, jchar);
   FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("charMethod")->fnPtr);
   printf("%s called!\n", __FUNCTION__);
   return fnPtr(env, klass, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10);
diff --git a/test/1900-track-alloc/alloc.cc b/test/1900-track-alloc/alloc.cc
index db5617c..f209611 100644
--- a/test/1900-track-alloc/alloc.cc
+++ b/test/1900-track-alloc/alloc.cc
@@ -24,7 +24,7 @@
 namespace art {
 namespace Test1900TrackAlloc {
 
-typedef jvmtiError (*GetGlobalState)(jvmtiEnv* env, jlong* allocated);
+using GetGlobalState = jvmtiError(*)(jvmtiEnv* env, jlong* allocated);
 
 struct AllocTrackingData {
   GetGlobalState get_global_state;
diff --git a/test/1940-ddms-ext/ddm_ext.cc b/test/1940-ddms-ext/ddm_ext.cc
index cc29df9..452187b 100644
--- a/test/1940-ddms-ext/ddm_ext.cc
+++ b/test/1940-ddms-ext/ddm_ext.cc
@@ -25,7 +25,7 @@
 namespace art {
 namespace Test1940DdmExt {
 
-typedef jvmtiError (*DdmHandleChunk)(jvmtiEnv* env,
+using DdmHandleChunk = jvmtiError(*)(jvmtiEnv* env,
                                      jint type_in,
                                      jint len_in,
                                      const jbyte* data_in,
diff --git a/test/1946-list-descriptors/descriptors.cc b/test/1946-list-descriptors/descriptors.cc
index 01b306d..07fee61 100644
--- a/test/1946-list-descriptors/descriptors.cc
+++ b/test/1946-list-descriptors/descriptors.cc
@@ -24,7 +24,7 @@
 namespace art {
 namespace Test1946Descriptors {
 
-typedef jvmtiError (*GetDescriptorList)(jvmtiEnv* env, jobject loader, jint* cnt, char*** descs);
+using GetDescriptorList = jvmtiError(*)(jvmtiEnv* env, jobject loader, jint* cnt, char*** descs);
 
 struct DescriptorData {
   GetDescriptorList get_descriptor_list;
diff --git a/test/1951-monitor-enter-no-suspend/raw_monitor.cc b/test/1951-monitor-enter-no-suspend/raw_monitor.cc
index 0425e35..efd02b6 100644
--- a/test/1951-monitor-enter-no-suspend/raw_monitor.cc
+++ b/test/1951-monitor-enter-no-suspend/raw_monitor.cc
@@ -26,7 +26,7 @@
 namespace art {
 namespace Test1951MonitorEnterNoSuspend {
 
-typedef jvmtiError (*RawMonitorEnterNoSuspend)(jvmtiEnv* env, jrawMonitorID mon);
+using RawMonitorEnterNoSuspend = jvmtiError(*)(jvmtiEnv* env, jrawMonitorID mon);
 
 template <typename T>
 static void Dealloc(T* t) {
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index e16fa69..b1bc51e 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -26,7 +26,7 @@
   /// CHECK-START: void Main.invokeStaticInlined() builder (after)
   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   /// CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
-  /// CHECK-DAG:                           InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
+  /// CHECK-DAG:                           InvokeStaticOrDirect [{{([ij]\d+,)?}}<<ClinitCheck>>]
 
   /// CHECK-START: void Main.invokeStaticInlined() inliner (after)
   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
@@ -69,7 +69,7 @@
   /// CHECK-START: void Main.invokeStaticNotInlined() builder (after)
   /// CHECK:         <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   /// CHECK:         <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
-  /// CHECK:                               InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
+  /// CHECK:                               InvokeStaticOrDirect [{{([ij]\d+,)?}}<<ClinitCheck>>]
 
   /// CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
   /// CHECK:         <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 746887f..0bceffd 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -41,10 +41,7 @@
     return x;
   }
 
-  /// CHECK-START: int Main.testSimple(int) sharpening (before)
-  /// CHECK:                InvokeStaticOrDirect method_load_kind:RuntimeCall
-
-  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testSimple(int) sharpening (after)
+  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testSimple(int) builder (after)
   /// CHECK:                InvokeStaticOrDirect method_load_kind:BssEntry
 
   /// CHECK-START-X86: int Main.testSimple(int) pc_relative_fixups_x86 (before)
@@ -59,11 +56,7 @@
     return $noinline$foo(x);
   }
 
-  /// CHECK-START: int Main.testDiamond(boolean, int) sharpening (before)
-  /// CHECK:                InvokeStaticOrDirect method_load_kind:RuntimeCall
-  /// CHECK:                InvokeStaticOrDirect method_load_kind:RuntimeCall
-
-  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testDiamond(boolean, int) sharpening (after)
+  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testDiamond(boolean, int) builder (after)
   /// CHECK:                InvokeStaticOrDirect method_load_kind:BssEntry
   /// CHECK:                InvokeStaticOrDirect method_load_kind:BssEntry
 
@@ -194,18 +187,12 @@
   }
 
   /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexString(int) builder (after)
-  /// CHECK:                InvokeStaticOrDirect method_load_kind:RuntimeCall
-
-  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexString(int) sharpening (after)
   /// CHECK:                InvokeStaticOrDirect method_load_kind:BootImageRelRo
   public static String $noinline$toHexString(int value) {
     return Integer.toString(value, 16);
   }
 
   /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexStringIndirect(int) builder (after)
-  /// CHECK:                InvokeStaticOrDirect method_load_kind:RuntimeCall
-
-  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexStringIndirect(int) sharpening (after)
   /// CHECK:                InvokeStaticOrDirect method_load_kind:BssEntry
 
   /// CHECK-START-X86: java.lang.String Main.$noinline$toHexStringIndirect(int) pc_relative_fixups_x86 (before)
diff --git a/test/565-checker-rotate/smali/Main2.smali b/test/565-checker-rotate/smali/Main2.smali
index ca5027e..98eaf11 100644
--- a/test/565-checker-rotate/smali/Main2.smali
+++ b/test/565-checker-rotate/smali/Main2.smali
@@ -16,13 +16,12 @@
 .super Ljava/lang/Object;
 
 ## CHECK-START: int Main2.rotateLeftBoolean(boolean, int) intrinsics_recognition (after)
-## CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
 ## CHECK:         <<ArgVal:z\d+>>  ParameterValue
 ## CHECK:         <<ArgDist:i\d+>> ParameterValue
 ## CHECK-DAG:     <<Zero:i\d+>>    IntConstant 0
 ## CHECK-DAG:     <<One:i\d+>>     IntConstant 1
 ## CHECK-DAG:     <<Val:i\d+>>     Phi [<<One>>,<<Zero>>]
-## CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<Val>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+## CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<Val>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateLeft
 ## CHECK-DAG:                      Return [<<Result>>]
 
 ## CHECK-START: int Main2.rotateLeftBoolean(boolean, int) instruction_simplifier (after)
@@ -92,14 +91,13 @@
 .end method
 
 ## CHECK-START: int Main2.rotateRightBoolean(boolean, int) intrinsics_recognition (after)
-## CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
 ## CHECK:         <<ArgVal:z\d+>>  ParameterValue
 ## CHECK:         <<ArgDist:i\d+>> ParameterValue
 ## CHECK-DAG:     <<Zero:i\d+>>    IntConstant 0
 ## CHECK-DAG:     <<One:i\d+>>     IntConstant 1
 ## CHECK-DAG:     <<Val:i\d+>>     Phi [<<One>>,<<Zero>>]
-## CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<Val>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
-## CHECK-DAG:                     Return [<<Result>>]
+## CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<Val>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateRight
+## CHECK-DAG:                      Return [<<Result>>]
 
 ## CHECK-START: int Main2.rotateRightBoolean(boolean, int) instruction_simplifier (after)
 ## CHECK:         <<ArgVal:z\d+>>  ParameterValue
diff --git a/test/565-checker-rotate/src-art/Main.java b/test/565-checker-rotate/src-art/Main.java
index b9e1315..f6f281b 100644
--- a/test/565-checker-rotate/src-art/Main.java
+++ b/test/565-checker-rotate/src-art/Main.java
@@ -21,10 +21,9 @@
   private static Class main2;
 
   /// CHECK-START: int Main.rotateLeftByte(byte, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:b\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateLeft
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateLeftByte(byte, int) instruction_simplifier (after)
@@ -42,10 +41,9 @@
   }
 
   /// CHECK-START: int Main.rotateLeftShort(short, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:s\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateLeft
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateLeftShort(short, int) instruction_simplifier (after)
@@ -63,10 +61,9 @@
   }
 
   /// CHECK-START: int Main.rotateLeftChar(char, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:c\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateLeft
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateLeftChar(char, int) instruction_simplifier (after)
@@ -84,10 +81,9 @@
   }
 
   /// CHECK-START: int Main.rotateLeftInt(int, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateLeft
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateLeftInt(int, int) instruction_simplifier (after)
@@ -105,10 +101,9 @@
   }
 
   /// CHECK-START: long Main.rotateLeftLong(long, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:j\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:j\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:LongRotateLeft
+  /// CHECK-DAG:     <<Result:j\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:LongRotateLeft
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: long Main.rotateLeftLong(long, int) instruction_simplifier (after)
@@ -126,10 +121,9 @@
   }
 
   /// CHECK-START: int Main.rotateRightByte(byte, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:b\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateRight
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateRightByte(byte, int) instruction_simplifier (after)
@@ -146,10 +140,9 @@
   }
 
   /// CHECK-START: int Main.rotateRightShort(short, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:s\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateRight
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateRightShort(short, int) instruction_simplifier (after)
@@ -166,10 +159,9 @@
   }
 
   /// CHECK-START: int Main.rotateRightChar(char, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:c\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateRight
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateRightChar(char, int) instruction_simplifier (after)
@@ -186,10 +178,9 @@
   }
 
   /// CHECK-START: int Main.rotateRightInt(int, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateRight
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateRightInt(int, int) instruction_simplifier (after)
@@ -206,10 +197,9 @@
   }
 
   /// CHECK-START: long Main.rotateRightLong(long, int) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:j\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:i\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:j\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:LongRotateRight
+  /// CHECK-DAG:     <<Result:j\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:LongRotateRight
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: long Main.rotateRightLong(long, int) instruction_simplifier (after)
@@ -227,10 +217,9 @@
 
 
   /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:b\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateLeft
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) instruction_simplifier (after)
@@ -248,10 +237,9 @@
   }
 
   /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) intrinsics_recognition (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
   /// CHECK:         <<ArgDist:b\d+>> ParameterValue
-  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>{{(,[ij]\d+)?}}] intrinsic:IntegerRotateRight
   /// CHECK-DAG:                      Return [<<Result>>]
 
   /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) instruction_simplifier (after)
diff --git a/test/566-checker-signum/smali/Main2.smali b/test/566-checker-signum/smali/Main2.smali
index d99ad86..ec63cf8 100644
--- a/test/566-checker-signum/smali/Main2.smali
+++ b/test/566-checker-signum/smali/Main2.smali
@@ -16,11 +16,10 @@
 .super Ljava/lang/Object;
 
 ## CHECK-START: int Main2.signBoolean(boolean) intrinsics_recognition (after)
-## CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
 ## CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
 ## CHECK-DAG:     <<One:i\d+>>    IntConstant 1
 ## CHECK-DAG:     <<Phi:i\d+>>    Phi [<<One>>,<<Zero>>]
-## CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<Phi>>,<<Method>>] intrinsic:IntegerSignum
+## CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<Phi>>{{(,[ij]\d+)?}}] intrinsic:IntegerSignum
 ## CHECK-DAG:                     Return [<<Result>>]
 
 ## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier (after)
diff --git a/test/567-checker-compare/smali/Smali.smali b/test/567-checker-compare/smali/Smali.smali
index 8fc39f1..94b1f13 100644
--- a/test/567-checker-compare/smali/Smali.smali
+++ b/test/567-checker-compare/smali/Smali.smali
@@ -16,12 +16,11 @@
 .super Ljava/lang/Object;
 
 ##  CHECK-START: int Smali.compareBooleans(boolean, boolean) intrinsics_recognition (after)
-##  CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
 ##  CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
 ##  CHECK-DAG:     <<One:i\d+>>    IntConstant 1
 ##  CHECK-DAG:     <<PhiX:i\d+>>   Phi [<<One>>,<<Zero>>]
 ##  CHECK-DAG:     <<PhiY:i\d+>>   Phi [<<One>>,<<Zero>>]
-##  CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<PhiX>>,<<PhiY>>,<<Method>>] intrinsic:IntegerCompare
+##  CHECK-DAG:     <<Result:i\d+>> InvokeStaticOrDirect [<<PhiX>>,<<PhiY>>{{(,[ij]\d+)?}}] intrinsic:IntegerCompare
 ##  CHECK-DAG:                     Return [<<Result>>]
 
 ##  CHECK-START: int Smali.compareBooleans(boolean, boolean) instruction_simplifier (after)
diff --git a/test/567-checker-compare/src/Main.java b/test/567-checker-compare/src/Main.java
index abfaf9f..f43ac30 100644
--- a/test/567-checker-compare/src/Main.java
+++ b/test/567-checker-compare/src/Main.java
@@ -22,9 +22,8 @@
 
   /// CHECK-START: void Main.$opt$noinline$testReplaceInputWithItself(int) intrinsics_recognition (after)
   /// CHECK-DAG:     <<ArgX:i\d+>>   ParameterValue
-  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
   /// CHECK-DAG:     <<Zero:i\d+>>   IntConstant 0
-  /// CHECK-DAG:     <<Cmp:i\d+>>    InvokeStaticOrDirect [<<ArgX>>,<<Zero>>,<<Method>>] intrinsic:IntegerCompare
+  /// CHECK-DAG:     <<Cmp:i\d+>>    InvokeStaticOrDirect [<<ArgX>>,<<Zero>>{{(,[ij]\d+)?}}] intrinsic:IntegerCompare
   /// CHECK-DAG:                     GreaterThanOrEqual [<<Cmp>>,<<Zero>>]
 
   /// CHECK-START: void Main.$opt$noinline$testReplaceInputWithItself(int) instruction_simplifier (after)
diff --git a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
index f74e88f..bd90fe7 100644
--- a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
+++ b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
@@ -210,14 +210,12 @@
 .end method
 
 ## CHECK-START: int SmaliTests.longToIntOfBoolean() builder (after)
-## CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
 ## CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
-## CHECK-DAG:     <<ZToJ:j\d+>>          InvokeStaticOrDirect [<<Sget>>,<<Method>>]
+## CHECK-DAG:     <<ZToJ:j\d+>>          InvokeStaticOrDirect [<<Sget>>{{(,[ij]\d+)?}}]
 ## CHECK-DAG:     <<JToI:i\d+>>          TypeConversion [<<ZToJ>>]
 ## CHECK-DAG:                            Return [<<JToI>>]
 
 ## CHECK-START: int SmaliTests.longToIntOfBoolean() inliner (after)
-## CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
 ## CHECK-DAG:     <<Zero:i\d+>>          IntConstant 0
 ## CHECK-DAG:     <<One:i\d+>>           IntConstant 1
 ## CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
@@ -228,7 +226,6 @@
 ## CHECK-DAG:                            Return [<<JToI>>]
 
 ## CHECK-START: int SmaliTests.longToIntOfBoolean() select_generator (after)
-## CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
 ## CHECK-DAG:     <<Zero:i\d+>>          IntConstant 0
 ## CHECK-DAG:     <<One:i\d+>>           IntConstant 1
 ## CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
@@ -236,7 +233,6 @@
 ## CHECK-DAG:                            Return [<<Sel>>]
 
 ## CHECK-START: int SmaliTests.longToIntOfBoolean() instruction_simplifier$after_bce (after)
-## CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
 ## CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
 ## CHECK-DAG:                            Return [<<Sget>>]
 .method public static longToIntOfBoolean()I
diff --git a/test/593-checker-boolean-2-integral-conv/src/Main.java b/test/593-checker-boolean-2-integral-conv/src/Main.java
index fdc0919..b085c42 100644
--- a/test/593-checker-boolean-2-integral-conv/src/Main.java
+++ b/test/593-checker-boolean-2-integral-conv/src/Main.java
@@ -100,14 +100,12 @@
   }
 
   /// CHECK-START: int Main.longToIntOfBoolean() builder (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
   /// CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
-  /// CHECK-DAG:     <<ZToJ:j\d+>>          InvokeStaticOrDirect [<<Sget>>,<<Method>>]
+  /// CHECK-DAG:     <<ZToJ:j\d+>>          InvokeStaticOrDirect [<<Sget>>{{(,[ij]\d+)?}}]
   /// CHECK-DAG:     <<JToI:i\d+>>          TypeConversion [<<ZToJ>>]
   /// CHECK-DAG:                            Return [<<JToI>>]
 
   /// CHECK-START: int Main.longToIntOfBoolean() inliner (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
   /// CHECK-DAG:     <<Zero:j\d+>>          LongConstant 0
   /// CHECK-DAG:     <<One:j\d+>>           LongConstant 1
   /// CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
@@ -123,7 +121,6 @@
   /// CHECK-NOT:                            Phi
 
   /// CHECK-START: int Main.longToIntOfBoolean() select_generator (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
   /// CHECK-DAG:     <<Zero:j\d+>>          LongConstant 0
   /// CHECK-DAG:     <<One:j\d+>>           LongConstant 1
   /// CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
@@ -135,7 +132,6 @@
   // TODO: Re-enable checks below after simplifier is updated to handle this pattern: b/63064517
 
   // CHECK-START: int Main.longToIntOfBoolean() instruction_simplifier$after_bce (after)
-  // CHECK-DAG:     <<Method:[ij]\d+>>     CurrentMethod
   // CHECK-DAG:     <<Sget:z\d+>>          StaticFieldGet
   // CHECK-DAG:                            Return [<<Sget>>]
 
diff --git a/test/602-deoptimizeable/src/Main.java b/test/602-deoptimizeable/src/Main.java
index d995923..3d45b86 100644
--- a/test/602-deoptimizeable/src/Main.java
+++ b/test/602-deoptimizeable/src/Main.java
@@ -126,6 +126,9 @@
                     assertIsManaged();
                     map.put(new DummyObject(10), Long.valueOf(100));
                     assertIsInterpreted();  // Every deoptimizeable method is deoptimized.
+                    if (map.get(new DummyObject(10)) == null) {
+                        System.out.println("Expected map to contain DummyObject(10)");
+                    }
                 } catch (Exception e) {
                     e.printStackTrace(System.out);
                 }
diff --git a/test/684-checker-simd-dotprod/expected.txt b/test/684-checker-simd-dotprod/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/684-checker-simd-dotprod/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/684-checker-simd-dotprod/info.txt b/test/684-checker-simd-dotprod/info.txt
new file mode 100644
index 0000000..6c1efb6
--- /dev/null
+++ b/test/684-checker-simd-dotprod/info.txt
@@ -0,0 +1 @@
+Functional tests on dot product idiom SIMD vectorization.
diff --git a/test/684-checker-simd-dotprod/src/Main.java b/test/684-checker-simd-dotprod/src/Main.java
new file mode 100644
index 0000000..e0c8716
--- /dev/null
+++ b/test/684-checker-simd-dotprod/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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 other.TestByte;
+import other.TestCharShort;
+import other.TestVarious;
+
+/**
+ * Tests for dot product idiom vectorization.
+ */
+public class Main {
+  public static void main(String[] args) {
+     TestByte.run();
+     TestCharShort.run();
+     TestVarious.run();
+     System.out.println("passed");
+  }
+}
diff --git a/test/684-checker-simd-dotprod/src/other/TestByte.java b/test/684-checker-simd-dotprod/src/other/TestByte.java
new file mode 100644
index 0000000..9acfc59
--- /dev/null
+++ b/test/684-checker-simd-dotprod/src/other/TestByte.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package other;
+
+/**
+ * Tests for dot product idiom vectorization: byte case.
+ */
+public class TestByte {
+
+  public static final int ARRAY_SIZE = 1024;
+
+  /// CHECK-START: int other.TestByte.testDotProdSimple(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<Get2>>]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdSimple(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Load2>>] type:Int8   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdSimple(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = a[i] * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdComplex(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC1:i\d+>>   Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:b\d+>>  TypeConversion [<<AddC1>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC2:i\d+>>   Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:b\d+>>  TypeConversion [<<AddC2>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdComplex(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Int8   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplex(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((byte)(a[i] + 1)) * ((byte)(b[i] + 1));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleUnsigned(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<Get2>>]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdSimpleUnsigned(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Load2>>] type:Uint8  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdSimpleUnsigned(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (a[i] & 0xff) * (b[i] & 0xff);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdComplexUnsigned(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC:i\d+>>    Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:a\d+>>  TypeConversion [<<AddC>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddGets:i\d+>> Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:a\d+>>  TypeConversion [<<AddGets>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdComplexUnsigned(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Uint8  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplexUnsigned(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (((a[i] & 0xff) + 1) & 0xff) * (((b[i] & 0xff) + 1) & 0xff);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdComplexUnsignedCastedToSigned(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC:i\d+>>    Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:b\d+>>  TypeConversion [<<AddC>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddGets:i\d+>> Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:b\d+>>  TypeConversion [<<AddGets>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdComplexUnsignedCastedToSigned(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Int8   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplexUnsignedCastedToSigned(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((byte)((a[i] & 0xff) + 1)) * ((byte)((b[i] & 0xff) + 1));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdComplexSignedCastedToUnsigned(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC:i\d+>>    Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:a\d+>>  TypeConversion [<<AddC>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddGets:i\d+>> Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:a\d+>>  TypeConversion [<<AddGets>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdComplexSignedCastedToUnsigned(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Uint8  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplexSignedCastedToUnsigned(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((a[i] + 1) & 0xff) * ((b[i] + 1) & 0xff);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdSignedWidening(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG:                  VecDotProd type:Int8
+  public static final int testDotProdSignedWidening(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((short)(a[i])) * ((short)(b[i]));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdParamSigned(int, byte[]) loop_optimization (after)
+  /// CHECK-DAG:                  VecDotProd type:Int8
+  public static final int testDotProdParamSigned(int x, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (byte)(x) * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START-{ARM64}: int other.TestByte.testDotProdParamUnsigned(int, byte[]) loop_optimization (after)
+  /// CHECK-DAG:                  VecDotProd type:Uint8
+  public static final int testDotProdParamUnsigned(int x, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (x & 0xff) * (b[i] & 0xff);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  // No DOTPROD cases.
+
+  /// CHECK-START: int other.TestByte.testDotProdIntParam(int, byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdIntParam(int x, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = b[i] * (x);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSignedToChar(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSignedToChar(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((char)(a[i])) * ((char)(b[i]));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  // Cases when result of Mul is type-converted are not supported.
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleCastedToSignedByte(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleCastedToSignedByte(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      byte temp = (byte)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleCastedToUnsignedByte(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleCastedToUnsignedByte(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      s += (a[i] * b[i]) & 0xff;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleUnsignedCastedToSignedByte(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToSignedByte(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      byte temp = (byte)((a[i] & 0xff) * (b[i] & 0xff));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleUnsignedCastedToUnsignedByte(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToUnsignedByte(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      s += ((a[i] & 0xff) * (b[i] & 0xff)) & 0xff;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleCastedToShort(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleCastedToShort(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      short temp = (short)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleCastedToChar(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleCastedToChar(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      char temp = (char)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleUnsignedCastedToShort(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToShort(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      short temp = (short)((a[i] & 0xff) * (b[i] & 0xff));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleUnsignedCastedToChar(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToChar(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      char temp = (char)((a[i] & 0xff) * (b[i] & 0xff));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdSimpleUnsignedCastedToLong(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToLong(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      long temp = (long)((a[i] & 0xff) * (b[i] & 0xff));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestByte.testDotProdUnsignedSigned(byte[], byte[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdUnsignedSigned(byte[] a, byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (a[i] & 0xff) * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void testDotProd(byte[] b1, byte[] b2, int[] results) {
+    expectEquals(results[0], testDotProdSimple(b1, b2));
+    expectEquals(results[1], testDotProdComplex(b1, b2));
+    expectEquals(results[2], testDotProdSimpleUnsigned(b1, b2));
+    expectEquals(results[3], testDotProdComplexUnsigned(b1, b2));
+    expectEquals(results[4], testDotProdComplexUnsignedCastedToSigned(b1, b2));
+    expectEquals(results[5], testDotProdComplexSignedCastedToUnsigned(b1, b2));
+    expectEquals(results[6], testDotProdSignedWidening(b1, b2));
+    expectEquals(results[7], testDotProdParamSigned(-128, b2));
+    expectEquals(results[8], testDotProdParamUnsigned(-128, b2));
+    expectEquals(results[9], testDotProdIntParam(-128, b2));
+    expectEquals(results[10], testDotProdSignedToChar(b1, b2));
+    expectEquals(results[11], testDotProdSimpleCastedToSignedByte(b1, b2));
+    expectEquals(results[12], testDotProdSimpleCastedToUnsignedByte(b1, b2));
+    expectEquals(results[13], testDotProdSimpleUnsignedCastedToSignedByte(b1, b2));
+    expectEquals(results[14], testDotProdSimpleUnsignedCastedToUnsignedByte(b1, b2));
+    expectEquals(results[15], testDotProdSimpleCastedToShort(b1, b2));
+    expectEquals(results[16], testDotProdSimpleCastedToChar(b1, b2));
+    expectEquals(results[17], testDotProdSimpleUnsignedCastedToShort(b1, b2));
+    expectEquals(results[18], testDotProdSimpleUnsignedCastedToChar(b1, b2));
+    expectEquals(results[19], testDotProdSimpleUnsignedCastedToLong(b1, b2));
+    expectEquals(results[20], testDotProdUnsignedSigned(b1, b2));
+  }
+
+  public static void run() {
+    byte[] b1_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127 };
+    byte[] b2_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127 };
+    int[] results_1 = { 64516, 65548, 64516, 65548, 65548, 65548, 64516, -65024, 65024, -65024,
+                        64516, 4, 4, 4, 4, 64516, 64516, 64516, 64516, 64516, 64516 };
+    testDotProd(b1_1, b2_1, results_1);
+
+    byte[] b1_2 = { 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127 };
+    byte[] b2_2 = { 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127 };
+    int[] results_2 = { 80645, 81931, 80645, 81931, 81931, 81931, 80645, -81280, 81280, -81280,
+                        80645, 5, 5, 5, 5, 80645, 80645, 80645, 80645, 80645, 80645 };
+    testDotProd(b1_2, b2_2, results_2);
+
+    byte[] b1_3 = { -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, -128, -128, -128 };
+    byte[] b2_3 = {  127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  127,  127,  127,  127 };
+    int[] results_3 = { -81280, 81291, 81280, 82571, 81291, 82571, -81280, -81280, 81280, -81280,
+                        41534080, -640, 640, -640, 640, -81280, 246400, 81280, 81280, 81280, 81280 };
+    testDotProd(b1_3, b2_3, results_3);
+
+    byte[] b1_4 = { -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, -128, -128, -128 };
+    byte[] b2_4 = { -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, -128, -128, -128 };
+    int[] results_4 = { 81920, 80656, 81920, 83216, 80656, 83216, 81920, 81920, 81920, 81920,
+                       -83804160, 0, 0, 0, 0, 81920, 81920, 81920, 81920, 81920, -81920 };
+    testDotProd(b1_4, b2_4, results_4);
+  }
+
+  public static void main(String[] args) {
+    run();
+  }
+}
diff --git a/test/684-checker-simd-dotprod/src/other/TestCharShort.java b/test/684-checker-simd-dotprod/src/other/TestCharShort.java
new file mode 100644
index 0000000..9cb9db5
--- /dev/null
+++ b/test/684-checker-simd-dotprod/src/other/TestCharShort.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package other;
+
+/**
+ * Tests for dot product idiom vectorization: char and short case.
+ */
+public class TestCharShort {
+
+  public static final int ARRAY_SIZE = 1024;
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimple(short[], short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:s\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:s\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<Get2>>]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdSimple(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const8:i\d+>>  IntConstant 8                                         loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Load2>>] type:Int16  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const8>>]                             loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdSimple(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = a[i] * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdComplex(short[], short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:s\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC1:i\d+>>   Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:s\d+>>  TypeConversion [<<AddC1>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:s\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC2:i\d+>>   Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:s\d+>>  TypeConversion [<<AddC2>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdComplex(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const8:i\d+>>  IntConstant 8                                         loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Int16  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const8>>]                             loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplex(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((short)(a[i] + 1)) * ((short)(b[i] + 1));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleUnsigned(char[], char[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<Get2>>]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdSimpleUnsigned(char[], char[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const8:i\d+>>  IntConstant 8                                         loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Load2>>] type:Uint16 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const8>>]                             loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdSimpleUnsigned(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = a[i] * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdComplexUnsigned(char[], char[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC:i\d+>>    Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:c\d+>>  TypeConversion [<<AddC>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddGets:i\d+>> Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:c\d+>>  TypeConversion [<<AddGets>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdComplexUnsigned(char[], char[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const8:i\d+>>  IntConstant 8                                         loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Uint16 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const8>>]                             loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplexUnsigned(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((char)(a[i] + 1)) * ((char)(b[i] + 1));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdComplexUnsignedCastedToSigned(char[], char[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC:i\d+>>    Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:s\d+>>  TypeConversion [<<AddC>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddGets:i\d+>> Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:s\d+>>  TypeConversion [<<AddGets>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdComplexUnsignedCastedToSigned(char[], char[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const8:i\d+>>  IntConstant 8                                         loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Int16  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const8>>]                             loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplexUnsignedCastedToSigned(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((short)(a[i] + 1)) * ((short)(b[i] + 1));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdComplexSignedCastedToUnsigned(short[], short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:s\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddC:i\d+>>    Add [<<Get1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:c\d+>>  TypeConversion [<<AddC>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:s\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddGets:i\d+>> Add [<<Get2>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:c\d+>>  TypeConversion [<<AddGets>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<TypeC1>>,<<TypeC2>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdComplexSignedCastedToUnsigned(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const8:i\d+>>  IntConstant 8                                         loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const1>>]                       loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd1:d\d+>>   VecAdd [<<Load1>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd2:d\d+>>   VecAdd [<<Load2>>,<<Repl>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<VAdd1>>,<<VAdd2>>] type:Uint16 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const8>>]                             loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdComplexSignedCastedToUnsigned(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((char)(a[i] + 1)) * ((char)(b[i] + 1));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdSignedToInt(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG:                  VecDotProd type:Int16
+  public static final int testDotProdSignedToInt(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((int)(a[i])) * ((int)(b[i]));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdParamSigned(int, short[]) loop_optimization (after)
+  /// CHECK-DAG:                  VecDotProd type:Int16
+  public static final int testDotProdParamSigned(int x, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (short)(x) * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdParamUnsigned(int, char[]) loop_optimization (after)
+  /// CHECK-DAG:                  VecDotProd type:Uint16
+  public static final int testDotProdParamUnsigned(int x, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (char)(x) * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdIntParam(int, short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdIntParam(int x, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = b[i] * (x);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START-{ARM64}: int other.TestCharShort.testDotProdSignedToChar(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG:                  VecDotProd type:Uint16
+  public static final int testDotProdSignedToChar(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((char)(a[i])) * ((char)(b[i]));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  // Cases when result of Mul is type-converted are not supported.
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleMulCastedToSigned(short[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd type:Uint16
+  public static final int testDotProdSimpleMulCastedToSigned(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      short temp = (short)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleMulCastedToUnsigned(short[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleMulCastedToUnsigned(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      char temp = (char)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleUnsignedMulCastedToSigned(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedMulCastedToSigned(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      short temp = (short)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleUnsignedMulCastedToUnsigned(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedMulCastedToUnsigned(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      char temp = (char)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleCastedToShort(short[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleCastedToShort(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      short temp = (short)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleCastedToChar(short[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleCastedToChar(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      char temp = (char)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleUnsignedCastedToShort(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToShort(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      short temp = (short)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleUnsignedCastedToChar(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToChar(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      char temp = (char)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSimpleUnsignedCastedToLong(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSimpleUnsignedCastedToLong(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      long temp = (long)(a[i] * b[i]);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  // Narrowing conversions.
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSignedNarrowerSigned(short[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSignedNarrowerSigned(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((byte)(a[i])) * ((byte)(b[i]));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdSignedNarrowerUnsigned(short[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdSignedNarrowerUnsigned(short[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (a[i] & 0xff) * (b[i] & 0xff);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdUnsignedNarrowerSigned(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdUnsignedNarrowerSigned(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((byte)(a[i])) * ((byte)(b[i]));
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdUnsignedNarrowerUnsigned(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdUnsignedNarrowerUnsigned(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = (a[i] & 0xff) * (b[i] & 0xff);
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  /// CHECK-START: int other.TestCharShort.testDotProdUnsignedSigned(char[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdUnsignedSigned(char[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = a[i] * b[i];
+      s += temp;
+    }
+    return s - 1;
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void testDotProd(short[] s1, short[] s2, char[] c1, char[] c2, int[] results) {
+    expectEquals(results[0], testDotProdSimple(s1, s2));
+    expectEquals(results[1], testDotProdComplex(s1, s2));
+    expectEquals(results[2], testDotProdSimpleUnsigned(c1, c2));
+    expectEquals(results[3], testDotProdComplexUnsigned(c1, c2));
+    expectEquals(results[4], testDotProdComplexUnsignedCastedToSigned(c1, c2));
+    expectEquals(results[5], testDotProdComplexSignedCastedToUnsigned(s1, s2));
+    expectEquals(results[6], testDotProdSignedToInt(s1, s2));
+    expectEquals(results[7], testDotProdParamSigned(-32768, s2));
+    expectEquals(results[8], testDotProdParamUnsigned(-32768, c2));
+    expectEquals(results[9], testDotProdIntParam(-32768, s2));
+    expectEquals(results[10], testDotProdSignedToChar(s1, s2));
+    expectEquals(results[11], testDotProdSimpleMulCastedToSigned(s1, s2));
+    expectEquals(results[12], testDotProdSimpleMulCastedToUnsigned(s1, s2));
+    expectEquals(results[13], testDotProdSimpleUnsignedMulCastedToSigned(c1, c2));
+    expectEquals(results[14], testDotProdSimpleUnsignedMulCastedToUnsigned(c1, c2));
+    expectEquals(results[15], testDotProdSimpleCastedToShort(s1, s2));
+    expectEquals(results[16], testDotProdSimpleCastedToChar(s1, s2));
+    expectEquals(results[17], testDotProdSimpleUnsignedCastedToShort(c1, c2));
+    expectEquals(results[18], testDotProdSimpleUnsignedCastedToChar(c1, c2));
+    expectEquals(results[19], testDotProdSimpleUnsignedCastedToLong(c1, c2));
+    expectEquals(results[20], testDotProdSignedNarrowerSigned(s1, s2));
+    expectEquals(results[21], testDotProdSignedNarrowerUnsigned(s1, s2));
+    expectEquals(results[22], testDotProdUnsignedNarrowerSigned(c1, c2));
+    expectEquals(results[23], testDotProdUnsignedNarrowerUnsigned(c1, c2));
+    expectEquals(results[24], testDotProdUnsignedSigned(c1, s2));
+  }
+
+  public static void run() {
+    final short MAX_S = Short.MAX_VALUE;
+    final short MIN_S = Short.MAX_VALUE;
+
+    short[] s1_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S };
+    short[] s2_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S };
+    char[]  c1_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S };
+    char[]  c2_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S };
+    int[] results_1 = { 2147352578, -2147483634, 2147352578, -2147483634, -2147483634, -2147483634,
+                        2147352578, -2147418112, 2147418112, -2147418112, 2147352578,
+                        2, 2, 2, 2, 2, 2, 2, 2, 2147352578, 2, 130050, 2, 130050, 2147352578 };
+    testDotProd(s1_1, s2_1, c1_1, c2_1, results_1);
+
+    short[] s1_2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S, MAX_S, MAX_S };
+    short[] s2_2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S, MAX_S, MAX_S };
+    char[]  c1_2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S, MAX_S, MAX_S };
+    char[]  c2_2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S, MAX_S, MAX_S };
+    int[] results_2 = { -262140, 12, -262140, 12, 12, 12, -262140, 131072, -131072, 131072,
+                        -262140, 4, 4, 4, 4, 4, 4, 4, 4, -262140, 4, 260100, 4, 260100, -262140 };
+    testDotProd(s1_2, s2_2, c1_2, c2_2, results_2);
+
+    short[] s1_3 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+    short[] s2_3 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S };
+    char[]  c1_3 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+    char[]  c2_3 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MAX_S, MAX_S };
+    int[] results_3 = { 2147352578, -2147483634, 2147352578, -2147483634, -2147483634,
+                        -2147483634, 2147352578, -2147418112, 2147418112, -2147418112,
+                        2147352578, 2, 2, 2, 2, 2, 2, 2, 2, 2147352578, 2, 130050, 2,
+                        130050, 2147352578};
+    testDotProd(s1_3, s2_3, c1_3, c2_3, results_3);
+
+
+    short[] s1_4 = { MIN_S, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+    short[] s2_4 = { MIN_S, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+    char[]  c1_4 = { MIN_S, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+    char[]  c2_4 = { MIN_S, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+    int[] results_4 = { -1073938429, -1073741811, -1073938429, -1073741811, -1073741811,
+                        -1073741811, -1073938429, 1073840128, -1073840128, 1073840128,
+                        -1073938429, 3, 3, 3, 3, 3, 3, 3, 3, -1073938429, 3, 195075, 3,
+                        195075, -1073938429 };
+    testDotProd(s1_4, s2_4, c1_4, c2_4, results_4);
+  }
+
+  public static void main(String[] args) {
+    run();
+  }
+}
diff --git a/test/684-checker-simd-dotprod/src/other/TestVarious.java b/test/684-checker-simd-dotprod/src/other/TestVarious.java
new file mode 100644
index 0000000..3f46098
--- /dev/null
+++ b/test/684-checker-simd-dotprod/src/other/TestVarious.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package other;
+
+/**
+ * Tests for dot product idiom vectorization.
+ */
+public class TestVarious {
+
+  /// CHECK-START: int other.TestVarious.testDotProdConstRight(byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const89:i\d+>> IntConstant 89                                        loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<Const89>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdConstRight(byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Const89:i\d+>> IntConstant 89                                        loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const89>>]                      loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Repl>>] type:Int8    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdConstRight(byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp =  b[i] * 89;
+      s += temp;
+    }
+    return s;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdConstLeft(byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const89:i\d+>> IntConstant 89                                        loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<Const89>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdConstLeft(byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Const89:i\d+>> IntConstant 89                                        loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const89>>]                      loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Repl>>] type:Uint8   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdConstLeft(byte[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = 89 * (b[i] & 0xff);
+      s += temp;
+    }
+    return s;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdLoopInvariantConvRight(byte[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Param:i\d+>>   ParameterValue                                        loop:none
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<ConstL:i\d+>>  IntConstant 129                                       loop:none
+  /// CHECK-DAG: <<AddP:i\d+>>    Add [<<Param>>,<<ConstL>>]                            loop:none
+  /// CHECK-DAG: <<TypeCnv:b\d+>> TypeConversion [<<AddP>>]                             loop:none
+  //
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                             loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<TypeCnv>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                             loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdLoopInvariantConvRight(byte[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Param:i\d+>>   ParameterValue                                        loop:none
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<ConstL:i\d+>>  IntConstant 129                                       loop:none
+  /// CHECK-DAG: <<AddP:i\d+>>    Add [<<Param>>,<<ConstL>>]                            loop:none
+  /// CHECK-DAG: <<TypeCnv:b\d+>> TypeConversion [<<AddP>>]                             loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<TypeCnv>>]                      loop:none
+  //
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Repl>>] type:Int8    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                                  loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]                         loop:none
+  public static final int testDotProdLoopInvariantConvRight(byte[] b, int param) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = b[i] * ((byte)(param + 129));
+      s += temp;
+    }
+    return s;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdByteToChar(char[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdByteToChar(char[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = ((char)((byte)(a[i] + 129))) * b[i];
+      s += temp;
+    }
+    return s;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdMixedSize(byte[], short[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdMixedSize(byte[] a, short[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = a[i] * b[i];
+      s += temp;
+    }
+    return s;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdMixedSizeAndSign(byte[], char[]) loop_optimization (after)
+  /// CHECK-NOT:                  VecDotProd
+  public static final int testDotProdMixedSizeAndSign(byte[] a, char[] b) {
+    int s = 1;
+    for (int i = 0; i < b.length; i++) {
+      int temp = a[i] * b[i];
+      s += temp;
+    }
+    return s;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdInt32(int[], int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:i\d+>>     Mul [<<Get1>>,<<Get2>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul>>]                    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdInt32(int[], int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Set:d\d+>>     VecSetScalars [<<Const1>>]                loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set>>,{{d\d+}}]                    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul:d\d+>>     VecMul [<<Load1>>,<<Load2>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecAdd [<<Phi2>>,<<Mul>>]                 loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<Reduce:d\d+>>  VecReduce [<<Phi2>>]                      loop:none
+  /// CHECK-DAG:                  VecExtractScalar [<<Reduce>>]             loop:none
+  public static final int testDotProdInt32(int[] a, int[] b) {
+    int s = 1;
+    for (int i = 0;  i < b.length; i++) {
+      int temp = a[i] * b[i];
+      s += temp;
+    }
+    return s;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdBothSignedUnsigned1(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                             loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:i\d+>>    Phi [<<Const2>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul1:i\d+>>    Mul [<<Get1>>,<<Get2>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:a\d+>>  TypeConversion [<<Get1>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC2:a\d+>>  TypeConversion [<<Get2>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul2:i\d+>>    Mul [<<TypeC1>>,<<TypeC2>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi3>>,<<Mul2>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdBothSignedUnsigned1(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Set1:d\d+>>    VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Set2:d\d+>>    VecSetScalars [<<Const2>>]                            loop:none
+  //
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set1>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:d\d+>>    Phi [<<Set2>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Load2>>] type:Int8   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi3>>,<<Load1>>,<<Load2>>] type:Uint8  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  public static final int testDotProdBothSignedUnsigned1(byte[] a, byte[] b) {
+    int s1 = 1;
+    int s2 = 2;
+    for (int i = 0; i < b.length; i++) {
+      byte a_val = a[i];
+      byte b_val = b[i];
+      s1 += a_val * b_val;
+      s2 += (a_val & 0xff) * (b_val & 0xff);
+    }
+    return s1 + s2;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdBothSignedUnsigned2(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                             loop:none
+  /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42                            loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:i\d+>>    Phi [<<Const2>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:b\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:a\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeC1:a\d+>>  TypeConversion [<<Get1>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul1:i\d+>>    Mul [<<Get2>>,<<TypeC1>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi3>>,<<Mul1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul2:i\d+>>    Mul [<<Get1>>,<<Const42>>]                loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul2>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdBothSignedUnsigned2(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42                                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>>    VecReplicateScalar [<<Const42>>]                      loop:none
+  /// CHECK-DAG: <<Set1:d\d+>>    VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Set2:d\d+>>    VecSetScalars [<<Const2>>]                            loop:none
+  //
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set1>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:d\d+>>    Phi [<<Set2>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi3>>,<<Load2>>,<<Load1>>] type:Uint8  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Repl>>] type:Int8    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  public static final int testDotProdBothSignedUnsigned2(byte[] a, byte[] b) {
+    int s1 = 1;
+    int s2 = 2;
+    for (int i = 0; i < b.length; i++) {
+      byte a_val = a[i];
+      byte b_val = b[i];
+      s2 += (a_val & 0xff) * (b_val & 0xff);
+      s1 += a_val * 42;
+    }
+    return s1 + s2;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdBothSignedUnsignedDoubleLoad(byte[], byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                             loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:i\d+>>    Phi [<<Const2>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<GetB1:b\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<GetB2:b\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul1:i\d+>>    Mul [<<GetB1>>,<<GetB2>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<GetA1:a\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<GetA2:a\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul2:i\d+>>    Mul [<<GetA1>>,<<GetA2>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi3>>,<<Mul2>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdBothSignedUnsignedDoubleLoad(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                                         loop:none
+  /// CHECK-DAG: <<Const16:i\d+>> IntConstant 16                                        loop:none
+  /// CHECK-DAG: <<Set1:d\d+>>    VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Set2:d\d+>>    VecSetScalars [<<Const2>>]                            loop:none
+  //
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set1>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:d\d+>>    Phi [<<Set2>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Load2>>] type:Int8   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load3:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load4:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi3>>,<<Load3>>,<<Load4>>] type:Uint8  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const16>>]                            loop:<<Loop>>      outer_loop:none
+  public static final int testDotProdBothSignedUnsignedDoubleLoad(byte[] a, byte[] b) {
+    int s1 = 1;
+    int s2 = 2;
+    for (int i = 0; i < b.length; i++) {
+      s1 += a[i] * b[i];
+      s2 += (a[i] & 0xff) * (b[i] & 0xff);
+    }
+    return s1 + s2;
+  }
+
+  /// CHECK-START: int other.TestVarious.testDotProdBothSignedUnsignedChar(char[], char[]) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                             loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>    Phi [<<Const1>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:i\d+>>    Phi [<<Const2>>,{{i\d+}}]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get2:c\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeS1:s\d+>>  TypeConversion [<<Get1>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<TypeS2:s\d+>>  TypeConversion [<<Get2>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul1:i\d+>>    Mul [<<TypeS1>>,<<TypeS2>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi3>>,<<Mul1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Mul2:i\d+>>    Mul [<<Get1>>,<<Get2>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi2>>,<<Mul2>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START-{ARM64}: int other.TestVarious.testDotProdBothSignedUnsignedChar(char[], char[]) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                                         loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                                         loop:none
+  /// CHECK-DAG: <<Const2:i\d+>>  IntConstant 2                                         loop:none
+  /// CHECK-DAG: <<Const8:i\d+>>  IntConstant 8                                         loop:none
+  /// CHECK-DAG: <<Set1:d\d+>>    VecSetScalars [<<Const1>>]                            loop:none
+  /// CHECK-DAG: <<Set2:d\d+>>    VecSetScalars [<<Const2>>]                            loop:none
+  //
+  /// CHECK-DAG: <<Phi1:i\d+>>    Phi [<<Const0>>,{{i\d+}}]                             loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>    Phi [<<Set1>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:d\d+>>    Phi [<<Set2>>,{{d\d+}}]                               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load1:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load2:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi3>>,<<Load1>>,<<Load2>>] type:Int16  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  VecDotProd [<<Phi2>>,<<Load1>>,<<Load2>>] type:Uint16 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  Add [<<Phi1>>,<<Const8>>]                             loop:<<Loop>>      outer_loop:none
+  public static final int testDotProdBothSignedUnsignedChar(char[] a, char[] b) {
+    int s1 = 1;
+    int s2 = 2;
+    for (int i = 0; i < b.length; i++) {
+      char a_val = a[i];
+      char b_val = b[i];
+      s2 += ((short)a_val) * ((short)b_val);
+      s1 += a_val * b_val;
+    }
+    return s1 + s2;
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void run() {
+    final short MAX_S = Short.MAX_VALUE;
+    final short MIN_S = Short.MAX_VALUE;
+
+    byte[] b1 = { -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, -128, -128, -128 };
+    byte[] b2 = {  127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  127,  127,  127,  127 };
+
+    char[] c1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+    char[] c2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+
+    int[] i1 = { -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, -128, -128, -128 };
+    int[] i2 = {  127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  127,  127,  127,  127 };
+
+    short[] s1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MIN_S, MIN_S };
+
+    expectEquals(56516, testDotProdConstRight(b2));
+    expectEquals(56516, testDotProdConstLeft(b2));
+    expectEquals(1271, testDotProdLoopInvariantConvRight(b2, 129));
+    expectEquals(-8519423, testDotProdByteToChar(c1, c2));
+    expectEquals(-8388351, testDotProdMixedSize(b1, s1));
+    expectEquals(-8388351, testDotProdMixedSizeAndSign(b1, c2));
+    expectEquals(-81279, testDotProdInt32(i1, i2));
+    expectEquals(3, testDotProdBothSignedUnsigned1(b1, b2));
+    expectEquals(54403, testDotProdBothSignedUnsigned2(b1, b2));
+    expectEquals(3, testDotProdBothSignedUnsignedDoubleLoad(b1, b2));
+    expectEquals(-262137, testDotProdBothSignedUnsignedChar(c1, c2));
+  }
+
+  public static void main(String[] args) {
+    run();
+  }
+}
diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt
index bbaaedb..8531709 100644
--- a/test/979-const-method-handle/expected.txt
+++ b/test/979-const-method-handle/expected.txt
@@ -7,3 +7,11 @@
 2.718281828459045
 repeatConstMethodHandle()
 Attempting to set Math.E raised IAE
+Quack
+Moo
+Woof
+Test
+Getting field in TestTokenizer raised WMTE (woohoo!)
+Stack: tos was 7
+Stack: capacity was 10
+Stack: capacity is 2
diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java
index 427ca7a..5368a22 100644
--- a/test/979-const-method-handle/src/Main.java
+++ b/test/979-const-method-handle/src/Main.java
@@ -18,6 +18,11 @@
 import annotations.ConstantMethodType;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.util.Stack;
 
 class Main {
     /**
@@ -45,6 +50,12 @@
         private int field;
     }
 
+    private static class TestTokenizer extends StreamTokenizer {
+        public TestTokenizer(String message) {
+            super(new StringReader(message));
+        }
+    }
+
     @ConstantMethodType(
             returnType = String.class,
             parameterTypes = {int.class, Integer.class, System.class})
@@ -136,6 +147,48 @@
         return null;
     }
 
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INSTANCE_GET,
+        owner = "java/io/StreamTokenizer",
+        fieldOrMethodName = "sval",
+        descriptor = "Ljava/lang/String;")
+     private static MethodHandle getSval() {
+        unreachable();
+        return null;
+    }
+
+    // This constant-method-handle references a private instance field. If
+    // referenced in bytecode it raises IAE at load time.
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INSTANCE_PUT,
+        owner = "java/io/StreamTokenizer",
+        fieldOrMethodName = "peekc",
+        descriptor = "I")
+     private static MethodHandle putPeekc() {
+        unreachable();
+        return null;
+    }
+
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+        owner = "java/util/Stack",
+        fieldOrMethodName = "pop",
+        descriptor = "()Ljava/lang/Object;")
+    private static MethodHandle stackPop() {
+        unreachable();
+        return null;
+    }
+
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+        owner = "java/util/Stack",
+        fieldOrMethodName = "trimToSize",
+        descriptor = "()V")
+    private static MethodHandle stackTrim() {
+        unreachable();
+        return null;
+    }
+
     private static void repeatConstMethodHandle() throws Throwable {
         System.out.println("repeatConstMethodHandle()");
         String[] values = {"A", "B", "C"};
@@ -166,5 +219,29 @@
         } catch (IllegalAccessError expected) {
             System.out.println("Attempting to set Math.E raised IAE");
         }
+
+        StreamTokenizer st = new StreamTokenizer(new StringReader("Quack Moo Woof"));
+        while (st.nextToken() != StreamTokenizer.TT_EOF) {
+            System.out.println((String) getSval().invokeExact(st));
+        }
+
+        TestTokenizer tt = new TestTokenizer("Test message 123");
+        tt.nextToken();
+        System.out.println((String) getSval().invoke(tt));
+        try {
+            System.out.println((String) getSval().invokeExact(tt));
+        } catch (WrongMethodTypeException wmte) {
+            System.out.println("Getting field in TestTokenizer raised WMTE (woohoo!)");
+        }
+
+        Stack stack = new Stack();
+        stack.push(Integer.valueOf(3));
+        stack.push(Integer.valueOf(5));
+        stack.push(Integer.valueOf(7));
+        Object tos = stackPop().invokeExact(stack);
+        System.out.println("Stack: tos was " + tos);
+        System.out.println("Stack: capacity was " + stack.capacity());
+        stackTrim().invokeExact(stack);
+        System.out.println("Stack: capacity is " + stack.capacity());
     }
 }
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 4ebe1b8..d5db76a 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -17,7 +17,7 @@
 COMPILE_FLAGS=""
 DALVIKVM="dalvikvm32"
 DEBUGGER="n"
-WITH_AGENT=""
+WITH_AGENT=()
 DEBUGGER_AGENT=""
 WRAP_DEBUGGER_AGENT="n"
 DEV_MODE="n"
@@ -232,7 +232,7 @@
     elif [ "x$1" = "x--with-agent" ]; then
         shift
         USE_JVMTI="y"
-        WITH_AGENT="$1"
+        WITH_AGENT+=("$1")
         shift
     elif [ "x$1" = "x--debug-wrap-agent" ]; then
         WRAP_DEBUGGER_AGENT="y"
@@ -454,9 +454,9 @@
   DEBUGGER_OPTS="-agentpath:${AGENTPATH}=transport=dt_socket,address=$PORT,server=y,suspend=y"
 fi
 
-if [ "x$WITH_AGENT" != "x" ]; then
-  FLAGS="${FLAGS} -agentpath:${WITH_AGENT}"
-fi
+for agent in "${WITH_AGENT[@]}"; do
+  FLAGS="${FLAGS} -agentpath:${agent}"
+done
 
 if [ "$USE_JVMTI" = "y" ]; then
   if [ "$USE_JVM" = "n" ]; then
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 768bc79..f0b88e9 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1082,5 +1082,12 @@
         "tests": ["566-polymorphic-inlining"],
         "variant": "jit & debuggable",
         "description": ["We do not inline with debuggable."]
+    },
+    {
+        "tests": ["135-MirandaDispatch"],
+        "variant": "interp-ac & 32 & host",
+        "env_vars": {"SANITIZE_HOST": "address"},
+        "bug": "b/112993554",
+        "description": ["Timeout with ASan and interp-ac on 32-bit host (x86)."]
     }
 ]
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 84490bf..ad72945 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -1,23 +1,23 @@
 target_config = {
 
-# Configuration syntax:
-#
-#   Required keys: (Use one or more of these)
-#    * golem - specify a golem machine-type to build, e.g. android-armv8
-#              (uses art/tools/golem/build-target.sh)
-#    * make - specify a make target to build, e.g. build-art-host
-#    * run-test - runs the tests in art/test/ directory with testrunner.py,
-#                 specify a list of arguments to pass to testrunner.py
-#
-#   Optional keys: (Use any of these)
-#    * env - Add additional environment variable to the current environment.
-#
-# *** IMPORTANT ***:
-#    This configuration is used by the android build server. Targets must not be renamed
-#    or removed.
-#
+    # Configuration syntax:
+    #
+    #   Required keys: (Use one or more of these)
+    #    * golem - specify a golem machine-type to build, e.g. android-armv8
+    #              (uses art/tools/golem/build-target.sh)
+    #    * make - specify a make target to build, e.g. build-art-host
+    #    * run-test - runs the tests in art/test/ directory with testrunner.py,
+    #                 specify a list of arguments to pass to testrunner.py
+    #
+    #   Optional keys: (Use any of these)
+    #    * env - Add additional environment variable to the current environment.
+    #
+    # *** IMPORTANT ***:
+    #    This configuration is used by the android build server. Targets must not be renamed
+    #    or removed.
+    #
 
-##########################################
+    ##########################################
 
     # General ART configurations.
     # Calls make and testrunner both.
@@ -56,27 +56,26 @@
     },
     'art-gcstress-gcverify': {
         # Do not exercise '--interpreter', '--optimizing', nor '--jit' in this
-        # configuration, as they are covered by the 'art-interpreter-gcstress',
-        # 'art-optimizing-gcstress' and 'art-jit-gcstress' configurations below.
+        # configuration, as they are covered by the
+        # 'art-interpreter-gcstress-gcverify',
+        # 'art-optimizing-gcstress-gcverify' and 'art-jit-gcstress-gcverify'
+        # configurations below.
         'run-test': ['--interp-ac',
                      '--speed-profile',
                      '--gcstress',
                      '--gcverify']
     },
-    # Rename this configuration as 'art-interpreter-gcstress-gcverify' (b/62611253).
-    'art-interpreter-gcstress' : {
+    'art-interpreter-gcstress-gcverify' : {
         'run-test' : ['--interpreter',
                       '--gcstress',
                       '--gcverify']
     },
-    # Rename this configuration as 'art-optimizing-gcstress-gcverify' (b/62611253).
-    'art-optimizing-gcstress' : {
+    'art-optimizing-gcstress-gcverify' : {
         'run-test' : ['--optimizing',
                       '--gcstress',
                       '--gcverify']
     },
-    # Rename this configuration as 'art-jit-gcstress-gcverify' (b/62611253).
-    'art-jit-gcstress' : {
+    'art-jit-gcstress-gcverify' : {
         'run-test' : ['--jit',
                       '--gcstress',
                       '--gcverify']
@@ -86,22 +85,9 @@
                       '--gcstress',
                       '--runtime-option=-Xjitthreshold:0']
     },
-    # TODO: Rename or repurpose this configuration as
-    # 'art-read-barrier-heap-poisoning' (b/62611253).
-    'art-read-barrier' : {
+    'art-read-barrier-heap-poisoning' : {
         'run-test': ['--interpreter',
-                  '--optimizing'],
-        'env' : {
-            'ART_HEAP_POISONING' : 'true'
-        }
-    },
-    # TODO: Remove or disable this configuration, as it is now covered
-    # by 'art-interpreter-gcstress' and 'art-optimizing-gcstress' --
-    # except for heap poisoning, but that's fine (b/62611253).
-    'art-read-barrier-gcstress' : {
-        'run-test' : ['--interpreter',
-                      '--optimizing',
-                      '--gcstress'],
+                     '--optimizing'],
         'env' : {
             'ART_HEAP_POISONING' : 'true'
         }
@@ -122,6 +108,9 @@
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
+    # TODO: Consider removing this configuration when it is no longer used by
+    # any continuous testing target (b/62611253), as the SS collector overlaps
+    # with the CC collector, since both move objects.
     'art-ss-gc' : {
         'run-test' : ['--interpreter',
                       '--optimizing',
@@ -131,6 +120,7 @@
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
+    # TODO: Remove this configuration (b/62611253) when the GSS collector is removed (b/73295078).
     'art-gss-gc' : {
         'run-test' : ['--interpreter',
                       '--optimizing',
@@ -140,6 +130,9 @@
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
+    # TODO: Consider removing this configuration when it is no longer used by
+    # any continuous testing target (b/62611253), as the SS collector overlaps
+    # with the CC collector, since both move objects.
     'art-ss-gc-tlab' : {
         'run-test' : ['--interpreter',
                       '--optimizing',
@@ -150,6 +143,7 @@
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
+    # TODO: Remove this configuration (b/62611253) when the GSS collector is removed (b/73295078).
     'art-gss-gc-tlab' : {
         'run-test' : ['--interpreter',
                       '--optimizing',
@@ -180,12 +174,6 @@
         'run-test' : ['--interpreter',
                       '--no-image']
     },
-    'art-relocate-no-patchoat' : {
-        'run-test' : ['--relocate-npatchoat']
-    },
-    'art-no-dex2oat' : {
-        # Deprecated configuration.
-    },
     'art-heap-poisoning' : {
         'run-test' : ['--interpreter',
                       '--optimizing',
@@ -226,6 +214,9 @@
             'ART_HEAP_POISONING' : 'true'
         }
     },
+    # TODO: Consider removing this configuration when it is no longer used by
+    # any continuous testing target (b/62611253), as the SS collector overlaps
+    # with the CC collector, since both move objects.
     'art-gtest-ss-gc': {
         'make' :  'test-art-host-gtest',
         'env': {
@@ -235,6 +226,7 @@
             'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'none'
         }
     },
+    # TODO: Remove this configuration (b/62611253) when the GSS collector is removed (b/73295078).
     'art-gtest-gss-gc': {
         'make' :  'test-art-host-gtest',
         'env' : {
@@ -242,6 +234,9 @@
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
+    # TODO: Consider removing this configuration when it is no longer used by
+    # any continuous testing target (b/62611253), as the SS collector overlaps
+    # with the CC collector, since both move objects.
     'art-gtest-ss-gc-tlab': {
         'make' :  'test-art-host-gtest',
         'env': {
@@ -250,6 +245,7 @@
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
+    # TODO: Remove this configuration (b/62611253) when the GSS collector is removed (b/73295078).
     'art-gtest-gss-gc-tlab': {
         'make' :  'test-art-host-gtest',
         'env': {
@@ -273,10 +269,10 @@
         }
     },
 
-   # ASAN (host) configurations.
+    # ASAN (host) configurations.
 
-   # These configurations need detect_leaks=0 to work in non-setup environments like build bots,
-   # as our build tools leak. b/37751350
+    # These configurations need detect_leaks=0 to work in non-setup environments like build bots,
+    # as our build tools leak. b/37751350
 
     'art-gtest-asan': {
         'make' : 'test-art-host-gtest',
@@ -306,11 +302,11 @@
         }
     },
 
-   # ART Golem build targets used by go/lem (continuous ART benchmarking),
-   # (art-opt-cc is used by default since it mimics the default preopt config),
-   #
-   # calls golem/build-target.sh which builds a golem tarball of the target name,
-   #     e.g. 'golem: android-armv7' produces an 'android-armv7.tar.gz' upon success.
+    # ART Golem build targets used by go/lem (continuous ART benchmarking),
+    # (art-opt-cc is used by default since it mimics the default preopt config),
+    #
+    # calls golem/build-target.sh which builds a golem tarball of the target name,
+    #     e.g. 'golem: android-armv7' produces an 'android-armv7.tar.gz' upon success.
 
     'art-golem-android-armv7': {
         'golem' : 'android-armv7'
diff --git a/tools/ahat/etc/ahat_api.txt b/tools/ahat/etc/ahat_api.txt
index 7aa994a..c82b314 100644
--- a/tools/ahat/etc/ahat_api.txt
+++ b/tools/ahat/etc/ahat_api.txt
@@ -73,6 +73,7 @@
     method public com.android.ahat.heapdump.AhatInstance getAssociatedBitmapInstance();
     method public com.android.ahat.heapdump.AhatClassObj getAssociatedClassForOverhead();
     method public com.android.ahat.heapdump.AhatInstance getBaseline();
+    method public java.lang.String getBinderProxyInterfaceName();
     method public java.lang.String getClassName();
     method public com.android.ahat.heapdump.AhatClassObj getClassObj();
     method public java.lang.String getDexCacheLocation(int);
diff --git a/tools/ahat/src/main/com/android/ahat/Summarizer.java b/tools/ahat/src/main/com/android/ahat/Summarizer.java
index ab88c04..877ecf4 100644
--- a/tools/ahat/src/main/com/android/ahat/Summarizer.java
+++ b/tools/ahat/src/main/com/android/ahat/Summarizer.java
@@ -112,6 +112,13 @@
       formatted.append(" overhead for ");
       formatted.append(summarize(cls));
     }
+
+    // Annotate BinderProxy with its interface name.
+    String binderInterface = inst.getBinderProxyInterfaceName();
+    if (binderInterface != null) {
+        formatted.appendFormat(" for %s", binderInterface);
+    }
+
     return formatted;
   }
 
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
index 0511798..141bdd9 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
@@ -145,6 +145,21 @@
     return null;
   }
 
+  @Override public String getBinderProxyInterfaceName() {
+    if (isInstanceOfClass("android.os.BinderProxy")) {
+      for (AhatInstance inst : getReverseReferences()) {
+        String className = inst.getClassName();
+        if (className.endsWith("$Stub$Proxy")) {
+          Value value = inst.getField("mRemote");
+          if (value != null && value.asAhatInstance() == this) {
+            return className.substring(0, className.lastIndexOf("$Stub$Proxy"));
+          }
+        }
+      }
+    }
+    return null;
+  }
+
   @Override public AhatInstance getAssociatedBitmapInstance() {
     return getBitmapInfo() == null ? null : this;
   }
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
index c85a057..3aae11e 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
@@ -490,6 +490,17 @@
   }
 
   /**
+   * Returns the name of the Binder proxy interface associated with this object. Only applies to
+   * instances of android.os.BinderProxy. If this is an instance of BinderProxy,
+   * returns the fully qualified binder interface name, otherwise returns null.
+   *
+   * @return the name of the binder interface associated with this object
+   */
+  public String getBinderProxyInterfaceName() {
+    return null;
+  }
+
+  /**
    * Returns the android.graphics.Bitmap instance associated with this object.
    * Instances of android.graphics.Bitmap return themselves. If this is a
    * byte[] array containing pixel data for an instance of
diff --git a/tools/ahat/src/test-dump/DumpedStuff.java b/tools/ahat/src/test-dump/DumpedStuff.java
index 804a3a3..50b8878 100644
--- a/tools/ahat/src/test-dump/DumpedStuff.java
+++ b/tools/ahat/src/test-dump/DumpedStuff.java
@@ -124,6 +124,35 @@
     }
   }
 
+  private static class IDumpedManager {
+    public static class Stub {
+      public static class Proxy {
+        android.os.IBinder mRemote;
+        Proxy(android.os.IBinder binderProxy) {
+          mRemote = binderProxy;
+        }
+      }
+    }
+  }
+
+  private static class IBinderInterfaceImpostor {
+    public static class Stub {
+      public static class Proxy {
+        android.os.IBinder mFakeRemote = new android.os.BinderProxy();
+        Proxy(android.os.IBinder binderProxy) {
+          mFakeRemote = binderProxy;
+        }
+      }
+    }
+  }
+
+  private static class BinderProxyCarrier {
+    android.os.IBinder mRemote;
+    BinderProxyCarrier(android.os.IBinder binderProxy) {
+      mRemote = binderProxy;
+    }
+  }
+
   public String basicString = "hello, world";
   public String nonAscii = "Sigma (Æ©) is not ASCII";
   public String embeddedZero = "embedded\0...";  // Non-ASCII for string compression purposes.
@@ -158,6 +187,12 @@
   public int[] modifiedArray;
   public Object objectAllocatedAtKnownSite;
   public Object objectAllocatedAtKnownSubSite;
+  public android.os.IBinder correctBinderProxy = new android.os.BinderProxy();
+  public android.os.IBinder imposedBinderProxy = new android.os.BinderProxy();
+  public android.os.IBinder carriedBinderProxy = new android.os.BinderProxy();
+  Object correctBinderProxyObject = new IDumpedManager.Stub.Proxy(correctBinderProxy);
+  Object impostorBinderProxyObject = new IBinderInterfaceImpostor.Stub.Proxy(imposedBinderProxy);
+  Object carrierBinderProxyObject = new BinderProxyCarrier(carriedBinderProxy);
 
   // Allocate those objects that we need to not be GC'd before taking the heap
   // dump.
diff --git a/tools/ahat/src/test-dump/android/os/BinderProxy.java b/tools/ahat/src/test-dump/android/os/BinderProxy.java
new file mode 100644
index 0000000..5f35c61
--- /dev/null
+++ b/tools/ahat/src/test-dump/android/os/BinderProxy.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package android.os;
+
+/** Fake android.os.BinderProxy class that does absolutely nothing. */
+public class BinderProxy implements IBinder {}
diff --git a/tools/ahat/src/test-dump/android/os/IBinder.java b/tools/ahat/src/test-dump/android/os/IBinder.java
new file mode 100644
index 0000000..6f01468
--- /dev/null
+++ b/tools/ahat/src/test-dump/android/os/IBinder.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package android.os;
+
+/** Fake android.os.IBinder that means nothing. */
+public interface IBinder {}
diff --git a/tools/ahat/src/test/com/android/ahat/InstanceTest.java b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
index 196eb1e..57aa31f 100644
--- a/tools/ahat/src/test/com/android/ahat/InstanceTest.java
+++ b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
@@ -549,4 +549,18 @@
     // Other kinds of objects should not have associated classes for overhead.
     assertNull(cls.getAssociatedClassForOverhead());
   }
+
+  @Test
+  public void binderProxy() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+
+    AhatInstance correctObj = dump.getDumpedAhatInstance("correctBinderProxy");
+    assertEquals("DumpedStuff$IDumpedManager", correctObj.getBinderProxyInterfaceName());
+
+    AhatInstance imposedObj = dump.getDumpedAhatInstance("imposedBinderProxy");
+    assertNull(imposedObj.getBinderProxyInterfaceName());
+
+    AhatInstance carriedObj = dump.getDumpedAhatInstance("carriedBinderProxy");
+    assertNull(carriedObj.getBinderProxyInterfaceName());
+  }
 }
diff --git a/tools/dexanalyze/dexanalyze_strings.cc b/tools/dexanalyze/dexanalyze_strings.cc
index de5c34e..dcadb59 100644
--- a/tools/dexanalyze/dexanalyze_strings.cc
+++ b/tools/dexanalyze/dexanalyze_strings.cc
@@ -119,8 +119,25 @@
       return false;
     }
     const uint8_t* prefix_data = &dictionary_.prefix_data_[prefix_offset];
-    return memcmp(prefix_data, data, prefix_len) == 0u &&
-        memcmp(suffix_data, data + prefix_len, len - prefix_len) == 0u;
+    if ((true)) {
+      return memcmp(prefix_data, data, prefix_len) == 0u &&
+          memcmp(suffix_data, data + prefix_len, len - prefix_len) == 0u;
+    } else {
+      len -= prefix_len;
+      while (prefix_len != 0u) {
+        if (*prefix_data++ != *data++) {
+          return false;
+        }
+        --prefix_len;
+      }
+      while (len != 0u) {
+        if (*suffix_data++ != *data++) {
+          return false;
+        }
+        --len;
+      }
+      return true;
+    }
   }
 
  public:
@@ -164,7 +181,6 @@
   std::vector<uint32_t> string_offsets_;
 };
 
-
 // Node value = (distance from root) * (occurrences - 1).
 class MatchTrie {
  public:
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 3ef78d5..a5fa332 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -216,16 +216,6 @@
   names: ["libcore.javax.crypto.spec.AlgorithmParametersTestGCM#testEncoding"]
 },
 {
-  description: "Tests fail because mockito can not read android.os.Build$VERSION",
-  result: EXEC_FAILED,
-  bug: 111704422,
-  names: ["libcore.java.lang.ThreadTest#testUncaughtExceptionPreHandler_calledBeforeDefaultHandler",
-          "libcore.java.lang.ThreadTest#testUncaughtExceptionPreHandler_noDefaultHandler",
-          "libcore.javax.crypto.CipherInputStreamTest#testCloseTwice",
-          "libcore.libcore.io.BlockGuardOsTest#test_android_getaddrinfo_networkPolicy",
-          "libcore.libcore.io.BlockGuardOsTest#test_checkNewMethodsInPosix"]
-},
-{
   description: "fdsan doesn't exist on the host",
   result: EXEC_FAILED,
   modes: [host],
@@ -233,6 +223,15 @@
   names: ["libcore.libcore.io.FdsanTest#testFileInputStream",
           "libcore.libcore.io.FdsanTest#testFileOutputStream",
           "libcore.libcore.io.FdsanTest#testRandomAccessFile",
-          "libcore.libcore.io.FdsanTest#testParcelFileDescriptor"]
+          "libcore.libcore.io.FdsanTest#testParcelFileDescriptor",
+          "libcore.libcore.io.FdsanTest#testDatagramSocket",
+          "libcore.libcore.io.FdsanTest#testSocket"]
+},
+{
+  description: "Timeout on heap-poisoning target builds",
+  result: EXEC_FAILED,
+  modes: [device],
+  bug: 116446372,
+  names: ["libcore.libcore.io.FdsanTest#testSocket"]
 }
 ]
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 13f756c..20e5c64 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -311,16 +311,16 @@
   vm_args="$vm_args --vm-arg $plugin"
 fi
 
-# Because we're running debuggable, we discard any AOT code.
-# Therefore we run de2oat with 'quicken' to avoid spending time compiling.
-vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken"
-debuggee_args="$debuggee_args -Xcompiler-option --compiler-filter=quicken"
-
-if $instant_jit; then
-  debuggee_args="$debuggee_args -Xjitthreshold:0"
-fi
-
 if [[ $mode != "ri" ]]; then
+  # Because we're running debuggable, we discard any AOT code.
+  # Therefore we run de2oat with 'quicken' to avoid spending time compiling.
+  vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken"
+  debuggee_args="$debuggee_args -Xcompiler-option --compiler-filter=quicken"
+
+  if $instant_jit; then
+    debuggee_args="$debuggee_args -Xjitthreshold:0"
+  fi
+
   vm_args="$vm_args --vm-arg -Xusejit:$use_jit"
   debuggee_args="$debuggee_args -Xusejit:$use_jit"
 fi
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 04e80df..ef958d6 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -43,7 +43,7 @@
 # Kill logd first, so that when we set the adb buffer size later in this file,
 # it is brought up again.
 echo -e "${green}Killing logd, seen leaking on fugu/N${nc}"
-adb shell killall -9 /system/bin/logd
+adb shell pkill -9 -U logd logd && echo -e "${green}...logd killed${nc}"
 
 # Update date on device if the difference with host is more than one hour.
 if [ $abs_time_difference_in_seconds -gt $seconds_per_hour ]; then
diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md
index bc46882..a0a7dd7 100644
--- a/tools/ti-fast/README.md
+++ b/tools/ti-fast/README.md
@@ -21,6 +21,10 @@
   called. This behavior is static. The no-log methods have no branches and just
   immediately return.
 
+* If 'all' is one of the arguments all events the current runtime is capable of
+  providing will be listened for and all other arguments (excepting 'log') will
+  be ignored.
+
 * The event-names are the same names as are used in the jvmtiEventCallbacks
   struct.
 
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
index b147add..85c433b 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -36,6 +36,13 @@
 // env.
 static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
 
+template <typename ...Args> static void Unused(Args... args ATTRIBUTE_UNUSED) {}
+
+// jthread is a typedef of jobject so we use this to allow the templates to distinguish them.
+struct jthreadContainer { jthread thread; };
+// jlocation is a typedef of jlong so use this to distinguish the less common jlong.
+struct jlongContainer { jlong val; };
+
 static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) {
   switch (event) {
 #define DO_CASE(name, cap_name) \
@@ -63,59 +70,520 @@
 }
 
 // Setup for all supported events. Give a macro with fun(name, event_num, args)
-#define FOR_ALL_SUPPORTED_EVENTS(fun) \
-    fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \
-    fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \
-    fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \
-    fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \
-    fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \
-    fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \
-    fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv*, JNIEnv*, jthread)) \
-    fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv*, JNIEnv*, jthread)) \
-    fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \
-    fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \
-    fun(ClassFileLoadHook, EVENT(CLASS_FILE_LOAD_HOOK), (jvmtiEnv*, JNIEnv*, jclass, jobject, const char*, jobject, jint, const unsigned char*, jint*, unsigned char**)) \
-    fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv*, jmethodID, jint, const void*, jint, const jvmtiAddrLocationMap*, const void*)) \
-    fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv*, jmethodID, const void*)) \
-    fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv*, const char*, const void*, jint)) \
-    fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv*)) \
-    fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \
-    fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \
-    fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv*, JNIEnv*, jthread, jobject, jlong)) \
-    fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv*, JNIEnv*, jthread, jobject, jboolean)) \
-    fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv*, JNIEnv*, jint, const void*, const char*)) \
-    fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv*, JNIEnv*, jthread, jobject, jclass, jlong)) \
-    fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv*)) \
-    fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv*))
+#define FOR_ALL_SUPPORTED_JNI_EVENTS(fun) \
+    fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, jlocation loc), (jvmti, jni, jthreadContainer{.thread = thread}, meth, loc)) \
+    fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth), (jvmti, jni, jthreadContainer{.thread = thread}, meth)) \
+    fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, jboolean jb, jvalue jv), (jvmti, jni, jthreadContainer{.thread = thread}, meth, jb, jv)) \
+    fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, void* v1, void** v2), (jvmti, jni, jthreadContainer{.thread = thread}, meth, v1, v2)) \
+    fun(Exception, EVENT(EXCEPTION), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth1, jlocation loc1, jobject obj, jmethodID meth2, jlocation loc2), (jvmti, jni, jthreadContainer{.thread = thread}, meth1, loc1, obj, meth2, loc2)) \
+    fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, jlocation loc, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, meth, loc, obj)) \
+    fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
+    fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
+    fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass), (jvmti, jni, jthreadContainer{.thread = thread}, klass) ) \
+    fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass), (jvmti, jni, jthreadContainer{.thread = thread}, klass)) \
+    fun(ClassFileLoadHook, EVENT(CLASS_FILE_LOAD_HOOK), (jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, jobject obj1, const char* c1, jobject obj2, jint i1, const unsigned char* c2, jint* ip1, unsigned char** cp1), (jvmti, jni, klass, obj1, c1, obj2, i1, c2, ip1, cp1)) \
+    fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, obj)) \
+    fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, obj)) \
+    fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jlong l1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, jlongContainer{.val = l1})) \
+    fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jboolean b1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, b1)) \
+    fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv* jvmti, JNIEnv* jni, jint i1, const void* cv, const char* cc), (jvmti, jni, i1, cv, cc)) \
+    fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jclass klass, jlong l1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, klass, jlongContainer{.val = l1})) \
 
-#define GENERATE_EMPTY_FUNCTION(name, number, args) \
-    static void JNICALL empty ## name  args { }
+#define FOR_ALL_SUPPORTED_NO_JNI_EVENTS(fun) \
+    fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv* jvmti, jmethodID meth, jint i1, const void* cv1, jint i2, const jvmtiAddrLocationMap* alm, const void* cv2), (jvmti, meth, i1, cv1, i2, alm, cv2)) \
+    fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv* jvmti, jmethodID meth, const void* cv1), (jvmti, meth, cv1)) \
+    fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv* jvmti, const char* cc, const void* cv, jint i1), (jvmti, cc, cv, i1)) \
+    fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv* jvmti), (jvmti)) \
+    fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv* jvmti), (jvmti)) \
+    fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv* jvmti), (jvmti))
+
+#define FOR_ALL_SUPPORTED_EVENTS(fun) \
+    FOR_ALL_SUPPORTED_JNI_EVENTS(fun) \
+    FOR_ALL_SUPPORTED_NO_JNI_EVENTS(fun)
+
+static const jvmtiEvent kAllEvents[] = {
+#define GET_EVENT(a, event, b, c) event,
+FOR_ALL_SUPPORTED_EVENTS(GET_EVENT)
+#undef GET_EVENT
+};
+
+#define GENERATE_EMPTY_FUNCTION(name, number, args, argnames) \
+    static void JNICALL empty ## name  args { Unused argnames ; }
 FOR_ALL_SUPPORTED_EVENTS(GENERATE_EMPTY_FUNCTION)
 #undef GENERATE_EMPTY_FUNCTION
 
 static jvmtiEventCallbacks kEmptyCallbacks {
-#define CREATE_EMPTY_EVENT_CALLBACKS(name, num, args) \
+#define CREATE_EMPTY_EVENT_CALLBACKS(name, num, args, argnames) \
     .name = empty ## name,
   FOR_ALL_SUPPORTED_EVENTS(CREATE_EMPTY_EVENT_CALLBACKS)
 #undef CREATE_EMPTY_EVENT_CALLBACKS
 };
 
-#define GENERATE_LOG_FUNCTION(name, number, args) \
-    static void JNICALL log ## name  args { \
-      LOG(INFO) << "Got event " << #name ; \
+static void DeleteLocalRef(JNIEnv* env, jobject obj) {
+  if (obj != nullptr && env != nullptr) {
+    env->DeleteLocalRef(obj);
+  }
+}
+
+class ScopedThreadInfo {
+ public:
+  ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread)
+      : jvmtienv_(jvmtienv), env_(env), free_name_(false) {
+    if (thread == nullptr) {
+      info_.name = const_cast<char*>("<NULLPTR>");
+    } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) {
+      info_.name = const_cast<char*>("<UNKNOWN THREAD>");
+    } else {
+      free_name_ = true;
     }
-FOR_ALL_SUPPORTED_EVENTS(GENERATE_LOG_FUNCTION)
+  }
+
+  ~ScopedThreadInfo() {
+    if (free_name_) {
+      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name));
+    }
+    DeleteLocalRef(env_, info_.thread_group);
+    DeleteLocalRef(env_, info_.context_class_loader);
+  }
+
+  const char* GetName() const {
+    return info_.name;
+  }
+
+ private:
+  jvmtiEnv* jvmtienv_;
+  JNIEnv* env_;
+  bool free_name_;
+  jvmtiThreadInfo info_{};
+};
+
+class ScopedClassInfo {
+ public:
+  ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c) : jvmtienv_(jvmtienv), class_(c) {}
+
+  ~ScopedClassInfo() {
+    if (class_ != nullptr) {
+      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
+      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
+      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(file_));
+      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_));
+    }
+  }
+
+  bool Init(bool get_generic = true) {
+    if (class_ == nullptr) {
+      name_ = const_cast<char*>("<NONE>");
+      generic_ = const_cast<char*>("<NONE>");
+      return true;
+    } else {
+      jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_);
+      jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_);
+      char** gen_ptr = &generic_;
+      if (!get_generic) {
+        generic_ = nullptr;
+        gen_ptr = nullptr;
+      }
+      return jvmtienv_->GetClassSignature(class_, &name_, gen_ptr) == JVMTI_ERROR_NONE &&
+          ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
+          ret1 != JVMTI_ERROR_INVALID_CLASS &&
+          ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
+          ret2 != JVMTI_ERROR_INVALID_CLASS;
+    }
+  }
+
+  jclass GetClass() const {
+    return class_;
+  }
+
+  const char* GetName() const {
+    return name_;
+  }
+
+  const char* GetGeneric() const {
+    return generic_;
+  }
+
+  const char* GetSourceDebugExtension() const {
+    if (debug_ext_ == nullptr) {
+      return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>";
+    } else {
+      return debug_ext_;
+    }
+  }
+  const char* GetSourceFileName() const {
+    if (file_ == nullptr) {
+      return "<UNKNOWN_FILE>";
+    } else {
+      return file_;
+    }
+  }
+
+ private:
+  jvmtiEnv* jvmtienv_;
+  jclass class_;
+  char* name_ = nullptr;
+  char* generic_ = nullptr;
+  char* file_ = nullptr;
+  char* debug_ext_ = nullptr;
+
+  friend std::ostream& operator<<(std::ostream &os, ScopedClassInfo const& m);
+};
+
+class ScopedMethodInfo {
+ public:
+  ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m)
+      : jvmtienv_(jvmtienv), env_(env), method_(m) {}
+
+  ~ScopedMethodInfo() {
+    DeleteLocalRef(env_, declaring_class_);
+    jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
+    jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_));
+    jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
+  }
+
+  bool Init(bool get_generic = true) {
+    if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) {
+      return false;
+    }
+    class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
+    jint nlines;
+    jvmtiLineNumberEntry* lines;
+    jvmtiError err = jvmtienv_->GetLineNumberTable(method_, &nlines, &lines);
+    if (err == JVMTI_ERROR_NONE) {
+      if (nlines > 0) {
+        first_line_ = lines[0].line_number;
+      }
+      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(lines));
+    } else if (err != JVMTI_ERROR_ABSENT_INFORMATION &&
+               err != JVMTI_ERROR_NATIVE_METHOD) {
+      return false;
+    }
+    return class_info_->Init(get_generic) &&
+        (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE);
+  }
+
+  const ScopedClassInfo& GetDeclaringClassInfo() const {
+    return *class_info_;
+  }
+
+  jclass GetDeclaringClass() const {
+    return declaring_class_;
+  }
+
+  const char* GetName() const {
+    return name_;
+  }
+
+  const char* GetSignature() const {
+    return signature_;
+  }
+
+  const char* GetGeneric() const {
+    return generic_;
+  }
+
+  jint GetFirstLine() const {
+    return first_line_;
+  }
+
+ private:
+  jvmtiEnv* jvmtienv_;
+  JNIEnv* env_;
+  jmethodID method_;
+  jclass declaring_class_ = nullptr;
+  std::unique_ptr<ScopedClassInfo> class_info_;
+  char* name_ = nullptr;
+  char* signature_ = nullptr;
+  char* generic_ = nullptr;
+  jint first_line_ = -1;
+
+  friend std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m);
+};
+
+std::ostream& operator<<(std::ostream &os, ScopedClassInfo const& c) {
+  const char* generic = c.GetGeneric();
+  if (generic != nullptr) {
+    return os << c.GetName() << "<" << generic << ">" << " file: " << c.GetSourceFileName();
+  } else {
+    return os << c.GetName() << " file: " << c.GetSourceFileName();
+  }
+}
+
+std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m) {
+  return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature()
+            << " (source: " << m.GetDeclaringClassInfo().GetSourceFileName() << ":"
+            << m.GetFirstLine() << ")";
+}
+
+
+class LogPrinter {
+ public:
+  explicit LogPrinter(jvmtiEvent event) : event_(event) {}
+
+  template <typename ...Args> void PrintRestNoJNI(jvmtiEnv* jvmti, Args... args) {
+    PrintRest(jvmti, static_cast<JNIEnv*>(nullptr), args...);
+  }
+
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti, JNIEnv* env, Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jlongContainer l,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jthreadContainer thr,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jboolean i,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jint i,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jclass klass,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jmethodID meth,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jlocation loc,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jint* ip,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             const void* loc,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             void* loc,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             void** loc,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             unsigned char** v,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             const unsigned char* v,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             const char* v,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             const jvmtiAddrLocationMap* v,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jvalue v,
+                                             Args... args);
+  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
+                                             JNIEnv* env,
+                                             jobject v,
+                                             Args... args);
+
+  std::string GetResult() {
+    std::string out_str = stream.str();
+    return start_args + out_str;
+  }
+
+ private:
+  jvmtiEvent event_;
+  std::string start_args;
+  std::ostringstream stream;
+};
+
+// Base case
+template<> void LogPrinter::PrintRest(jvmtiEnv* jvmti ATTRIBUTE_UNUSED, JNIEnv* jni) {
+  if (jni == nullptr) {
+    start_args = "jvmtiEnv*";
+  } else {
+    start_args = "jvmtiEnv*, JNIEnv*";
+  }
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti,
+                           JNIEnv* jni,
+                           const jvmtiAddrLocationMap* v,
+                           Args... args) {
+  if (v != nullptr) {
+    stream << ", const jvmtiAddrLocationMap*[start_address: "
+           << v->start_address << ", location: " << v->location << "]";
+  } else {
+    stream << ", const jvmtiAddrLocationMap*[nullptr]";
+  }
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jint* v, Args... args) {
+  stream << ", jint*[" << static_cast<const void*>(v) << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, const void* v, Args... args) {
+  stream << ", const void*[" << v << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, unsigned char** v, Args... args) {
+  stream << ", unsigned char**[" << static_cast<const void*>(v) << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, const unsigned char* v, Args... args) {
+  stream << ", const unsigned char*[" << static_cast<const void*>(v) << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, const char* v, Args... args) {
+  stream << ", const char*[" << v << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jvalue v ATTRIBUTE_UNUSED, Args... args) {
+  stream << ", jvalue[<UNION>]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, void** v, Args... args) {
+  stream << ", void**[" << v << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, void* v, Args... args) {
+  stream << ", void*[" << v << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jlongContainer l, Args... args) {
+  stream << ", jlong[" << l.val << ", hex: 0x" << std::hex << l.val << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jlocation l, Args... args) {
+  stream << ", jlocation[" << l << ", hex: 0x" << std::hex << l << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jboolean b, Args... args) {
+  stream << ", jboolean[" << (b ? "true" : "false") << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jint i, Args... args) {
+  stream << ", jint[" << i << ", hex: 0x" << std::hex << i << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jobject obj, Args... args) {
+  if (obj == nullptr) {
+    stream << ", jobject[nullptr]";
+  } else {
+    jni->PushLocalFrame(1);
+    jclass klass = jni->GetObjectClass(obj);
+    ScopedClassInfo sci(jvmti, klass);
+    if (sci.Init(event_ != JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+      stream << ", jobject[type: " << sci << "]";
+    } else {
+      stream << ", jobject[type: TYPE UNKNOWN]";
+    }
+    jni->PopLocalFrame(nullptr);
+  }
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jthreadContainer thr, Args... args) {
+  ScopedThreadInfo sti(jvmti, jni, thr.thread);
+  stream << ", jthread[" << sti.GetName() << "]";
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, Args... args) {
+  ScopedClassInfo sci(jvmti, klass);
+  if (sci.Init(/*get_generic*/event_ != JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+    stream << ", jclass[" << sci << "]";
+  } else {
+    stream << ", jclass[TYPE UNKNOWN]";
+  }
+  PrintRest(jvmti, jni, args...);
+}
+
+template<typename ...Args>
+void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jmethodID meth, Args... args) {
+  ScopedMethodInfo smi(jvmti, jni, meth);
+  if (smi.Init()) {
+    stream << ", jmethodID[" << smi << "]";
+  } else {
+    stream << ", jmethodID[METHOD UNKNOWN]";
+  }
+  PrintRest(jvmti, jni, args...);
+}
+
+#define GENERATE_LOG_FUNCTION_JNI(name, event, args, argnames) \
+    static void JNICALL log ## name  args { \
+      LogPrinter printer(event); \
+      printer.PrintRest argnames; \
+      LOG(INFO) << "Got event " << #name << "(" << printer.GetResult() << ")"; \
+    } \
+
+#define GENERATE_LOG_FUNCTION_NO_JNI(name, event, args, argnames) \
+    static void JNICALL log ## name  args { \
+      LogPrinter printer(event); \
+      printer.PrintRestNoJNI argnames; \
+      LOG(INFO) << "Got event " << #name << "(" << printer.GetResult() << ")"; \
+    } \
+
+FOR_ALL_SUPPORTED_JNI_EVENTS(GENERATE_LOG_FUNCTION_JNI)
+FOR_ALL_SUPPORTED_NO_JNI_EVENTS(GENERATE_LOG_FUNCTION_NO_JNI)
 #undef GENERATE_LOG_FUNCTION
 
 static jvmtiEventCallbacks kLogCallbacks {
-#define CREATE_LOG_EVENT_CALLBACK(name, num, args) \
+#define CREATE_LOG_EVENT_CALLBACK(name, num, args, argnames) \
     .name = log ## name,
   FOR_ALL_SUPPORTED_EVENTS(CREATE_LOG_EVENT_CALLBACK)
 #undef CREATE_LOG_EVENT_CALLBACK
 };
 
+static std::string EventToName(jvmtiEvent desired_event) {
+#define CHECK_NAME(name, event, args, argnames) \
+  if (desired_event == event) { \
+    return #name; \
+  }
+  FOR_ALL_SUPPORTED_EVENTS(CHECK_NAME);
+  LOG(FATAL) << "Unknown event " << desired_event;
+  __builtin_unreachable();
+#undef CHECK_NAME
+}
 static jvmtiEvent NameToEvent(const std::string& desired_name) {
-#define CHECK_NAME(name, event, args) \
+#define CHECK_NAME(name, event, args, argnames) \
   if (desired_name == #name) { \
     return event; \
   }
@@ -125,14 +593,46 @@
 #undef CHECK_NAME
 }
 
+#undef FOR_ALL_SUPPORTED_JNI_EVENTS
+#undef FOR_ALL_SUPPORTED_NO_JNI_EVENTS
 #undef FOR_ALL_SUPPORTED_EVENTS
-static std::vector<jvmtiEvent> GetRequestedEventList(const std::string& args) {
+
+static std::vector<jvmtiEvent> GetAllAvailableEvents(jvmtiEnv* jvmti) {
+  std::vector<jvmtiEvent> out;
+  jvmtiCapabilities caps{};
+  jvmti->GetPotentialCapabilities(&caps);
+  uint8_t caps_bytes[sizeof(caps)];
+  memcpy(caps_bytes, &caps, sizeof(caps));
+  for (jvmtiEvent e : kAllEvents) {
+    jvmtiCapabilities req{};
+    AddCapsForEvent(e, &req);
+    uint8_t req_bytes[sizeof(req)];
+    memcpy(req_bytes, &req, sizeof(req));
+    bool good = true;
+    for (size_t i = 0; i < sizeof(caps); i++) {
+      if ((req_bytes[i] & caps_bytes[i]) != req_bytes[i]) {
+        good = false;
+        break;
+      }
+    }
+    if (good) {
+      out.push_back(e);
+    } else {
+      LOG(WARNING) << "Unable to get capabilities for event " << EventToName(e);
+    }
+  }
+  return out;
+}
+
+static std::vector<jvmtiEvent> GetRequestedEventList(jvmtiEnv* jvmti, const std::string& args) {
   std::vector<jvmtiEvent> res;
   std::stringstream args_stream(args);
   std::string item;
   while (std::getline(args_stream, item, ',')) {
     if (item == "") {
       continue;
+    } else if (item == "all") {
+      return GetAllAvailableEvents(jvmti);
     }
     res.push_back(NameToEvent(item));
   }
@@ -168,12 +668,17 @@
     args = args.substr(3);
   }
 
-  std::vector<jvmtiEvent> events = GetRequestedEventList(args);
+  std::vector<jvmtiEvent> events = GetRequestedEventList(jvmti, args);
 
   jvmtiCapabilities caps{};
   for (jvmtiEvent e : events) {
     AddCapsForEvent(e, &caps);
   }
+  if (is_log) {
+    caps.can_get_line_numbers = 1;
+    caps.can_get_source_file_name = 1;
+    caps.can_get_source_debug_extension = 1;
+  }
   error = jvmti->AddCapabilities(&caps);
   if (error != JVMTI_ERROR_NONE) {
     LOG(ERROR) << "Unable to set caps";
diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk
index 2faa577..c0b5ca1 100644
--- a/tools/veridex/Android.mk
+++ b/tools/veridex/Android.mk
@@ -31,6 +31,7 @@
 	$(transform-classes.jar-to-dex)
 
 app_compat_lists := \
+  $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \
   $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
   $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
   $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
@@ -46,22 +47,30 @@
 
 $(VERIDEX_FILES_PATH): PRIVATE_VERIDEX_FILES := $(VERIDEX_FILES)
 $(VERIDEX_FILES_PATH): PRIVATE_APP_COMPAT_LISTS := $(app_compat_lists)
+$(VERIDEX_FILES_PATH): PRIVATE_SYSTEM_STUBS_DEX_DIR := $(dir $(system_stub_dex))
 $(VERIDEX_FILES_PATH): PRIVATE_SYSTEM_STUBS_ZIP := $(dir $(VERIDEX_FILES_PATH))/system-stubs.zip
+$(VERIDEX_FILES_PATH): PRIVATE_OAHL_STUBS_DEX_DIR := $(dir $(oahl_stub_dex))
 $(VERIDEX_FILES_PATH): PRIVATE_OAHL_STUBS_ZIP := $(dir $(VERIDEX_FILES_PATH))/org.apache.http.legacy-stubs.zip
 $(VERIDEX_FILES_PATH) : $(SOONG_ZIP) $(VERIDEX_FILES) $(app_compat_lists) $(HOST_OUT_EXECUTABLES)/veridex $(system_stub_dex) $(oahl_stub_dex)
-	$(hide) rm -f $(PRIVATE_SYSTEM_STUBS_ZIP) $(PRIVATE_OAHL_STUBS_ZIP)
-	$(hide) zip -j $(PRIVATE_SYSTEM_STUBS_ZIP) $(dir $(system_stub_dex))/classes*.dex
-	$(hide) zip -j $(PRIVATE_OAHL_STUBS_ZIP) $(dir $(oahl_stub_dex))/classes*.dex
-	$(hide) $(SOONG_ZIP) -o $@ -C art/tools/veridex -f $(PRIVATE_VERIDEX_FILES) \
-                             -C $(dir $(lastword $(PRIVATE_APP_COMPAT_LISTS))) $(addprefix -f , $(PRIVATE_APP_COMPAT_LISTS)) \
-                             -C $(HOST_OUT_EXECUTABLES) -f $(HOST_OUT_EXECUTABLES)/veridex \
-                             -C $(dir $(PRIVATE_SYSTEM_STUBS_ZIP)) -f $(PRIVATE_SYSTEM_STUBS_ZIP) \
-                             -C $(dir $(PRIVATE_OAHL_STUBS_ZIP)) -f $(PRIVATE_OAHL_STUBS_ZIP)
-	$(hide) rm -f $(PRIVATE_SYSTEM_STUBS_ZIP)
-	$(hide) rm -f $(PRIVATE_OAHL_STUBS_ZIP)
+	rm -rf $(dir $@)/*
+	ls -1 $(PRIVATE_SYSTEM_STUBS_DEX_DIR)/classes*.dex | sort >$(PRIVATE_SYSTEM_STUBS_ZIP).list
+	$(SOONG_ZIP) -o $(PRIVATE_SYSTEM_STUBS_ZIP) -C $(PRIVATE_SYSTEM_STUBS_DEX_DIR) -l $(PRIVATE_SYSTEM_STUBS_ZIP).list
+	rm $(PRIVATE_SYSTEM_STUBS_ZIP).list
+	ls -1 $(PRIVATE_OAHL_STUBS_DEX_DIR)/classes*.dex | sort >$(PRIVATE_OAHL_STUBS_ZIP).list
+	$(SOONG_ZIP) -o $(PRIVATE_OAHL_STUBS_ZIP) -C $(PRIVATE_OAHL_STUBS_DEX_DIR) -l $(PRIVATE_OAHL_STUBS_ZIP).list
+	rm $(PRIVATE_OAHL_STUBS_ZIP).list
+	$(SOONG_ZIP) -o $@ -C art/tools/veridex -f $(PRIVATE_VERIDEX_FILES) \
+                     -C $(dir $(lastword $(PRIVATE_APP_COMPAT_LISTS))) $(addprefix -f , $(PRIVATE_APP_COMPAT_LISTS)) \
+                     -C $(HOST_OUT_EXECUTABLES) -f $(HOST_OUT_EXECUTABLES)/veridex \
+                     -C $(dir $(PRIVATE_SYSTEM_STUBS_ZIP)) -f $(PRIVATE_SYSTEM_STUBS_ZIP) \
+                     -C $(dir $(PRIVATE_OAHL_STUBS_ZIP)) -f $(PRIVATE_OAHL_STUBS_ZIP)
+	rm -f $(PRIVATE_SYSTEM_STUBS_ZIP)
+	rm -f $(PRIVATE_OAHL_STUBS_ZIP)
 
 # Make the zip file available for prebuilts.
 $(call dist-for-goals,sdk,$(VERIDEX_FILES_PATH))
 
 VERIDEX_FILES :=
 app_compat_lists :=
+system_stub_dex :=
+oahl_stub_dex :=
diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh
index e7b735d..f57c8a4 100755
--- a/tools/veridex/appcompat.sh
+++ b/tools/veridex/appcompat.sh
@@ -22,6 +22,7 @@
 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 
 if [[ -e ${SCRIPT_DIR}/veridex && \
+      -e ${SCRIPT_DIR}/hiddenapi-whitelist.txt && \
       -e ${SCRIPT_DIR}/hiddenapi-blacklist.txt && \
       -e ${SCRIPT_DIR}/hiddenapi-light-greylist.txt && \
       -e ${SCRIPT_DIR}/hiddenapi-dark-greylist.txt && \
@@ -29,6 +30,7 @@
       -e ${SCRIPT_DIR}/system-stubs.zip ]]; then
   exec ${SCRIPT_DIR}/veridex \
     --core-stubs=${SCRIPT_DIR}/system-stubs.zip:${SCRIPT_DIR}/org.apache.http.legacy-stubs.zip \
+    --whitelist=${SCRIPT_DIR}/hiddenapi-whitelist.txt \
     --blacklist=${SCRIPT_DIR}/hiddenapi-blacklist.txt \
     --light-greylist=${SCRIPT_DIR}/hiddenapi-light-greylist.txt \
     --dark-greylist=${SCRIPT_DIR}/hiddenapi-dark-greylist.txt \
@@ -43,8 +45,8 @@
 fi
 
 # Logic for setting out_dir from build/make/core/envsetup.mk:
-if [[ -z $OUT_DIR ]]; then
-  if [[ -z $OUT_DIR_COMMON_BASE ]]; then
+if [[ -z "${OUT_DIR}" ]]; then
+  if [[ -z "${OUT_DIR_COMMON_BASE}" ]]; then
     OUT=out
   else
     OUT=${OUT_DIR_COMMON_BASE}/${PWD##*/}
@@ -53,15 +55,18 @@
   OUT=${OUT_DIR}
 fi
 
-PACKAGING=${OUT}/target/common/obj/PACKAGING
+if [[ -z "${PACKAGING}" ]]; then
+  PACKAGING=${OUT}/target/common/obj/PACKAGING
+fi
 
-if [ -z "$ANDROID_HOST_OUT" ] ; then
+if [[ -z "${ANDROID_HOST_OUT}" ]]; then
   ANDROID_HOST_OUT=${OUT}/host/linux-x86
 fi
 
 
 ${ANDROID_HOST_OUT}/bin/veridex \
     --core-stubs=${PACKAGING}/core_dex_intermediates/classes.dex:${PACKAGING}/oahl_dex_intermediates/classes.dex \
+    --whitelist=${PACKAGING}/hiddenapi-whitelist.txt \
     --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \
     --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \
     --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index b1c8559..68485bd 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -33,10 +33,14 @@
  */
 class HiddenApi {
  public:
-  HiddenApi(const char* blacklist, const char* dark_greylist, const char* light_greylist) {
+  HiddenApi(const char* whitelist,
+            const char* blacklist,
+            const char* dark_greylist,
+            const char* light_greylist) {
     FillList(light_greylist, light_greylist_);
     FillList(dark_greylist, dark_greylist_);
     FillList(blacklist, blacklist_);
+    FillList(whitelist, whitelist_);
   }
 
   HiddenApiAccessFlags::ApiList GetApiList(const std::string& name) const {
@@ -46,13 +50,15 @@
       return HiddenApiAccessFlags::kDarkGreylist;
     } else if (IsInList(name, light_greylist_)) {
       return HiddenApiAccessFlags::kLightGreylist;
-    } else {
+    } else if (IsInList(name, whitelist_)) {
       return HiddenApiAccessFlags::kWhitelist;
+    } else {
+      return HiddenApiAccessFlags::kNoList;
     }
   }
 
-  bool IsInRestrictionList(const std::string& name) const {
-    return GetApiList(name) != HiddenApiAccessFlags::kWhitelist;
+  bool IsInAnyList(const std::string& name) const {
+    return GetApiList(name) != HiddenApiAccessFlags::kNoList;
   }
 
   static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index);
@@ -76,6 +82,7 @@
 
   static void FillList(const char* filename, std::set<std::string>& entries);
 
+  std::set<std::string> whitelist_;
   std::set<std::string> blacklist_;
   std::set<std::string> light_greylist_;
   std::set<std::string> dark_greylist_;
@@ -85,7 +92,7 @@
   uint32_t count = 0;
   uint32_t reflection_count = 0;
   uint32_t linking_count = 0;
-  uint32_t api_counts[4] = { 0, 0, 0, 0 };
+  uint32_t api_counts[5] = { 0, 0, 0, 0, 0 };
 };
 
 }  // namespace art
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
index d81f133..9e2f936 100644
--- a/tools/veridex/hidden_api_finder.cc
+++ b/tools/veridex/hidden_api_finder.cc
@@ -35,7 +35,7 @@
   // Note: we always query whether a method is in a list, as the app
   // might define blacklisted APIs (which won't be used at runtime).
   std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id);
-  if (hidden_api_.IsInRestrictionList(name)) {
+  if (hidden_api_.IsInAnyList(name)) {
     method_locations_[name].push_back(ref);
   }
 }
@@ -46,7 +46,7 @@
   // Note: we always query whether a field is in a list, as the app
   // might define blacklisted APIs (which won't be used at runtime).
   std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id);
-  if (hidden_api_.IsInRestrictionList(name)) {
+  if (hidden_api_.IsInAnyList(name)) {
     field_locations_[name].push_back(ref);
   }
 }
@@ -57,7 +57,7 @@
   // types can lead to being used through reflection.
   for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
     std::string name(dex_file.StringByTypeIdx(dex::TypeIndex(i)));
-    if (hidden_api_.IsInRestrictionList(name)) {
+    if (hidden_api_.IsInAnyList(name)) {
       classes_.insert(name);
     }
   }
@@ -81,9 +81,9 @@
               // private methods and fields in them.
               // We don't add class names to the `strings_` set as we know method/field names
               // don't have '.' or '/'. All hidden API class names have a '/'.
-              if (hidden_api_.IsInRestrictionList(str)) {
+              if (hidden_api_.IsInAnyList(str)) {
                 classes_.insert(str);
-              } else if (hidden_api_.IsInRestrictionList(name)) {
+              } else if (hidden_api_.IsInAnyList(name)) {
                 // Could be something passed to JNI.
                 classes_.insert(name);
               } else {
@@ -210,7 +210,7 @@
         std::string full_name = cls + "->" + name;
         HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
         stats->api_counts[api_list]++;
-        if (api_list != HiddenApiAccessFlags::kWhitelist) {
+        if (api_list != HiddenApiAccessFlags::kNoList) {
           stats->reflection_count++;
           os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
              << " potential use(s):";
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
index 445221e..08ac6d7 100644
--- a/tools/veridex/precise_hidden_api_finder.cc
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -92,7 +92,7 @@
       std::string name(info.name.ToString());
       std::string full_name = cls + "->" + name;
       HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
-      if (api_list != HiddenApiAccessFlags::kWhitelist) {
+      if (api_list != HiddenApiAccessFlags::kNoList) {
         named_uses[full_name].push_back(ref);
       }
     }
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index 1d3a4fb..7206c7d 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -65,14 +65,26 @@
 VeriMethod VeriClass::loadClass_ = nullptr;
 VeriField VeriClass::sdkInt_ = nullptr;
 
+static const char* kDexFileOption = "--dex-file=";
+static const char* kStubsOption = "--core-stubs=";
+static const char* kWhitelistOption = "--whitelist=";
+static const char* kBlacklistOption = "--blacklist=";
+static const char* kDarkGreylistOption = "--dark-greylist=";
+static const char* kLightGreylistOption = "--light-greylist=";
+static const char* kImprecise = "--imprecise";
+static const char* kTargetSdkVersion = "--target-sdk-version=";
+static const char* kOnlyReportSdkUses = "--only-report-sdk-uses";
+
 struct VeridexOptions {
   const char* dex_file = nullptr;
   const char* core_stubs = nullptr;
+  const char* whitelist = nullptr;
   const char* blacklist = nullptr;
   const char* light_greylist = nullptr;
   const char* dark_greylist = nullptr;
   bool precise = true;
   int target_sdk_version = 28; /* P */
+  bool only_report_sdk_uses = false;
 };
 
 static const char* Substr(const char* str, int index) {
@@ -88,19 +100,13 @@
   argv++;
   argc--;
 
-  static const char* kDexFileOption = "--dex-file=";
-  static const char* kStubsOption = "--core-stubs=";
-  static const char* kBlacklistOption = "--blacklist=";
-  static const char* kDarkGreylistOption = "--dark-greylist=";
-  static const char* kLightGreylistOption = "--light-greylist=";
-  static const char* kImprecise = "--imprecise";
-  static const char* kTargetSdkVersion = "--target-sdk-version=";
-
   for (int i = 0; i < argc; ++i) {
     if (StartsWith(argv[i], kDexFileOption)) {
       options->dex_file = Substr(argv[i], strlen(kDexFileOption));
     } else if (StartsWith(argv[i], kStubsOption)) {
       options->core_stubs = Substr(argv[i], strlen(kStubsOption));
+    } else if (StartsWith(argv[i], kWhitelistOption)) {
+      options->whitelist = Substr(argv[i], strlen(kWhitelistOption));
     } else if (StartsWith(argv[i], kBlacklistOption)) {
       options->blacklist = Substr(argv[i], strlen(kBlacklistOption));
     } else if (StartsWith(argv[i], kDarkGreylistOption)) {
@@ -111,6 +117,8 @@
       options->precise = false;
     } else if (StartsWith(argv[i], kTargetSdkVersion)) {
       options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion)));
+    } else if (strcmp(argv[i], kOnlyReportSdkUses) == 0) {
+      options->only_report_sdk_uses = true;
     }
   }
 }
@@ -130,6 +138,12 @@
   static int Run(int argc, char** argv) {
     VeridexOptions options;
     ParseArgs(&options, argc, argv);
+
+    if (!options.dex_file) {
+      LOG(ERROR) << "Required argument '" << kDexFileOption << "' not provided.";
+      return 1;
+    }
+
     gTargetSdkVersion = options.target_sdk_version;
 
     std::vector<std::string> boot_content;
@@ -215,8 +229,20 @@
     std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
 
+    if (options.only_report_sdk_uses) {
+      // If we only need to report SDK uses, clear out any of the other lists so that
+      // the analysis don't report them.
+      options.blacklist = nullptr;
+      options.dark_greylist = nullptr;
+      options.light_greylist = nullptr;
+    } else {
+      // Otherwise, omit SDK uses.
+      options.whitelist = nullptr;
+    }
+
     // Find and log uses of hidden APIs.
-    HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
+    HiddenApi hidden_api(
+        options.whitelist, options.blacklist, options.dark_greylist, options.light_greylist);
     HiddenApiStats stats;
 
     HiddenApiFinder api_finder(hidden_api);
@@ -229,7 +255,7 @@
       precise_api_finder.Dump(std::cout, &stats);
     }
 
-    DumpSummaryStats(std::cout, stats);
+    DumpSummaryStats(std::cout, stats, options);
 
     if (options.precise) {
       std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
@@ -240,17 +266,23 @@
   }
 
  private:
-  static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) {
+  static void DumpSummaryStats(std::ostream& os,
+                               const HiddenApiStats& stats,
+                               const VeridexOptions& options) {
     static const char* kPrefix = "       ";
-    os << stats.count << " hidden API(s) used: "
-       << stats.linking_count << " linked against, "
-       << stats.reflection_count << " through reflection" << std::endl;
-    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist]
-       << " in blacklist" << std::endl;
-    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist]
-       << " in dark greylist" << std::endl;
-    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist]
-       << " in light greylist" << std::endl;
+    if (options.only_report_sdk_uses) {
+      os << stats.api_counts[HiddenApiAccessFlags::kWhitelist] << " SDK API uses." << std::endl;
+    } else {
+      os << stats.count << " hidden API(s) used: "
+         << stats.linking_count << " linked against, "
+         << stats.reflection_count << " through reflection" << std::endl;
+      os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist]
+         << " in blacklist" << std::endl;
+      os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist]
+         << " in dark greylist" << std::endl;
+      os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist]
+         << " in light greylist" << std::endl;
+    }
   }
 
   static bool Load(const std::string& filename,
@@ -312,4 +344,3 @@
 int main(int argc, char** argv) {
   return art::Veridex::Run(argc, argv);
 }
-