Merge "ARM(64): Implement the isInfinite intrinsics"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index cd463ec..3b459c3 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -650,11 +650,11 @@
 
 ifeq ($(ART_BUILD_TARGET),true)
   $(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),,libbacktrace)))
-  $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler libbacktrace)))
+  $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler libbacktrace libnativeloader)))
 endif
 ifeq ($(ART_BUILD_HOST),true)
   $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),,libbacktrace)))
-  $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler libbacktrace)))
+  $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler libbacktrace libnativeloader)))
 endif
 
 # Used outside the art project to get a list of the current tests
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 81b854e..7c53e01 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -461,8 +461,8 @@
   * Test successes
   */
   {
-    EXPECT_SINGLE_PARSE_VALUE(true, "-Xusejit:true", M::UseJIT);
-    EXPECT_SINGLE_PARSE_VALUE(false, "-Xusejit:false", M::UseJIT);
+    EXPECT_SINGLE_PARSE_VALUE(true, "-Xusejit:true", M::UseJitCompilation);
+    EXPECT_SINGLE_PARSE_VALUE(false, "-Xusejit:false", M::UseJitCompilation);
   }
   {
     EXPECT_SINGLE_PARSE_VALUE(
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index c0a00cc..4797540 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -620,6 +620,8 @@
         log_verbosity.verifier = true;
       } else if (verbose_options[j] == "image") {
         log_verbosity.image = true;
+      } else if (verbose_options[j] == "systrace-locks") {
+        log_verbosity.systrace_lock_logging = true;
       } else {
         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
       }
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index f75a252..bf29e1c 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -180,6 +180,7 @@
                                             isa,
                                             instruction_set_features_.get(),
                                             /* boot_image */ true,
+                                            /* app_image */ false,
                                             GetImageClasses(),
                                             GetCompiledClasses(),
                                             GetCompiledMethods(),
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 1491a18..606302b 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -60,7 +60,7 @@
     // TODO: Investigate why are we doing the work again for this method and try to avoid it.
     LOG(WARNING) << "Method processed more than once: "
         << PrettyMethod(ref.dex_method_index, *ref.dex_file);
-    if (!Runtime::Current()->UseJit()) {
+    if (!Runtime::Current()->UseJitCompilation()) {
       DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
       DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
     }
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 5c0253c..bace014 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -54,7 +54,8 @@
     }
 
     // Only need dequicken info for JIT so far.
-    if (Runtime::Current()->UseJit() && !verified_method->GenerateDequickenMap(method_verifier)) {
+    if (Runtime::Current()->UseJitCompilation() &&
+        !verified_method->GenerateDequickenMap(method_verifier)) {
       return nullptr;
     }
   }
@@ -72,7 +73,7 @@
 }
 
 const DexFileReference* VerifiedMethod::GetDequickenIndex(uint32_t dex_pc) const {
-  DCHECK(Runtime::Current()->UseJit());
+  DCHECK(Runtime::Current()->UseJitCompilation());
   auto it = dequicken_map_.find(dex_pc);
   return (it != dequicken_map_.end()) ? &it->second : nullptr;
 }
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 9e0c22c..6863f42 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -36,6 +36,7 @@
                         /* instruction_set_ */ kNone,
                         /* instruction_set_features */ nullptr,
                         /* boot_image */ false,
+                        /* app_image */ false,
                         /* image_classes */ nullptr,
                         /* compiled_classes */ nullptr,
                         /* compiled_methods */ nullptr,
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index be82956..1ab1d31 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -341,6 +341,7 @@
     InstructionSet instruction_set,
     const InstructionSetFeatures* instruction_set_features,
     bool boot_image,
+    bool app_image,
     std::unordered_set<std::string>* image_classes,
     std::unordered_set<std::string>* compiled_classes,
     std::unordered_set<std::string>* compiled_methods,
@@ -363,6 +364,7 @@
       compiled_methods_(MethodTable::key_compare()),
       non_relative_linker_patch_count_(0u),
       boot_image_(boot_image),
+      app_image_(app_image),
       image_classes_(image_classes),
       classes_to_compile_(compiled_classes),
       methods_to_compile_(compiled_methods),
@@ -473,7 +475,7 @@
     const DexFile& dex_file, const DexFile::ClassDef& class_def)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   auto* const runtime = Runtime::Current();
-  if (runtime->UseJit() || driver.GetCompilerOptions().VerifyAtRuntime()) {
+  if (runtime->UseJitCompilation() || driver.GetCompilerOptions().VerifyAtRuntime()) {
     // Verify at runtime shouldn't dex to dex since we didn't resolve of verify.
     return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
   }
@@ -945,7 +947,7 @@
 
 class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
  public:
-  ResolveCatchBlockExceptionsClassVisitor(
+  explicit ResolveCatchBlockExceptionsClassVisitor(
       std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
      : exceptions_to_resolve_(exceptions_to_resolve) {}
 
@@ -1268,7 +1270,7 @@
 bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) {
   Runtime* runtime = Runtime::Current();
   if (!runtime->IsAotCompiler()) {
-    DCHECK(runtime->UseJit());
+    DCHECK(runtime->UseJitCompilation());
     // Having the klass reference here implies that the klass is already loaded.
     return true;
   }
@@ -1289,7 +1291,7 @@
   if ((IsBootImage() &&
        IsImageClass(dex_cache->GetDexFile()->StringDataByIdx(
            dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) ||
-      Runtime::Current()->UseJit()) {
+      Runtime::Current()->UseJitCompilation()) {
     mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
     result = (resolved_class != nullptr);
   }
@@ -1307,7 +1309,7 @@
   // See also Compiler::ResolveDexFile
 
   bool result = false;
-  if (IsBootImage() || Runtime::Current()->UseJit()) {
+  if (IsBootImage() || Runtime::Current()->UseJitCompilation()) {
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
@@ -1319,7 +1321,7 @@
       result = true;
     } else {
       // Just check whether the dex cache already has the string.
-      DCHECK(Runtime::Current()->UseJit());
+      DCHECK(Runtime::Current()->UseJitCompilation());
       result = (dex_cache->GetResolvedString(string_idx) != nullptr);
     }
   }
@@ -1427,7 +1429,7 @@
     } else {
       return false;
     }
-  } else if (runtime->UseJit() && !heap->IsMovableObject(resolved_class)) {
+  } else if (runtime->UseJitCompilation() && !heap->IsMovableObject(resolved_class)) {
     *is_type_initialized = resolved_class->IsInitialized();
     // If the class may move around, then don't embed it as a direct pointer.
     *use_direct_type_ptr = true;
@@ -1604,7 +1606,7 @@
       }
     }
   }
-  if (runtime->UseJit()) {
+  if (runtime->UseJitCompilation()) {
     // If we are the JIT, then don't allow a direct call to the interpreter bridge since this will
     // never be updated even after we compile the method.
     if (cl->IsQuickToInterpreterBridge(
@@ -1636,7 +1638,7 @@
   bool must_use_direct_pointers = false;
   mirror::DexCache* dex_cache = declaring_class->GetDexCache();
   if (target_method->dex_file == dex_cache->GetDexFile() &&
-    !(runtime->UseJit() && dex_cache->GetResolvedMethod(
+    !(runtime->UseJitCompilation() && dex_cache->GetResolvedMethod(
         method->GetDexMethodIndex(), pointer_size) == nullptr)) {
     target_method->dex_method_index = method->GetDexMethodIndex();
   } else {
@@ -1673,7 +1675,7 @@
         break;
       }
     }
-    if (method_in_image || compiling_boot || runtime->UseJit()) {
+    if (method_in_image || compiling_boot || runtime->UseJitCompilation()) {
       // We know we must be able to get to the method in the image, so use that pointer.
       // In the case where we are the JIT, we can always use direct pointers since we know where
       // the method and its code are / will be. We don't sharpen to interpreter bridge since we
@@ -2440,9 +2442,12 @@
   context.ForAll(0, dex_file.NumClassDefs(), &visitor, init_thread_count);
 }
 
-class InitializeArrayClassVisitor : public ClassVisitor {
+class InitializeArrayClassesAndCreateConflictTablesVisitor : public ClassVisitor {
  public:
   virtual bool operator()(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+      return true;
+    }
     if (klass->IsArrayClass()) {
       StackHandleScope<1> hs(Thread::Current());
       Runtime::Current()->GetClassLinker()->EnsureInitialized(hs.Self(),
@@ -2450,6 +2455,10 @@
                                                               true,
                                                               true);
     }
+    // Create the conflict tables.
+    if (!klass->IsTemp() && klass->ShouldHaveEmbeddedImtAndVTable()) {
+      Runtime::Current()->GetClassLinker()->FillIMTAndConflictTables(klass);
+    }
     return true;
   }
 };
@@ -2462,13 +2471,15 @@
     CHECK(dex_file != nullptr);
     InitializeClasses(class_loader, *dex_file, dex_files, timings);
   }
-  {
+  if (boot_image_ || app_image_) {
     // Make sure that we call EnsureIntiailized on all the array classes to call
     // SetVerificationAttempted so that the access flags are set. If we do not do this they get
     // changed at runtime resulting in more dirty image pages.
+    // Also create conflict tables.
+    // Only useful if we are compiling an image (image_classes_ is not null).
     ScopedObjectAccess soa(Thread::Current());
-    InitializeArrayClassVisitor visitor;
-    Runtime::Current()->GetClassLinker()->VisitClasses(&visitor);
+    InitializeArrayClassesAndCreateConflictTablesVisitor visitor;
+    Runtime::Current()->GetClassLinker()->VisitClassesWithoutClassesLock(&visitor);
   }
   if (IsBootImage()) {
     // Prune garbage objects created during aborted transactions.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index d63dffa..19a1ecc 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -92,6 +92,7 @@
                  InstructionSet instruction_set,
                  const InstructionSetFeatures* instruction_set_features,
                  bool boot_image,
+                 bool app_image,
                  std::unordered_set<std::string>* image_classes,
                  std::unordered_set<std::string>* compiled_classes,
                  std::unordered_set<std::string>* compiled_methods,
@@ -652,6 +653,7 @@
   size_t non_relative_linker_patch_count_ GUARDED_BY(compiled_methods_lock_);
 
   const bool boot_image_;
+  const bool app_image_;
 
   // If image_ is true, specifies the classes that will be included in the image.
   // Note if image_classes_ is null, all classes are included in the image.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 8bb462c..00ff522 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -653,8 +653,7 @@
   for (ImageInfo& image_info : image_infos_) {
     ImageSection unused_sections[ImageHeader::kSectionCount];
     const size_t length = RoundUp(
-        image_info.CreateImageSections(target_ptr_size_, unused_sections),
-        kPageSize);
+        image_info.CreateImageSections(unused_sections), kPageSize);
 
     std::string error_msg;
     image_info.image_.reset(MemMap::MapAnonymous("image writer image",
@@ -1214,6 +1213,20 @@
           AssignMethodOffset(&m, type, oat_index);
         }
         (any_dirty ? dirty_methods_ : clean_methods_) += num_methods;
+
+        // Assign offsets for all runtime methods in the IMT since these may hold conflict tables
+        // live.
+        if (as_klass->ShouldHaveEmbeddedImtAndVTable()) {
+          for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+            ArtMethod* imt_method = as_klass->GetEmbeddedImTableEntry(i, target_ptr_size_);
+            DCHECK(imt_method != nullptr);
+            if (imt_method->IsRuntimeMethod() &&
+                !IsInBootImage(imt_method) &&
+                !NativeRelocationAssigned(imt_method)) {
+              AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index);
+            }
+          }
+        }
       }
     } else if (h_obj->IsObjectArray()) {
       // Walk elements of an object array.
@@ -1237,13 +1250,37 @@
   }
 }
 
+bool ImageWriter::NativeRelocationAssigned(void* ptr) const {
+  return native_object_relocations_.find(ptr) != native_object_relocations_.end();
+}
+
+void ImageWriter::TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index) {
+  // No offset, or already assigned.
+  if (table == nullptr || NativeRelocationAssigned(table)) {
+    return;
+  }
+  CHECK(!IsInBootImage(table));
+  // If the method is a conflict method we also want to assign the conflict table offset.
+  ImageInfo& image_info = GetImageInfo(oat_index);
+  const size_t size = table->ComputeSize(target_ptr_size_);
+  native_object_relocations_.emplace(
+      table,
+      NativeObjectRelocation {
+          oat_index,
+          image_info.bin_slot_sizes_[kBinIMTConflictTable],
+          kNativeObjectRelocationTypeIMTConflictTable});
+  image_info.bin_slot_sizes_[kBinIMTConflictTable] += size;
+}
+
 void ImageWriter::AssignMethodOffset(ArtMethod* method,
                                      NativeObjectRelocationType type,
                                      size_t oat_index) {
   DCHECK(!IsInBootImage(method));
-  auto it = native_object_relocations_.find(method);
-  CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned "
+  CHECK(!NativeRelocationAssigned(method)) << "Method " << method << " already assigned "
       << PrettyMethod(method);
+  if (method->IsRuntimeMethod()) {
+    TryAssignConflictTableOffset(method->GetImtConflictTable(target_ptr_size_), oat_index);
+  }
   ImageInfo& image_info = GetImageInfo(oat_index);
   size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)];
   native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type });
@@ -1292,8 +1329,7 @@
   // know where image_roots is going to end up
   image_objects_offset_begin_ = RoundUp(sizeof(ImageHeader), kObjectAlignment);  // 64-bit-alignment
 
-  // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
-  heap->VisitObjects(WalkFieldsCallback, this);
+  const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
   // Write the image runtime methods.
   image_methods_[ImageHeader::kResolutionMethod] = runtime->GetResolutionMethod();
   image_methods_[ImageHeader::kImtConflictMethod] = runtime->GetImtConflictMethod();
@@ -1303,31 +1339,19 @@
       runtime->GetCalleeSaveMethod(Runtime::kRefsOnly);
   image_methods_[ImageHeader::kRefsAndArgsSaveMethod] =
       runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
-
-  // Add room for fake length prefixed array for holding the image methods.
-  const auto image_method_type = kNativeObjectRelocationTypeArtMethodArrayClean;
-  auto it = native_object_relocations_.find(&image_method_array_);
-  CHECK(it == native_object_relocations_.end());
-  ImageInfo& default_image_info = GetImageInfo(GetDefaultOatIndex());
-  size_t& offset =
-      default_image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)];
-  if (!compile_app_image_) {
-    native_object_relocations_.emplace(&image_method_array_,
-        NativeObjectRelocation { GetDefaultOatIndex(), offset, image_method_type });
-  }
-  size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
-  const size_t array_size = LengthPrefixedArray<ArtMethod>::ComputeSize(
-      0, ArtMethod::Size(target_ptr_size_), method_alignment);
-  CHECK_ALIGNED_PARAM(array_size, method_alignment);
-  offset += array_size;
+  // Visit image methods first to have the main runtime methods in the first image.
   for (auto* m : image_methods_) {
     CHECK(m != nullptr);
     CHECK(m->IsRuntimeMethod());
     DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
     if (!IsInBootImage(m)) {
-      AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, GetDefaultOatIndex());
+      AssignMethodOffset(m, kNativeObjectRelocationTypeRuntimeMethod, GetDefaultOatIndex());
     }
   }
+
+  // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
+  heap->VisitObjects(WalkFieldsCallback, this);
+
   // Calculate size of the dex cache arrays slot and prepare offsets.
   PrepareDexCacheArraySlots();
 
@@ -1346,15 +1370,22 @@
   for (ImageInfo& image_info : image_infos_) {
     size_t bin_offset = image_objects_offset_begin_;
     for (size_t i = 0; i != kBinSize; ++i) {
+      switch (i) {
+        case kBinArtMethodClean:
+        case kBinArtMethodDirty: {
+          bin_offset = RoundUp(bin_offset, method_alignment);
+          break;
+        }
+        case kBinIMTConflictTable: {
+          bin_offset = RoundUp(bin_offset, target_ptr_size_);
+          break;
+        }
+        default: {
+          // Normal alignment.
+        }
+      }
       image_info.bin_slot_offsets_[i] = bin_offset;
       bin_offset += image_info.bin_slot_sizes_[i];
-      if (i == kBinArtField) {
-        static_assert(kBinArtField + 1 == kBinArtMethodClean, "Methods follow fields.");
-        static_assert(alignof(ArtField) == 4u, "ArtField alignment is 4.");
-        DCHECK_ALIGNED(bin_offset, 4u);
-        DCHECK(method_alignment == 4u || method_alignment == 8u);
-        bin_offset = RoundUp(bin_offset, method_alignment);
-      }
     }
     // NOTE: There may be additional padding between the bin slots and the intern table.
     DCHECK_EQ(image_info.image_end_,
@@ -1367,9 +1398,7 @@
     image_info.image_begin_ = global_image_begin_ + image_offset;
     image_info.image_offset_ = image_offset;
     ImageSection unused_sections[ImageHeader::kSectionCount];
-    image_info.image_size_ = RoundUp(
-        image_info.CreateImageSections(target_ptr_size_, unused_sections),
-        kPageSize);
+    image_info.image_size_ = RoundUp(image_info.CreateImageSections(unused_sections), kPageSize);
     // There should be no gaps until the next image.
     image_offset += image_info.image_size_;
   }
@@ -1396,42 +1425,52 @@
   // Note that image_info.image_end_ is left at end of used mirror object section.
 }
 
-size_t ImageWriter::ImageInfo::CreateImageSections(size_t target_ptr_size,
-                                                   ImageSection* out_sections) const {
+size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections) const {
   DCHECK(out_sections != nullptr);
+
+  // Do not round up any sections here that are represented by the bins since it will break
+  // offsets.
+
   // Objects section
-  auto* objects_section = &out_sections[ImageHeader::kSectionObjects];
+  ImageSection* objects_section = &out_sections[ImageHeader::kSectionObjects];
   *objects_section = ImageSection(0u, image_end_);
-  size_t cur_pos = objects_section->End();
+
   // Add field section.
-  auto* field_section = &out_sections[ImageHeader::kSectionArtFields];
-  *field_section = ImageSection(cur_pos, bin_slot_sizes_[kBinArtField]);
+  ImageSection* field_section = &out_sections[ImageHeader::kSectionArtFields];
+  *field_section = ImageSection(bin_slot_offsets_[kBinArtField], bin_slot_sizes_[kBinArtField]);
   CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset());
-  cur_pos = field_section->End();
-  // Round up to the alignment the required by the method section.
-  cur_pos = RoundUp(cur_pos, ArtMethod::Alignment(target_ptr_size));
+
   // Add method section.
-  auto* methods_section = &out_sections[ImageHeader::kSectionArtMethods];
-  *methods_section = ImageSection(cur_pos,
-                                  bin_slot_sizes_[kBinArtMethodClean] +
-                                      bin_slot_sizes_[kBinArtMethodDirty]);
-  CHECK_EQ(bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset());
-  cur_pos = methods_section->End();
+  ImageSection* methods_section = &out_sections[ImageHeader::kSectionArtMethods];
+  *methods_section = ImageSection(
+      bin_slot_offsets_[kBinArtMethodClean],
+      bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinArtMethodDirty]);
+
+  // Conflict tables section.
+  ImageSection* imt_conflict_tables_section = &out_sections[ImageHeader::kSectionIMTConflictTables];
+  *imt_conflict_tables_section = ImageSection(bin_slot_offsets_[kBinIMTConflictTable],
+                                              bin_slot_sizes_[kBinIMTConflictTable]);
+
+  // Runtime methods section.
+  ImageSection* runtime_methods_section = &out_sections[ImageHeader::kSectionRuntimeMethods];
+  *runtime_methods_section = ImageSection(bin_slot_offsets_[kBinRuntimeMethod],
+                                          bin_slot_sizes_[kBinRuntimeMethod]);
+
   // Add dex cache arrays section.
-  auto* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays];
-  *dex_cache_arrays_section = ImageSection(cur_pos, bin_slot_sizes_[kBinDexCacheArray]);
-  CHECK_EQ(bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset());
-  cur_pos = dex_cache_arrays_section->End();
+  ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays];
+  *dex_cache_arrays_section = ImageSection(bin_slot_offsets_[kBinDexCacheArray],
+                                           bin_slot_sizes_[kBinDexCacheArray]);
+
   // Round up to the alignment the string table expects. See HashSet::WriteToMemory.
-  cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
+  size_t cur_pos = RoundUp(dex_cache_arrays_section->End(), sizeof(uint64_t));
   // Calculate the size of the interned strings.
-  auto* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings];
+  ImageSection* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings];
   *interned_strings_section = ImageSection(cur_pos, intern_table_bytes_);
   cur_pos = interned_strings_section->End();
   // Round up to the alignment the class table expects. See HashSet::WriteToMemory.
   cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
   // Calculate the size of the class table section.
-  auto* class_table_section = &out_sections[ImageHeader::kSectionClassTable];
+  ImageSection* class_table_section = &out_sections[ImageHeader::kSectionClassTable];
   *class_table_section = ImageSection(cur_pos, class_table_bytes_);
   cur_pos = class_table_section->End();
   // Image end goes right before the start of the image bitmap.
@@ -1446,7 +1485,7 @@
 
   // Create the image sections.
   ImageSection sections[ImageHeader::kSectionCount];
-  const size_t image_end = image_info.CreateImageSections(target_ptr_size_, sections);
+  const size_t image_end = image_info.CreateImageSections(sections);
 
   // Finally bitmap section.
   const size_t bitmap_bytes = image_info.image_bitmap_->Size();
@@ -1531,8 +1570,20 @@
   ImageWriter* const image_writer_;
 };
 
+void ImageWriter::CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy) {
+  const size_t count = orig->NumEntries(target_ptr_size_);
+  for (size_t i = 0; i < count; ++i) {
+    ArtMethod* interface_method = orig->GetInterfaceMethod(i, target_ptr_size_);
+    ArtMethod* implementation_method = orig->GetImplementationMethod(i, target_ptr_size_);
+    copy->SetInterfaceMethod(i, target_ptr_size_, NativeLocationInImage(interface_method));
+    copy->SetImplementationMethod(i,
+                                  target_ptr_size_,
+                                  NativeLocationInImage(implementation_method));
+  }
+}
+
 void ImageWriter::CopyAndFixupNativeData(size_t oat_index) {
-  ImageInfo& image_info = GetImageInfo(oat_index);
+  const ImageInfo& image_info = GetImageInfo(oat_index);
   // Copy ArtFields and methods to their locations and update the array for convenience.
   for (auto& pair : native_object_relocations_) {
     NativeObjectRelocation& relocation = pair.second;
@@ -1550,6 +1601,7 @@
             GetImageAddress(reinterpret_cast<ArtField*>(pair.first)->GetDeclaringClass()));
         break;
       }
+      case kNativeObjectRelocationTypeRuntimeMethod:
       case kNativeObjectRelocationTypeArtMethodClean:
       case kNativeObjectRelocationTypeArtMethodDirty: {
         CopyAndFixupMethod(reinterpret_cast<ArtMethod*>(pair.first),
@@ -1575,26 +1627,22 @@
       case kNativeObjectRelocationTypeDexCacheArray:
         // Nothing to copy here, everything is done in FixupDexCache().
         break;
+      case kNativeObjectRelocationTypeIMTConflictTable: {
+        auto* orig_table = reinterpret_cast<ImtConflictTable*>(pair.first);
+        CopyAndFixupImtConflictTable(
+            orig_table,
+            new(dest)ImtConflictTable(orig_table->NumEntries(target_ptr_size_), target_ptr_size_));
+        break;
+      }
     }
   }
   // Fixup the image method roots.
   auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
-  const ImageSection& methods_section = image_header->GetMethodsSection();
   for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) {
     ArtMethod* method = image_methods_[i];
     CHECK(method != nullptr);
-    // Only place runtime methods in the image of the default oat file.
-    if (method->IsRuntimeMethod() && oat_index != GetDefaultOatIndex()) {
-      continue;
-    }
     if (!IsInBootImage(method)) {
-      auto it = native_object_relocations_.find(method);
-      CHECK(it != native_object_relocations_.end()) << "No forwarding for " << PrettyMethod(method);
-      NativeObjectRelocation& relocation = it->second;
-      CHECK(methods_section.Contains(relocation.offset)) << relocation.offset << " not in "
-          << methods_section;
-      CHECK(relocation.IsArtMethodRelocation()) << relocation.type;
-      method = reinterpret_cast<ArtMethod*>(global_image_begin_ + it->second.offset);
+      method = NativeLocationInImage(method);
     }
     image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method);
   }
@@ -2057,24 +2105,28 @@
 
   // The resolution method has a special trampoline to call.
   Runtime* runtime = Runtime::Current();
-  if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
-    copy->SetEntryPointFromQuickCompiledCodePtrSize(
-        GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_);
-  } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
-                      orig == runtime->GetImtUnimplementedMethod())) {
-    copy->SetEntryPointFromQuickCompiledCodePtrSize(
-        GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_);
-  } else if (UNLIKELY(orig->IsRuntimeMethod())) {
-    bool found_one = false;
-    for (size_t i = 0; i < static_cast<size_t>(Runtime::kLastCalleeSaveType); ++i) {
-      auto idx = static_cast<Runtime::CalleeSaveType>(i);
-      if (runtime->HasCalleeSaveMethod(idx) && runtime->GetCalleeSaveMethod(idx) == orig) {
-        found_one = true;
-        break;
+  if (orig->IsRuntimeMethod()) {
+    ImtConflictTable* orig_table = orig->GetImtConflictTable(target_ptr_size_);
+    if (orig_table != nullptr) {
+      // Special IMT conflict method, normal IMT conflict method or unimplemented IMT method.
+      copy->SetEntryPointFromQuickCompiledCodePtrSize(
+          GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_);
+      copy->SetImtConflictTable(NativeLocationInImage(orig_table), target_ptr_size_);
+    } else if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
+      copy->SetEntryPointFromQuickCompiledCodePtrSize(
+          GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_);
+    } else {
+      bool found_one = false;
+      for (size_t i = 0; i < static_cast<size_t>(Runtime::kLastCalleeSaveType); ++i) {
+        auto idx = static_cast<Runtime::CalleeSaveType>(i);
+        if (runtime->HasCalleeSaveMethod(idx) && runtime->GetCalleeSaveMethod(idx) == orig) {
+          found_one = true;
+          break;
+        }
       }
+      CHECK(found_one) << "Expected to find callee save method but got " << PrettyMethod(orig);
+      CHECK(copy->IsRuntimeMethod());
     }
-    CHECK(found_one) << "Expected to find callee save method but got " << PrettyMethod(orig);
-    CHECK(copy->IsRuntimeMethod());
   } else {
     // We assume all methods have code. If they don't currently then we set them to the use the
     // resolution trampoline. Abstract methods never have code and so we need to make sure their
@@ -2141,6 +2193,10 @@
       return kBinArtMethodDirty;
     case kNativeObjectRelocationTypeDexCacheArray:
       return kBinDexCacheArray;
+    case kNativeObjectRelocationTypeRuntimeMethod:
+      return kBinRuntimeMethod;
+    case kNativeObjectRelocationTypeIMTConflictTable:
+      return kBinIMTConflictTable;
   }
   UNREACHABLE();
 }
@@ -2242,7 +2298,6 @@
       compile_app_image_(compile_app_image),
       target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
       image_infos_(oat_filenames.size()),
-      image_method_array_(ImageHeader::kImageMethodsCount),
       dirty_methods_(0u),
       clean_methods_(0u),
       image_storage_mode_(image_storage_mode),
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 0cb6aea..51976c5 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -169,6 +169,10 @@
     // ArtMethods may be dirty if the class has native methods or a declaring class that isn't
     // initialized.
     kBinArtMethodDirty,
+    // Conflict tables (clean).
+    kBinIMTConflictTable,
+    // Runtime methods (always clean, do not have a length prefix array).
+    kBinRuntimeMethod,
     // Dex cache arrays have a special slot for PC-relative addressing. Since they are
     // huge, and as such their dirtiness is not important for the clean/dirty separation,
     // we arbitrarily keep them at the end of the native data.
@@ -186,6 +190,8 @@
     kNativeObjectRelocationTypeArtMethodArrayClean,
     kNativeObjectRelocationTypeArtMethodDirty,
     kNativeObjectRelocationTypeArtMethodArrayDirty,
+    kNativeObjectRelocationTypeRuntimeMethod,
+    kNativeObjectRelocationTypeIMTConflictTable,
     kNativeObjectRelocationTypeDexCacheArray,
   };
   friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type);
@@ -240,7 +246,7 @@
 
     // Create the image sections into the out sections variable, returns the size of the image
     // excluding the bitmap.
-    size_t CreateImageSections(size_t target_ptr_size, ImageSection* out_sections) const;
+    size_t CreateImageSections(ImageSection* out_sections) const;
 
     std::unique_ptr<MemMap> image_;  // Memory mapped for generating the image.
 
@@ -395,6 +401,8 @@
   void CopyAndFixupObject(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
   void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info)
       SHARED_REQUIRES(Locks::mutator_lock_);
+  void CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void FixupClass(mirror::Class* orig, mirror::Class* copy)
       SHARED_REQUIRES(Locks::mutator_lock_);
   void FixupObject(mirror::Object* orig, mirror::Object* copy)
@@ -425,6 +433,11 @@
                           size_t oat_index)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Assign the offset for an IMT conflict table. Does nothing if the table already has a native
+  // relocation.
+  void TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   // Return true if klass is loaded by the boot class loader but not in the boot image.
   bool IsBootClassLoaderNonImageClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -481,6 +494,9 @@
   // remove duplicates in the multi image and app image case.
   mirror::String* FindInternedString(mirror::String* string) SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Return true if there already exists a native allocation for an object.
+  bool NativeRelocationAssigned(void* ptr) const;
+
   const CompilerDriver& compiler_driver_;
 
   // Beginning target image address for the first image.
@@ -517,16 +533,14 @@
 
     bool IsArtMethodRelocation() const {
       return type == kNativeObjectRelocationTypeArtMethodClean ||
-          type == kNativeObjectRelocationTypeArtMethodDirty;
+          type == kNativeObjectRelocationTypeArtMethodDirty ||
+          type == kNativeObjectRelocationTypeRuntimeMethod;
     }
   };
   std::unordered_map<void*, NativeObjectRelocation> native_object_relocations_;
 
   // Runtime ArtMethods which aren't reachable from any Class but need to be copied into the image.
   ArtMethod* image_methods_[ImageHeader::kImageMethodsCount];
-  // Fake length prefixed array for image methods. This array does not contain the actual
-  // ArtMethods. We only use it for the header and relocation addresses.
-  LengthPrefixedArray<ArtMethod> image_method_array_;
 
   // Counters for measurements, used for logging only.
   uint64_t dirty_methods_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 5de9842..c2d7ff7 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -155,7 +155,8 @@
       Compiler::kOptimizing,
       instruction_set,
       instruction_set_features_.get(),
-      /* image */ false,
+      /* boot_image */ false,
+      /* app_image */ false,
       /* image_classes */ nullptr,
       /* compiled_classes */ nullptr,
       /* compiled_methods */ nullptr,
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index cf836a9..251dc39 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -31,6 +31,7 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/stack_trace_element.h"
+#include "nativeloader/native_loader.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
@@ -53,6 +54,11 @@
     check_generic_jni_ = false;
   }
 
+  void TearDown() OVERRIDE {
+    android::ResetNativeLoader();
+    CommonCompilerTest::TearDown();
+  }
+
   void SetCheckGenericJni(bool generic) {
     check_generic_jni_ = generic;
   }
@@ -93,10 +99,12 @@
       // Start runtime.
       Thread::Current()->TransitionFromSuspendedToRunnable();
       bool started = runtime_->Start();
+      android::InitializeNativeLoader();
       CHECK(started);
     }
     // JNI operations after runtime start.
     env_ = Thread::Current()->GetJniEnv();
+    library_search_path_ = env_->NewStringUTF("");
     jklass_ = env_->FindClass("MyClassNatives");
     ASSERT_TRUE(jklass_ != nullptr) << method_name << " " << method_sig;
 
@@ -168,6 +176,7 @@
   void StackArgsSignExtendedMips64Impl();
 
   JNIEnv* env_;
+  jstring library_search_path_;
   jmethodID jmethod_;
   bool check_generic_jni_;
 };
@@ -220,7 +229,7 @@
 
   std::string reason;
   ASSERT_TRUE(Runtime::Current()->GetJavaVM()->
-                  LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason))
+                  LoadNativeLibrary(env_, "", class_loader_, library_search_path_, &reason))
       << reason;
 
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
@@ -235,7 +244,7 @@
 
   std::string reason;
   ASSERT_TRUE(Runtime::Current()->GetJavaVM()->
-                  LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason))
+                  LoadNativeLibrary(env_, "", class_loader_, library_search_path_, &reason))
       << reason;
 
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index c07de79..ec69107 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -51,6 +51,7 @@
                 instruction_set,
                 /* instruction_set_features*/ nullptr,
                 /* boot_image */ false,
+                /* app_image */ false,
                 /* image_classes */ nullptr,
                 /* compiled_classes */ nullptr,
                 /* compiled_methods */ nullptr,
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 73b16d5..5b19284 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -112,6 +112,7 @@
                                               insn_set,
                                               insn_features_.get(),
                                               /* boot_image */ false,
+                                              /* app_image */ false,
                                               /* image_classes */ nullptr,
                                               /* compiled_classes */ nullptr,
                                               /* compiled_methods */ nullptr,
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index e804bee..8da9f06 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1127,17 +1127,23 @@
     return target_offset;
   }
 
-  mirror::Class* GetTargetType(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
-    mirror::DexCache* dex_cache = (dex_file_ == patch.TargetTypeDexFile())
+  mirror::DexCache* GetDexCache(const DexFile* target_dex_file)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    return (target_dex_file == dex_file_)
         ? dex_cache_
-        : class_linker_->FindDexCache(Thread::Current(), *patch.TargetTypeDexFile());
+        : class_linker_->FindDexCache(Thread::Current(), *target_dex_file);
+  }
+
+  mirror::Class* GetTargetType(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
+    mirror::DexCache* dex_cache = GetDexCache(patch.TargetTypeDexFile());
     mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
     CHECK(type != nullptr);
     return type;
   }
 
   mirror::String* GetTargetString(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
-    mirror::String* string = dex_cache_->GetResolvedString(patch.TargetStringIndex());
+    mirror::DexCache* dex_cache = GetDexCache(patch.TargetStringDexFile());
+    mirror::String* string = dex_cache->GetResolvedString(patch.TargetStringIndex());
     DCHECK(string != nullptr);
     DCHECK(writer_->HasBootImage() ||
            Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string));
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 659c6f8..b65e98a 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -552,7 +552,11 @@
     DCHECK(!IsAddedBlock(block));
     first_index_bounds_check_map_.clear();
     HGraphVisitor::VisitBasicBlock(block);
-    AddComparesWithDeoptimization(block);
+    // We should never deoptimize from an osr method, otherwise we might wrongly optimize
+    // code dominated by the deoptimization.
+    if (!GetGraph()->IsCompilingOsr()) {
+      AddComparesWithDeoptimization(block);
+    }
   }
 
   void Finish() {
@@ -1358,6 +1362,11 @@
       if (loop->IsIrreducible()) {
         return false;
       }
+      // We should never deoptimize from an osr method, otherwise we might wrongly optimize
+      // code dominated by the deoptimization.
+      if (GetGraph()->IsCompilingOsr()) {
+        return false;
+      }
       // A try boundary preheader is hard to handle.
       // TODO: remove this restriction.
       if (loop->GetPreHeader()->GetLastInstruction()->IsTryBoundary()) {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 45d23fe..197e473 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5183,10 +5183,10 @@
     case HLoadString::LoadKind::kBootImageAddress:
       break;
     case HLoadString::LoadKind::kDexCacheAddress:
-      DCHECK(Runtime::Current()->UseJit());
+      DCHECK(Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kDexCachePcRelative:
-      DCHECK(!Runtime::Current()->UseJit());
+      DCHECK(!Runtime::Current()->UseJitCompilation());
       // We disable pc-relative load when there is an irreducible loop, as the optimization
       // is incompatible with it.
       // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index e8e6b68..9680f2b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4010,10 +4010,10 @@
     case HLoadString::LoadKind::kBootImageAddress:
       break;
     case HLoadString::LoadKind::kDexCacheAddress:
-      DCHECK(Runtime::Current()->UseJit());
+      DCHECK(Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kDexCachePcRelative:
-      DCHECK(!Runtime::Current()->UseJit());
+      DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e73e880..6dc480b 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3303,17 +3303,6 @@
   int shift;
   CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
 
-  NearLabel ndiv;
-  NearLabel end;
-  // If numerator is 0, the result is 0, no computation needed.
-  __ testl(eax, eax);
-  __ j(kNotEqual, &ndiv);
-
-  __ xorl(out, out);
-  __ jmp(&end);
-
-  __ Bind(&ndiv);
-
   // Save the numerator.
   __ movl(num, eax);
 
@@ -3348,7 +3337,6 @@
   } else {
     __ movl(eax, edx);
   }
-  __ Bind(&end);
 }
 
 void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instruction) {
@@ -5977,7 +5965,7 @@
       DCHECK(GetCompilerOptions().GetCompilePic());
       FALLTHROUGH_INTENDED;
     case HLoadString::LoadKind::kDexCachePcRelative:
-      DCHECK(!Runtime::Current()->UseJit());  // Note: boot image is also non-JIT.
+      DCHECK(!Runtime::Current()->UseJitCompilation());  // Note: boot image is also non-JIT.
       // We disable pc-relative load when there is an irreducible loop, as the optimization
       // is incompatible with it.
       // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
@@ -5989,7 +5977,7 @@
     case HLoadString::LoadKind::kBootImageAddress:
       break;
     case HLoadString::LoadKind::kDexCacheAddress:
-      DCHECK(Runtime::Current()->UseJit());
+      DCHECK(Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 5576d83..96ec09c 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3390,16 +3390,6 @@
 
     __ movl(numerator, eax);
 
-    NearLabel no_div;
-    NearLabel end;
-    __ testl(eax, eax);
-    __ j(kNotEqual, &no_div);
-
-    __ xorl(out, out);
-    __ jmp(&end);
-
-    __ Bind(&no_div);
-
     __ movl(eax, Immediate(magic));
     __ imull(numerator);
 
@@ -3425,7 +3415,6 @@
     } else {
       __ movl(eax, edx);
     }
-    __ Bind(&end);
   } else {
     int64_t imm = second.GetConstant()->AsLongConstant()->GetValue();
 
@@ -5413,10 +5402,10 @@
     case HLoadString::LoadKind::kBootImageAddress:
       break;
     case HLoadString::LoadKind::kDexCacheAddress:
-      DCHECK(Runtime::Current()->UseJit());
+      DCHECK(Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kDexCachePcRelative:
-      DCHECK(!Runtime::Current()->UseJit());
+      DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 96837a8..968e267 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -258,6 +258,15 @@
   VisitInstruction(check);
 }
 
+void GraphChecker::VisitDeoptimize(HDeoptimize* deopt) {
+  if (GetGraph()->IsCompilingOsr()) {
+    AddError(StringPrintf("A graph compiled OSR cannot have a HDeoptimize instruction"));
+  }
+
+  // Perform the instruction base checks too.
+  VisitInstruction(deopt);
+}
+
 void GraphChecker::VisitTryBoundary(HTryBoundary* try_boundary) {
   ArrayRef<HBasicBlock* const> handlers = try_boundary->GetExceptionHandlers();
 
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 83b1984..3060c80 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -57,6 +57,7 @@
   void VisitCheckCast(HCheckCast* check) OVERRIDE;
   void VisitCondition(HCondition* op) OVERRIDE;
   void VisitConstant(HConstant* instruction) OVERRIDE;
+  void VisitDeoptimize(HDeoptimize* instruction) OVERRIDE;
   void VisitIf(HIf* instruction) OVERRIDE;
   void VisitInstanceOf(HInstanceOf* check) OVERRIDE;
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index ff4b9a7..59de895 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -308,7 +308,7 @@
 
   // Check if we can use an inline cache.
   ArtMethod* caller = graph_->GetArtMethod();
-  if (Runtime::Current()->UseJit()) {
+  if (Runtime::Current()->UseJitCompilation()) {
     // Under JIT, we should always know the caller.
     DCHECK(caller != nullptr);
     ScopedProfilingInfoInlineUse spiis(caller, soa.Self());
@@ -322,7 +322,13 @@
         return false;
       } else if (ic.IsMonomorphic()) {
         MaybeRecordStat(kMonomorphicCall);
-        return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic);
+        if (outermost_graph_->IsCompilingOsr()) {
+          // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the
+          // interpreter and it may have seen different receiver types.
+          return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
+        } else {
+          return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic);
+        }
       } else if (ic.IsPolymorphic()) {
         MaybeRecordStat(kPolymorphicCall);
         return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
@@ -510,6 +516,11 @@
       bool deoptimize = all_targets_inlined &&
           (i != InlineCache::kIndividualCacheSize - 1) &&
           (ic.GetTypeAt(i + 1) == nullptr);
+
+      if (outermost_graph_->IsCompilingOsr()) {
+        // We do not support HDeoptimize in OSR methods.
+        deoptimize = false;
+      }
       HInstruction* compare = AddTypeGuard(
           receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
       if (deoptimize) {
@@ -623,7 +634,7 @@
                                                     ArtMethod* resolved_method,
                                                     const InlineCache& ic) {
   // This optimization only works under JIT for now.
-  DCHECK(Runtime::Current()->UseJit());
+  DCHECK(Runtime::Current()->UseJitCompilation());
   if (graph_->GetInstructionSet() == kMips64) {
     // TODO: Support HClassTableGet for mips64.
     return false;
@@ -672,7 +683,8 @@
   HInstruction* cursor = invoke_instruction->GetPrevious();
   HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
 
-  if (!TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ false)) {
+  HInstruction* return_replacement = nullptr;
+  if (!TryBuildAndInline(invoke_instruction, actual_method, &return_replacement)) {
     return false;
   }
 
@@ -701,9 +713,6 @@
   }
 
   HNotEqual* compare = new (graph_->GetArena()) HNotEqual(class_table_get, constant);
-  HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
-      compare, invoke_instruction->GetDexPc());
-  // TODO: Extend reference type propagation to understand the guard.
   if (cursor != nullptr) {
     bb_cursor->InsertInstructionAfter(receiver_class, cursor);
   } else {
@@ -711,8 +720,20 @@
   }
   bb_cursor->InsertInstructionAfter(class_table_get, receiver_class);
   bb_cursor->InsertInstructionAfter(compare, class_table_get);
-  bb_cursor->InsertInstructionAfter(deoptimize, compare);
-  deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+
+  if (outermost_graph_->IsCompilingOsr()) {
+    CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
+  } else {
+    // TODO: Extend reference type propagation to understand the guard.
+    HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
+        compare, invoke_instruction->GetDexPc());
+    bb_cursor->InsertInstructionAfter(deoptimize, compare);
+    deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+    if (return_replacement != nullptr) {
+      invoke_instruction->ReplaceWith(return_replacement);
+    }
+    invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+  }
 
   // Run type propagation to get the guard typed.
   ReferenceTypePropagation rtp_fixup(graph_,
@@ -744,6 +765,12 @@
                                  HInstruction** return_replacement) {
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
 
+  if (method->IsProxyMethod()) {
+    VLOG(compiler) << "Method " << PrettyMethod(method)
+                   << " is not inlined because of unimplemented inline support for proxy methods.";
+    return false;
+  }
+
   // Check whether we're allowed to inline. The outermost compilation unit is the relevant
   // dex file here (though the transitivity of an inline chain would allow checking the calller).
   if (!compiler_driver_->MayInline(method->GetDexFile(),
@@ -802,7 +829,7 @@
 
   if (!method->GetDeclaringClass()->IsVerified()) {
     uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
-    if (Runtime::Current()->UseJit() ||
+    if (Runtime::Current()->UseJitCompilation() ||
         !compiler_driver_->IsMethodVerifiedWithoutFailures(
             method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
       VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
@@ -1265,6 +1292,8 @@
 size_t HInliner::RunOptimizations(HGraph* callee_graph,
                                   const DexFile::CodeItem* code_item,
                                   const DexCompilationUnit& dex_compilation_unit) {
+  // Note: if the outermost_graph_ is being compiled OSR, we should not run any
+  // optimization that could lead to a HDeoptimize. The following optimizations do not.
   HDeadCodeElimination dce(callee_graph, stats_);
   HConstantFolding fold(callee_graph);
   HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 00cd2f6..a834216 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -673,6 +673,16 @@
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
       soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
   Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
+  // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId
+  // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache.
+  Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod(
+      method_idx, dex_compilation_unit_->GetDexCache(), class_loader)));
+
+  if (UNLIKELY(methods_class.Get() == nullptr)) {
+    // Clean up any exception left by type resolution.
+    soa.Self()->ClearException();
+    return nullptr;
+  }
 
   ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
       *dex_compilation_unit_->GetDexFile(),
@@ -711,47 +721,33 @@
       DCHECK(Runtime::Current()->IsAotCompiler());
       return nullptr;
     }
-    ArtMethod* current_method = graph_->GetArtMethod();
-    DCHECK(current_method != nullptr);
-    Handle<mirror::Class> methods_class(hs.NewHandle(
-        dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
-                                                                                method_idx,
-                                                                                current_method)));
-    if (methods_class.Get() == nullptr) {
-      // Invoking a super method requires knowing the actual super class. If we did not resolve
-      // the compiling method's declaring class (which only happens for ahead of time
-      // compilation), bail out.
-      DCHECK(Runtime::Current()->IsAotCompiler());
-      return nullptr;
+    ArtMethod* actual_method;
+    if (methods_class->IsInterface()) {
+      actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
+          resolved_method, class_linker->GetImagePointerSize());
     } else {
-      ArtMethod* actual_method;
-      if (methods_class->IsInterface()) {
-        actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
-            resolved_method, class_linker->GetImagePointerSize());
-      } else {
-        uint16_t vtable_index = resolved_method->GetMethodIndex();
-        actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
-            vtable_index, class_linker->GetImagePointerSize());
-      }
-      if (actual_method != resolved_method &&
-          !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
-        // The back-end code generator relies on this check in order to ensure that it will not
-        // attempt to read the dex_cache with a dex_method_index that is not from the correct
-        // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
-        // builder, which means that the code-generator (and compiler driver during sharpening and
-        // inliner, maybe) might invoke an incorrect method.
-        // TODO: The actual method could still be referenced in the current dex file, so we
-        //       could try locating it.
-        // TODO: Remove the dex_file restriction.
-        return nullptr;
-      }
-      if (!actual_method->IsInvokable()) {
-        // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
-        // could resolve the callee to the wrong method.
-        return nullptr;
-      }
-      resolved_method = actual_method;
+      uint16_t vtable_index = resolved_method->GetMethodIndex();
+      actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
+          vtable_index, class_linker->GetImagePointerSize());
     }
+    if (actual_method != resolved_method &&
+        !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+      // The back-end code generator relies on this check in order to ensure that it will not
+      // attempt to read the dex_cache with a dex_method_index that is not from the correct
+      // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
+      // builder, which means that the code-generator (and compiler driver during sharpening and
+      // inliner, maybe) might invoke an incorrect method.
+      // TODO: The actual method could still be referenced in the current dex file, so we
+      //       could try locating it.
+      // TODO: Remove the dex_file restriction.
+      return nullptr;
+    }
+    if (!actual_method->IsInvokable()) {
+      // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
+      // could resolve the callee to the wrong method.
+      return nullptr;
+    }
+    resolved_method = actual_method;
   }
 
   // Check for incompatible class changes. The class linker has a fast path for
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fe75451..6703695 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2218,6 +2218,7 @@
     ScopedObjectAccess soa(Thread::Current());
     DCHECK(IsValidHandle(type_handle));
     DCHECK(!type_handle->IsErroneous());
+    DCHECK(!type_handle->IsArrayClass() || !type_handle->GetComponentType()->IsErroneous());
     if (!is_exact) {
       DCHECK(!type_handle->CannotBeAssignedFromOtherTypes())
           << "Callers of ReferenceTypeInfo::Create should ensure is_exact is properly computed";
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 04c9ff9..f2394f6 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -46,6 +46,13 @@
   return *cache;
 }
 
+// Returns true if klass is admissible to the propagation: non-null and non-erroneous.
+// For an array type, we also check if the component type is admissible.
+static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) {
+  return klass != nullptr && !klass->IsErroneous() &&
+      (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType()));
+}
+
 ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() {
   return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_);
 }
@@ -453,15 +460,10 @@
     }
     instr->SetReferenceTypeInfo(
         ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true));
-  } else if (klass != nullptr) {
-    if (klass->IsErroneous()) {
-      // Set inexact object type for erroneous types.
-      instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
-    } else {
-      ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
-      is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
-      instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
-    }
+  } else if (IsAdmissible(klass)) {
+    ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
+    is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
+    instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
   } else {
     instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
   }
@@ -563,7 +565,7 @@
                                                        instr->GetDexFile(),
                                                        instr->GetTypeIndex(),
                                                        hint_dex_cache_);
-  if (resolved_class != nullptr && !resolved_class->IsErroneous()) {
+  if (IsAdmissible(resolved_class)) {
     instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
         handle_cache_->NewHandle(resolved_class), /* is_exact */ true));
   }
@@ -664,12 +666,6 @@
   }
 
   if (phi->GetBlock()->IsLoopHeader()) {
-    if (!is_first_run_ && graph_->IsCompilingOsr()) {
-      // Don't update the type of a loop phi when compiling OSR: we may have done
-      // speculative optimizations dominating that phi, that do not hold at the
-      // point the interpreter jumps to that loop header.
-      return;
-    }
     // Set the initial type for the phi. Use the non back edge input for reaching
     // a fixed point faster.
     HInstruction* first_input = phi->InputAt(0);
@@ -742,7 +738,7 @@
   }
 
   Handle<mirror::Class> handle = parent_rti.GetTypeHandle();
-  if (handle->IsObjectArrayClass() && !handle->GetComponentType()->IsErroneous()) {
+  if (handle->IsObjectArrayClass() && IsAdmissible(handle->GetComponentType())) {
     ReferenceTypeInfo::TypeHandle component_handle =
         handle_cache->NewHandle(handle->GetComponentType());
     bool is_exact = component_handle->CannotBeAssignedFromOtherTypes();
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 7a1bb31..08bd35f 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -99,7 +99,7 @@
     if (direct_method != 0u) {  // Should we use a direct pointer to the method?
       // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
       // kDirectAddress would be fine for image methods, we don't support it at the moment.
-      DCHECK(!Runtime::Current()->UseJit());
+      DCHECK(!Runtime::Current()->UseJitCompilation());
       if (direct_method != static_cast<uintptr_t>(-1)) {  // Is the method pointer known now?
         method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
         method_load_data = direct_method;
@@ -109,7 +109,7 @@
     } else {  // Use dex cache.
       DCHECK_EQ(target_method.dex_file, &graph_->GetDexFile());
       if (use_pc_relative_instructions) {  // Can we use PC-relative access to the dex cache arrays?
-        DCHECK(!Runtime::Current()->UseJit());
+        DCHECK(!Runtime::Current()->UseJitCompilation());
         method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
         DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
                                     &graph_->GetDexFile());
@@ -121,7 +121,7 @@
     if (direct_code != 0u) {  // Should we use a direct pointer to the code?
       // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
       // while kCallDirect would be fine for image methods, we don't support it at the moment.
-      DCHECK(!Runtime::Current()->UseJit());
+      DCHECK(!Runtime::Current()->UseJitCompilation());
       if (direct_code != static_cast<uintptr_t>(-1)) {  // Is the code pointer known now?
         code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
         direct_code_ptr = direct_code;
@@ -174,7 +174,7 @@
 
     if (compiler_driver_->IsBootImage()) {
       // Compiling boot image. Resolve the string and allocate it if needed.
-      DCHECK(!runtime->UseJit());
+      DCHECK(!runtime->UseJitCompilation());
       mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
       CHECK(string != nullptr);
       if (!compiler_driver_->GetSupportBootImageFixup()) {
@@ -187,7 +187,7 @@
             ? HLoadString::LoadKind::kBootImageLinkTimePcRelative
             : HLoadString::LoadKind::kBootImageLinkTimeAddress;
       }
-    } else if (runtime->UseJit()) {
+    } else if (runtime->UseJitCompilation()) {
       // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
       // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
       mirror::String* string = dex_cache->GetResolvedString(string_index);
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 40dab74..1141fd1 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -1003,6 +1003,15 @@
 
   void AddBackEdgeUses(const HBasicBlock& block_at_use) {
     DCHECK(block_at_use.IsInLoop());
+    if (block_at_use.GetGraph()->HasIrreducibleLoops()) {
+      // Linear order may not be well formed when irreducible loops are present,
+      // i.e. loop blocks may not be adjacent and a back edge may not be last,
+      // which violates assumptions made in this method.
+      return;
+    }
+
+    DCHECK(IsLinearOrderWellFormed(*block_at_use.GetGraph()));
+
     // Add synthesized uses at the back edge of loops to help the register allocator.
     // Note that this method is called in decreasing liveness order, to faciliate adding
     // uses at the head of the `first_use_` linked list. Because below
@@ -1027,30 +1036,12 @@
       if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) {
         // There was a use already seen in this loop. Therefore the previous call to `AddUse`
         // already inserted the backedge use. We can stop going outward.
-        if (kIsDebugBuild) {
-          if (!HasSynthesizeUseAt(back_edge_use_position)) {
-            // There exists a use prior to `back_edge_use_position` but there is
-            // no synthesized use at the back edge. This can happen in the presence
-            // of irreducible loops, when blocks of the loop are not adjacent in
-            // linear order, i.e. when there is an out-of-loop block between
-            // `block_at_use` and `back_edge_position` that uses this interval.
-            DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops());
-            DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph()));
-          }
-        }
+        DCHECK(HasSynthesizeUseAt(back_edge_use_position));
         break;
       }
 
-      if (last_in_new_list != nullptr &&
-          back_edge_use_position <= last_in_new_list->GetPosition()) {
-        // Loops are not properly nested in the linear order, i.e. the back edge
-        // of an outer loop preceeds blocks of an inner loop. This can happen
-        // in the presence of irreducible loops.
-        DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops());
-        DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph()));
-        // We must bail out, otherwise we would generate an unsorted use list.
-        break;
-      }
+      DCHECK(last_in_new_list == nullptr ||
+             back_edge_use_position > last_in_new_list->GetPosition());
 
       UsePosition* new_use = new (allocator_) UsePosition(
           /* user */ nullptr,
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 2c73fb8..546dd65 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <type_traits>
+
 #include "assembler_thumb2.h"
 
 #include "base/bit_utils.h"
@@ -25,6 +27,30 @@
 namespace art {
 namespace arm {
 
+template <typename Function>
+void Thumb2Assembler::Fixup::ForExpandableDependencies(Thumb2Assembler* assembler, Function fn) {
+  static_assert(
+      std::is_same<typename std::result_of<Function(FixupId, FixupId)>::type, void>::value,
+      "Incorrect signature for argument `fn`: expected (FixupId, FixupId) -> void");
+  Fixup* fixups = assembler->fixups_.data();
+  for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) {
+    uint32_t target = fixups[fixup_id].target_;
+    if (target > fixups[fixup_id].location_) {
+      for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
+        if (fixups[id].CanExpand()) {
+          fn(id, fixup_id);
+        }
+      }
+    } else {
+      for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) {
+        if (fixups[id - 1u].CanExpand()) {
+          fn(id - 1u, fixup_id);
+        }
+      }
+    }
+  }
+}
+
 void Thumb2Assembler::Fixup::PrepareDependents(Thumb2Assembler* assembler) {
   // For each Fixup, it's easy to find the Fixups that it depends on as they are either
   // the following or the preceding Fixups until we find the target. However, for fixup
@@ -34,24 +60,16 @@
   // index and count. (Instead of having a per-fixup vector.)
 
   // Count the number of dependents of each Fixup.
-  const FixupId end_id = assembler->fixups_.size();
   Fixup* fixups = assembler->fixups_.data();
-  for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
-    uint32_t target = fixups[fixup_id].target_;
-    if (target > fixups[fixup_id].location_) {
-      for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
-        fixups[id].dependents_count_ += 1u;
-      }
-    } else {
-      for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) {
-        fixups[id - 1u].dependents_count_ += 1u;
-      }
-    }
-  }
+  ForExpandableDependencies(
+      assembler,
+      [fixups](FixupId dependency, FixupId dependent ATTRIBUTE_UNUSED) {
+        fixups[dependency].dependents_count_ += 1u;
+      });
   // Assign index ranges in fixup_dependents_ to individual fixups. Record the end of the
   // range in dependents_start_, we shall later decrement it as we fill in fixup_dependents_.
   uint32_t number_of_dependents = 0u;
-  for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
+  for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) {
     number_of_dependents += fixups[fixup_id].dependents_count_;
     fixups[fixup_id].dependents_start_ = number_of_dependents;
   }
@@ -61,20 +79,12 @@
   // Create and fill in the fixup_dependents_.
   assembler->fixup_dependents_.resize(number_of_dependents);
   FixupId* dependents = assembler->fixup_dependents_.data();
-  for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
-    uint32_t target = fixups[fixup_id].target_;
-    if (target > fixups[fixup_id].location_) {
-      for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
-        fixups[id].dependents_start_ -= 1u;
-        dependents[fixups[id].dependents_start_] = fixup_id;
-      }
-    } else {
-      for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) {
-        fixups[id - 1u].dependents_start_ -= 1u;
-        dependents[fixups[id - 1u].dependents_start_] = fixup_id;
-      }
-    }
-  }
+  ForExpandableDependencies(
+      assembler,
+      [fixups, dependents](FixupId dependency, FixupId dependent) {
+        fixups[dependency].dependents_start_ -= 1u;
+        dependents[fixups[dependency].dependents_start_] = dependent;
+      });
 }
 
 void Thumb2Assembler::BindLabel(Label* label, uint32_t bound_pc) {
@@ -115,6 +125,7 @@
                                           std::deque<FixupId>* fixups_to_recalculate) {
   uint32_t adjustment = fixup->AdjustSizeIfNeeded(*current_code_size);
   if (adjustment != 0u) {
+    DCHECK(fixup->CanExpand());
     *current_code_size += adjustment;
     for (FixupId dependent_id : fixup->Dependents(*this)) {
       Fixup* dependent = GetFixup(dependent_id);
@@ -2546,9 +2557,19 @@
       }
     } else {
       branch_type = Fixup::kUnconditional;             // B.
+      // The T2 encoding offset is `SignExtend(imm11:'0', 32)` and there is a PC adjustment of 4.
+      static constexpr size_t kMaxT2BackwardDistance = (1u << 11) - 4u;
+      if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT2BackwardDistance) {
+        use32bit = true;
+      }
     }
   } else {
     branch_type = Fixup::kConditional;                 // B<cond>.
+    // The T1 encoding offset is `SignExtend(imm8:'0', 32)` and there is a PC adjustment of 4.
+    static constexpr size_t kMaxT1BackwardDistance = (1u << 8) - 4u;
+    if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT1BackwardDistance) {
+      use32bit = true;
+    }
   }
 
   Fixup::Size size = use32bit ? Fixup::kBranch32Bit : Fixup::kBranch16Bit;
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 111a6b0..ce310a4 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -538,6 +538,20 @@
       return GetType() >= kLoadLiteralNarrow;
     }
 
+    // Returns whether the Fixup can expand from the original size.
+    bool CanExpand() const {
+      switch (GetOriginalSize()) {
+        case kBranch32Bit:
+        case kCbxz48Bit:
+        case kLiteralFar:
+        case kLiteralAddrFar:
+        case kLongOrFPLiteralFar:
+          return false;
+        default:
+          return true;
+      }
+    }
+
     Size GetOriginalSize() const {
       return original_size_;
     }
@@ -611,6 +625,7 @@
           dependents_count_(0u),
           dependents_start_(0u) {
     }
+
     static size_t SizeInBytes(Size size);
 
     // The size of padding added before the literal pool.
@@ -623,6 +638,9 @@
 
     int32_t LoadWideOrFpEncoding(Register rbase, int32_t offset) const;
 
+    template <typename Function>
+    static void ForExpandableDependencies(Thumb2Assembler* assembler, Function fn);
+
     static constexpr uint32_t kUnresolved = 0xffffffff;     // Value for target_ for unresolved.
 
     const Register rn_;   // Rn for cbnz/cbz, Rt for literal loads.
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 650b089..b5cafcb 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -372,6 +372,31 @@
   DriverStr(expected, "StoreWordPairToNonThumbOffset");
 }
 
+TEST_F(AssemblerThumb2Test, DistantBackBranch) {
+  Label start, end;
+  __ Bind(&start);
+  constexpr size_t kLdrR0R0Count1 = 256;
+  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
+    __ ldr(arm::R0, arm::Address(arm::R0));
+  }
+  __ b(&end, arm::EQ);
+  __ b(&start, arm::LT);
+  constexpr size_t kLdrR0R0Count2 = 256;
+  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
+    __ ldr(arm::R0, arm::Address(arm::R0));
+  }
+  __ Bind(&end);
+
+  std::string expected =
+      "0:\n" +
+      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
+      "beq 1f\n"
+      "blt 0b\n" +
+      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+      "1:\n";
+  DriverStr(expected, "DistantBackBranch");
+}
+
 TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
   Label label0, label1, label2;
   __ cbz(arm::R0, &label1);
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 370583e..be38336 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1526,6 +1526,7 @@
                                      instruction_set_,
                                      instruction_set_features_.get(),
                                      IsBootImage(),
+                                     IsAppImage(),
                                      image_classes_.release(),
                                      compiled_classes_.release(),
                                      /* compiled_methods */ nullptr,
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 3c6a05d..6ecfc74 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1416,11 +1416,10 @@
       indent_os << "\n";
       // TODO: Dump fields.
       // Dump methods after.
-      const auto& methods_section = image_header_.GetMethodsSection();
       DumpArtMethodVisitor visitor(this);
-      methods_section.VisitPackedArtMethods(&visitor,
-                                            image_space_.Begin(),
-                                            image_header_.GetPointerSize());
+      image_header_.VisitPackedArtMethods(&visitor,
+                                          image_space_.Begin(),
+                                          image_header_.GetPointerSize());
       // Dump the large objects separately.
       heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
       indent_os << "\n";
@@ -1779,6 +1778,7 @@
     DCHECK(method != nullptr);
     const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
     const void* quick_oat_code_end = GetQuickOatCodeEnd(method);
+    const size_t pointer_size = image_header_.GetPointerSize();
     OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
         reinterpret_cast<uintptr_t>(quick_oat_code_begin) - sizeof(OatQuickMethodHeader));
     if (method->IsNative()) {
@@ -1792,13 +1792,16 @@
           image_header_.GetPointerSize())) {
         indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
       }
-    } else if (method->IsAbstract() ||
-               method->IsCalleeSaveMethod() ||
-               method->IsResolutionMethod() ||
-               (method == Runtime::Current()->GetImtConflictMethod()) ||
-               method->IsImtUnimplementedMethod() ||
-               method->IsClassInitializer()) {
+    } else if (method->IsAbstract() || method->IsClassInitializer()) {
       // Don't print information for these.
+    } else if (method->IsRuntimeMethod()) {
+      ImtConflictTable* table = method->GetImtConflictTable(image_header_.GetPointerSize());
+      if (table != nullptr) {
+        indent_os << "IMT conflict table " << table << " method: ";
+        for (size_t i = 0, count = table->NumEntries(pointer_size); i < count; ++i) {
+          indent_os << PrettyMethod(table->GetImplementationMethod(i, pointer_size)) << " ";
+        }
+      }
     } else {
       const DexFile::CodeItem* code_item = method->GetCodeItem();
       size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 93e40af..0a7ffda 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -472,8 +472,7 @@
 
 void PatchOat::PatchArtFields(const ImageHeader* image_header) {
   PatchOatArtFieldVisitor visitor(this);
-  const auto& section = image_header->GetImageSection(ImageHeader::kSectionArtFields);
-  section.VisitPackedArtFields(&visitor, heap_->Begin());
+  image_header->VisitPackedArtFields(&visitor, heap_->Begin());
 }
 
 class PatchOatArtMethodVisitor : public ArtMethodVisitor {
@@ -490,10 +489,20 @@
 };
 
 void PatchOat::PatchArtMethods(const ImageHeader* image_header) {
-  const auto& section = image_header->GetMethodsSection();
   const size_t pointer_size = InstructionSetPointerSize(isa_);
   PatchOatArtMethodVisitor visitor(this);
-  section.VisitPackedArtMethods(&visitor, heap_->Begin(), pointer_size);
+  image_header->VisitPackedArtMethods(&visitor, heap_->Begin(), pointer_size);
+}
+
+void PatchOat::PatchImtConflictTables(const ImageHeader* image_header) {
+  const size_t pointer_size = InstructionSetPointerSize(isa_);
+  // We can safely walk target image since the conflict tables are independent.
+  image_header->VisitPackedImtConflictTables(
+      [this](ArtMethod* method) {
+        return RelocatedAddressOfPointer(method);
+      },
+      image_->Begin(),
+      pointer_size);
 }
 
 class FixupRootVisitor : public RootVisitor {
@@ -627,6 +636,7 @@
 
   PatchArtFields(image_header);
   PatchArtMethods(image_header);
+  PatchImtConflictTables(image_header);
   PatchInternedStrings(image_header);
   PatchClassTable(image_header);
   // Patch dex file int/long arrays which point to ArtFields.
@@ -725,6 +735,7 @@
       RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes(pointer_size)), pointer_size);
   copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer(
       object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size);
+  // No special handling for IMT conflict table since all pointers are moved by the same offset.
   copy->SetEntryPointFromJniPtrSize(RelocatedAddressOfPointer(
       object->GetEntryPointFromJniPtrSize(pointer_size)), pointer_size);
 }
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 510ff1e..3ef837f 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -117,6 +117,8 @@
   bool PatchImage(bool primary_image) SHARED_REQUIRES(Locks::mutator_lock_);
   void PatchArtFields(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_);
   void PatchArtMethods(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_);
+  void PatchImtConflictTables(const ImageHeader* image_header)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void PatchInternedStrings(const ImageHeader* image_header)
       SHARED_REQUIRES(Locks::mutator_lock_);
   void PatchClassTable(const ImageHeader* image_header)
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index e358ff8..f0e9ac5 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -17,6 +17,7 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/math_entrypoints.h"
@@ -47,67 +48,12 @@
 extern "C" int64_t __aeabi_ldivmod(int64_t, int64_t);
 
 void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
-  // JNI
-  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
-  // Alloc
-  ResetQuickAllocEntryPoints(qpoints);
+  DefaultInitEntryPoints(jpoints, qpoints);
 
   // Cast
   qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
   qpoints->pCheckCast = art_quick_check_cast;
 
-  // DexCache
-  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
-  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
-  qpoints->pInitializeType = art_quick_initialize_type;
-  qpoints->pResolveString = art_quick_resolve_string;
-
-  // Field
-  qpoints->pSet8Instance = art_quick_set8_instance;
-  qpoints->pSet8Static = art_quick_set8_static;
-  qpoints->pSet16Instance = art_quick_set16_instance;
-  qpoints->pSet16Static = art_quick_set16_static;
-  qpoints->pSet32Instance = art_quick_set32_instance;
-  qpoints->pSet32Static = art_quick_set32_static;
-  qpoints->pSet64Instance = art_quick_set64_instance;
-  qpoints->pSet64Static = art_quick_set64_static;
-  qpoints->pSetObjInstance = art_quick_set_obj_instance;
-  qpoints->pSetObjStatic = art_quick_set_obj_static;
-  qpoints->pGetByteInstance = art_quick_get_byte_instance;
-  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
-  qpoints->pGetShortInstance = art_quick_get_short_instance;
-  qpoints->pGetCharInstance = art_quick_get_char_instance;
-  qpoints->pGet32Instance = art_quick_get32_instance;
-  qpoints->pGet64Instance = art_quick_get64_instance;
-  qpoints->pGetObjInstance = art_quick_get_obj_instance;
-  qpoints->pGetByteStatic = art_quick_get_byte_static;
-  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
-  qpoints->pGetShortStatic = art_quick_get_short_static;
-  qpoints->pGetCharStatic = art_quick_get_char_static;
-  qpoints->pGet32Static = art_quick_get32_static;
-  qpoints->pGet64Static = art_quick_get64_static;
-  qpoints->pGetObjStatic = art_quick_get_obj_static;
-
-  // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
-  qpoints->pAputObject = art_quick_aput_obj;
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
-  // JNI
-  qpoints->pJniMethodStart = JniMethodStart;
-  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
-  qpoints->pJniMethodEnd = JniMethodEnd;
-  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
-  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
-  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
-  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
-  // Locks
-  qpoints->pLockObject = art_quick_lock_object;
-  qpoints->pUnlockObject = art_quick_unlock_object;
-
   // Math
   qpoints->pIdivmod = __aeabi_idivmod;
   qpoints->pLdiv = __aeabi_ldivmod;
@@ -154,35 +100,6 @@
   qpoints->pStringCompareTo = art_quick_string_compareto;
   qpoints->pMemcpy = memcpy;
 
-  // Invocation
-  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
-  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
-  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
-  qpoints->pInvokeDirectTrampolineWithAccessCheck =
-      art_quick_invoke_direct_trampoline_with_access_check;
-  qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
-      art_quick_invoke_interface_trampoline_with_access_check;
-  qpoints->pInvokeStaticTrampolineWithAccessCheck =
-      art_quick_invoke_static_trampoline_with_access_check;
-  qpoints->pInvokeSuperTrampolineWithAccessCheck =
-      art_quick_invoke_super_trampoline_with_access_check;
-  qpoints->pInvokeVirtualTrampolineWithAccessCheck =
-      art_quick_invoke_virtual_trampoline_with_access_check;
-
-  // Thread
-  qpoints->pTestSuspend = art_quick_test_suspend;
-
-  // Throws
-  qpoints->pDeliverException = art_quick_deliver_exception;
-  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
-  qpoints->pThrowDivZero = art_quick_throw_div_zero;
-  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
-  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
-  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
-  // Deoptimization from compiled code.
-  qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
   // Read barrier.
   qpoints->pReadBarrierJni = ReadBarrierJni;
   qpoints->pReadBarrierMark = artReadBarrierMark;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index e6ff0aa..321b9d2 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -544,6 +544,15 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_lock_object
 
+ENTRY art_quick_lock_object_no_inline
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2  @ save callee saves in case we block
+    mov    r1, r9                     @ pass Thread::Current
+    bl     artLockObjectFromCode      @ (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    RETURN_IF_RESULT_IS_ZERO
+    DELIVER_PENDING_EXCEPTION
+END art_quick_lock_object_no_inline
+
     /*
      * Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
      * r0 holds the possibly null object to lock.
@@ -601,6 +610,16 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_unlock_object
 
+ENTRY art_quick_unlock_object_no_inline
+    @ save callee saves in case exception allocation triggers GC
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2
+    mov    r1, r9                     @ pass Thread::Current
+    bl     artUnlockObjectFromCode    @ (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    RETURN_IF_RESULT_IS_ZERO
+    DELIVER_PENDING_EXCEPTION
+END art_quick_unlock_object_no_inline
+
     /*
      * Entry from managed code that calls artIsAssignableFromCode and on failure calls
      * artThrowClassCastException.
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 4db9411..1618ced 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -17,6 +17,7 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/math_entrypoints.h"
@@ -30,67 +31,12 @@
                                             const mirror::Class* ref_class);
 
 void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
-  // JNI
-  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
-  // Alloc
-  ResetQuickAllocEntryPoints(qpoints);
+  DefaultInitEntryPoints(jpoints, qpoints);
 
   // Cast
   qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
   qpoints->pCheckCast = art_quick_check_cast;
 
-  // DexCache
-  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
-  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
-  qpoints->pInitializeType = art_quick_initialize_type;
-  qpoints->pResolveString = art_quick_resolve_string;
-
-  // Field
-  qpoints->pSet8Instance = art_quick_set8_instance;
-  qpoints->pSet8Static = art_quick_set8_static;
-  qpoints->pSet16Instance = art_quick_set16_instance;
-  qpoints->pSet16Static = art_quick_set16_static;
-  qpoints->pSet32Instance = art_quick_set32_instance;
-  qpoints->pSet32Static = art_quick_set32_static;
-  qpoints->pSet64Instance = art_quick_set64_instance;
-  qpoints->pSet64Static = art_quick_set64_static;
-  qpoints->pSetObjInstance = art_quick_set_obj_instance;
-  qpoints->pSetObjStatic = art_quick_set_obj_static;
-  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
-  qpoints->pGetByteInstance = art_quick_get_byte_instance;
-  qpoints->pGetCharInstance = art_quick_get_char_instance;
-  qpoints->pGetShortInstance = art_quick_get_short_instance;
-  qpoints->pGet32Instance = art_quick_get32_instance;
-  qpoints->pGet64Instance = art_quick_get64_instance;
-  qpoints->pGetObjInstance = art_quick_get_obj_instance;
-  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
-  qpoints->pGetByteStatic = art_quick_get_byte_static;
-  qpoints->pGetCharStatic = art_quick_get_char_static;
-  qpoints->pGetShortStatic = art_quick_get_short_static;
-  qpoints->pGet32Static = art_quick_get32_static;
-  qpoints->pGet64Static = art_quick_get64_static;
-  qpoints->pGetObjStatic = art_quick_get_obj_static;
-
-  // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
-  qpoints->pAputObject = art_quick_aput_obj;
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
-  // JNI
-  qpoints->pJniMethodStart = JniMethodStart;
-  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
-  qpoints->pJniMethodEnd = JniMethodEnd;
-  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
-  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
-  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
-  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
-  // Locks
-  qpoints->pLockObject = art_quick_lock_object;
-  qpoints->pUnlockObject = art_quick_unlock_object;
-
   // Math
   // TODO null entrypoints not needed for ARM64 - generate inline.
   qpoints->pCmpgDouble = nullptr;
@@ -137,35 +83,6 @@
   qpoints->pStringCompareTo = art_quick_string_compareto;
   qpoints->pMemcpy = memcpy;
 
-  // Invocation
-  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
-  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
-  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
-  qpoints->pInvokeDirectTrampolineWithAccessCheck =
-      art_quick_invoke_direct_trampoline_with_access_check;
-  qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
-      art_quick_invoke_interface_trampoline_with_access_check;
-  qpoints->pInvokeStaticTrampolineWithAccessCheck =
-      art_quick_invoke_static_trampoline_with_access_check;
-  qpoints->pInvokeSuperTrampolineWithAccessCheck =
-      art_quick_invoke_super_trampoline_with_access_check;
-  qpoints->pInvokeVirtualTrampolineWithAccessCheck =
-      art_quick_invoke_virtual_trampoline_with_access_check;
-
-  // Thread
-  qpoints->pTestSuspend = art_quick_test_suspend;
-
-  // Throws
-  qpoints->pDeliverException = art_quick_deliver_exception;
-  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
-  qpoints->pThrowDivZero = art_quick_throw_div_zero;
-  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
-  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
-  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
-  // Deoptimization from compiled code.
-  qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
   // Read barrier.
   qpoints->pReadBarrierJni = ReadBarrierJni;
   qpoints->pReadBarrierMark = artReadBarrierMark;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 613bb5c..cad13b2 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -39,7 +39,7 @@
   if (!needs_a53_835769_fix) {
     // Check to see if this is an expected variant.
     static const char* arm64_known_variants[] = {
-        "denver64", "kryo"
+        "denver64", "kryo", "exynos-m1"
     };
     if (!FindVariantInArray(arm64_known_variants, arraysize(arm64_known_variants), variant)) {
       std::ostringstream os;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 1cdda2d..7774106 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1113,6 +1113,14 @@
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_lock_object
 
+ENTRY art_quick_lock_object_no_inline
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case we block
+    mov    x1, xSELF                  // pass Thread::Current
+    bl     artLockObjectFromCode      // (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    RETURN_IF_W0_IS_ZERO_OR_DELIVER
+END art_quick_lock_object_no_inline
+
     /*
      * Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
      * x0 holds the possibly null object to lock.
@@ -1171,6 +1179,14 @@
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_unlock_object
 
+ENTRY art_quick_unlock_object_no_inline
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case exception allocation triggers GC
+    mov    x1, xSELF                  // pass Thread::Current
+    bl     artUnlockObjectFromCode    // (Object* obj, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    RETURN_IF_W0_IS_ZERO_OR_DELIVER
+END art_quick_unlock_object_no_inline
+
     /*
      * Entry from managed code that calls artIsAssignableFromCode and on failure calls
      * artThrowClassCastException.
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 51eb77f..45e33a8 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -59,6 +59,9 @@
 extern "C" int64_t __moddi3(int64_t, int64_t);
 
 void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
+  // Note: MIPS has asserts checking for the type of entrypoint. Don't move it
+  //       to InitDefaultEntryPoints().
+
   // JNI
   jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
 
@@ -167,9 +170,14 @@
                 "Non-direct C stub marked direct.");
 
   // Locks
-  qpoints->pLockObject = art_quick_lock_object;
+  if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
+    qpoints->pLockObject = art_quick_lock_object_no_inline;
+    qpoints->pUnlockObject = art_quick_unlock_object_no_inline;
+  } else {
+    qpoints->pLockObject = art_quick_lock_object;
+    qpoints->pUnlockObject = art_quick_unlock_object;
+  }
   static_assert(!IsDirectEntrypoint(kQuickLockObject), "Non-direct C stub marked direct.");
-  qpoints->pUnlockObject = art_quick_unlock_object;
   static_assert(!IsDirectEntrypoint(kQuickUnlockObject), "Non-direct C stub marked direct.");
 
   // Math
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 8939a48..3ee26af 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -906,6 +906,16 @@
     RETURN_IF_ZERO
 END art_quick_lock_object
 
+ENTRY art_quick_lock_object_no_inline
+    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+    nop
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case we block
+    la      $t9, artLockObjectFromCode
+    jalr    $t9                           # (Object* obj, Thread*)
+    move    $a1, rSELF                    # pass Thread::Current
+    RETURN_IF_ZERO
+END art_quick_lock_object_no_inline
+
     /*
      * Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
      */
@@ -920,6 +930,16 @@
     RETURN_IF_ZERO
 END art_quick_unlock_object
 
+ENTRY art_quick_unlock_object_no_inline
+    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+    nop
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
+    la      $t9, artUnlockObjectFromCode
+    jalr    $t9                       # (Object* obj, Thread*)
+    move    $a1, rSELF                # pass Thread::Current
+    RETURN_IF_ZERO
+END art_quick_unlock_object_no_inline
+
     /*
      * Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
      */
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index 4bdb38e..030c127 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -18,6 +18,7 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/math_entrypoints.h"
@@ -57,67 +58,12 @@
 extern "C" int64_t __moddi3(int64_t, int64_t);
 
 void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
-  // JNI
-  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
-  // Alloc
-  ResetQuickAllocEntryPoints(qpoints);
+  DefaultInitEntryPoints(jpoints, qpoints);
 
   // Cast
   qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
   qpoints->pCheckCast = art_quick_check_cast;
 
-  // DexCache
-  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
-  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
-  qpoints->pInitializeType = art_quick_initialize_type;
-  qpoints->pResolveString = art_quick_resolve_string;
-
-  // Field
-  qpoints->pSet8Instance = art_quick_set8_instance;
-  qpoints->pSet8Static = art_quick_set8_static;
-  qpoints->pSet16Instance = art_quick_set16_instance;
-  qpoints->pSet16Static = art_quick_set16_static;
-  qpoints->pSet32Instance = art_quick_set32_instance;
-  qpoints->pSet32Static = art_quick_set32_static;
-  qpoints->pSet64Instance = art_quick_set64_instance;
-  qpoints->pSet64Static = art_quick_set64_static;
-  qpoints->pSetObjInstance = art_quick_set_obj_instance;
-  qpoints->pSetObjStatic = art_quick_set_obj_static;
-  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
-  qpoints->pGetByteInstance = art_quick_get_byte_instance;
-  qpoints->pGetCharInstance = art_quick_get_char_instance;
-  qpoints->pGetShortInstance = art_quick_get_short_instance;
-  qpoints->pGet32Instance = art_quick_get32_instance;
-  qpoints->pGet64Instance = art_quick_get64_instance;
-  qpoints->pGetObjInstance = art_quick_get_obj_instance;
-  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
-  qpoints->pGetByteStatic = art_quick_get_byte_static;
-  qpoints->pGetCharStatic = art_quick_get_char_static;
-  qpoints->pGetShortStatic = art_quick_get_short_static;
-  qpoints->pGet32Static = art_quick_get32_static;
-  qpoints->pGet64Static = art_quick_get64_static;
-  qpoints->pGetObjStatic = art_quick_get_obj_static;
-
-  // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
-  qpoints->pAputObject = art_quick_aput_obj;
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
-  // JNI
-  qpoints->pJniMethodStart = JniMethodStart;
-  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
-  qpoints->pJniMethodEnd = JniMethodEnd;
-  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
-  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
-  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
-  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
-  // Locks
-  qpoints->pLockObject = art_quick_lock_object;
-  qpoints->pUnlockObject = art_quick_unlock_object;
-
   // Math
   qpoints->pCmpgDouble = CmpgDouble;
   qpoints->pCmpgFloat = CmpgFloat;
@@ -144,35 +90,6 @@
   qpoints->pStringCompareTo = art_quick_string_compareto;
   qpoints->pMemcpy = memcpy;
 
-  // Invocation
-  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
-  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
-  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
-  qpoints->pInvokeDirectTrampolineWithAccessCheck =
-      art_quick_invoke_direct_trampoline_with_access_check;
-  qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
-      art_quick_invoke_interface_trampoline_with_access_check;
-  qpoints->pInvokeStaticTrampolineWithAccessCheck =
-      art_quick_invoke_static_trampoline_with_access_check;
-  qpoints->pInvokeSuperTrampolineWithAccessCheck =
-      art_quick_invoke_super_trampoline_with_access_check;
-  qpoints->pInvokeVirtualTrampolineWithAccessCheck =
-      art_quick_invoke_virtual_trampoline_with_access_check;
-
-  // Thread
-  qpoints->pTestSuspend = art_quick_test_suspend;
-
-  // Throws
-  qpoints->pDeliverException = art_quick_deliver_exception;
-  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
-  qpoints->pThrowDivZero = art_quick_throw_div_zero;
-  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
-  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
-  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
-  // Deoptimization from compiled code.
-  qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
   // TODO - use lld/scd instructions for Mips64
   // Atomic 64-bit load/store
   qpoints->pA64Load = QuasiAtomic::Read64;
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 5d0c94c..8f1a35a 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -971,6 +971,15 @@
     RETURN_IF_ZERO
 END art_quick_lock_object
 
+ENTRY art_quick_lock_object_no_inline
+    beq     $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+    nop
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case we block
+    jal     artLockObjectFromCode         # (Object* obj, Thread*)
+    move    $a1, rSELF                    # pass Thread::Current
+    RETURN_IF_ZERO
+END art_quick_lock_object_no_inline
+
     /*
      * Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
      */
@@ -984,6 +993,15 @@
     RETURN_IF_ZERO
 END art_quick_unlock_object
 
+ENTRY art_quick_unlock_object_no_inline
+    beq     $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+    nop
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  # save callee saves in case exception allocation triggers GC
+    jal     artUnlockObjectFromCode    # (Object* obj, Thread*)
+    move    $a1, rSELF                 # pass Thread::Current
+    RETURN_IF_ZERO
+END art_quick_unlock_object_no_inline
+
     /*
      * Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
      */
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 75d9073..3cdff55 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -2010,14 +2010,14 @@
   // that will create it: the runtime stub expects to be called by compiled code.
   LinearAlloc* linear_alloc = Runtime::Current()->GetLinearAlloc();
   ArtMethod* conflict_method = Runtime::Current()->CreateImtConflictMethod(linear_alloc);
-  static ImtConflictTable::Entry empty_entry = { nullptr, nullptr };
-  ImtConflictTable* empty_conflict_table = reinterpret_cast<ImtConflictTable*>(&empty_entry);
+  ImtConflictTable* empty_conflict_table =
+      Runtime::Current()->GetClassLinker()->CreateImtConflictTable(/*count*/0u, linear_alloc);
   void* data = linear_alloc->Alloc(
       self,
-      ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table));
+      ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table, sizeof(void*)));
   ImtConflictTable* new_table = new (data) ImtConflictTable(
-      empty_conflict_table, inf_contains, contains_amethod);
-  conflict_method->SetImtConflictTable(new_table);
+      empty_conflict_table, inf_contains, contains_amethod, sizeof(void*));
+  conflict_method->SetImtConflictTable(new_table, sizeof(void*));
 
   size_t result =
       Invoke3WithReferrerAndHidden(reinterpret_cast<size_t>(conflict_method),
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index e593f39..15a8571 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -17,6 +17,7 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "interpreter/interpreter.h"
@@ -33,67 +34,12 @@
 extern "C" mirror::Object* art_quick_read_barrier_for_root_slow(GcRoot<mirror::Object>*);
 
 void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
-  // JNI
-  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
-  // Alloc
-  ResetQuickAllocEntryPoints(qpoints);
+  DefaultInitEntryPoints(jpoints, qpoints);
 
   // Cast
   qpoints->pInstanceofNonTrivial = art_quick_is_assignable;
   qpoints->pCheckCast = art_quick_check_cast;
 
-  // DexCache
-  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
-  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
-  qpoints->pInitializeType = art_quick_initialize_type;
-  qpoints->pResolveString = art_quick_resolve_string;
-
-  // Field
-  qpoints->pSet8Instance = art_quick_set8_instance;
-  qpoints->pSet8Static = art_quick_set8_static;
-  qpoints->pSet16Instance = art_quick_set16_instance;
-  qpoints->pSet16Static = art_quick_set16_static;
-  qpoints->pSet32Instance = art_quick_set32_instance;
-  qpoints->pSet32Static = art_quick_set32_static;
-  qpoints->pSet64Instance = art_quick_set64_instance;
-  qpoints->pSet64Static = art_quick_set64_static;
-  qpoints->pSetObjInstance = art_quick_set_obj_instance;
-  qpoints->pSetObjStatic = art_quick_set_obj_static;
-  qpoints->pGetByteInstance = art_quick_get_byte_instance;
-  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
-  qpoints->pGetShortInstance = art_quick_get_short_instance;
-  qpoints->pGetCharInstance = art_quick_get_char_instance;
-  qpoints->pGet32Instance = art_quick_get32_instance;
-  qpoints->pGet64Instance = art_quick_get64_instance;
-  qpoints->pGetObjInstance = art_quick_get_obj_instance;
-  qpoints->pGetByteStatic = art_quick_get_byte_static;
-  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
-  qpoints->pGetShortStatic = art_quick_get_short_static;
-  qpoints->pGetCharStatic = art_quick_get_char_static;
-  qpoints->pGet32Static = art_quick_get32_static;
-  qpoints->pGet64Static = art_quick_get64_static;
-  qpoints->pGetObjStatic = art_quick_get_obj_static;
-
-  // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
-  qpoints->pAputObject = art_quick_aput_obj;
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
-  // JNI
-  qpoints->pJniMethodStart = JniMethodStart;
-  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
-  qpoints->pJniMethodEnd = JniMethodEnd;
-  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
-  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
-  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
-  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
-  // Locks
-  qpoints->pLockObject = art_quick_lock_object;
-  qpoints->pUnlockObject = art_quick_unlock_object;
-
   // More math.
   qpoints->pCos = cos;
   qpoints->pSin = sin;
@@ -128,35 +74,6 @@
   qpoints->pStringCompareTo = art_quick_string_compareto;
   qpoints->pMemcpy = art_quick_memcpy;
 
-  // Invocation
-  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
-  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
-  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
-  qpoints->pInvokeDirectTrampolineWithAccessCheck =
-      art_quick_invoke_direct_trampoline_with_access_check;
-  qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
-      art_quick_invoke_interface_trampoline_with_access_check;
-  qpoints->pInvokeStaticTrampolineWithAccessCheck =
-      art_quick_invoke_static_trampoline_with_access_check;
-  qpoints->pInvokeSuperTrampolineWithAccessCheck =
-      art_quick_invoke_super_trampoline_with_access_check;
-  qpoints->pInvokeVirtualTrampolineWithAccessCheck =
-      art_quick_invoke_virtual_trampoline_with_access_check;
-
-  // Thread
-  qpoints->pTestSuspend = art_quick_test_suspend;
-
-  // Throws
-  qpoints->pDeliverException = art_quick_deliver_exception;
-  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
-  qpoints->pThrowDivZero = art_quick_throw_div_zero;
-  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
-  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
-  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
-  // Deoptimize
-  qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
   // Read barrier.
   qpoints->pReadBarrierJni = ReadBarrierJni;
   qpoints->pReadBarrierMark = art_quick_read_barrier_mark;
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 4f9b3f7..485da9f 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1075,6 +1075,22 @@
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_lock_object
 
+DEFINE_FUNCTION art_quick_lock_object_no_inline
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
+    // Outgoing argument set up
+    subl LITERAL(8), %esp                 // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
+    pushl %fs:THREAD_SELF_OFFSET          // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH eax                              // pass object
+    call SYMBOL(artLockObjectFromCode)    // artLockObjectFromCode(object, Thread*)
+    addl LITERAL(16), %esp                // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-16)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME   // restore frame up to return address
+    RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_lock_object_no_inline
+
+
 DEFINE_FUNCTION art_quick_unlock_object
     testl %eax, %eax                      // null check object/eax
     jz   .Lslow_unlock
@@ -1130,6 +1146,21 @@
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_unlock_object
 
+DEFINE_FUNCTION art_quick_unlock_object_no_inline
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
+    // Outgoing argument set up
+    subl LITERAL(8), %esp                 // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
+    pushl %fs:THREAD_SELF_OFFSET          // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH eax                              // pass object
+    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*)
+    addl LITERAL(16), %esp                // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-16)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME   // restore frame up to return address
+    RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_unlock_object_no_inline
+
 DEFINE_FUNCTION art_quick_is_assignable
     PUSH eax                              // alignment padding
     PUSH ecx                              // pass arg2 - obj->klass
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 0a5d14a..bd6df70 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -17,6 +17,9 @@
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
+#if !defined(__APPLE__)
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
+#endif
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/math_entrypoints.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
@@ -38,67 +41,12 @@
   UNUSED(jpoints, qpoints);
   UNIMPLEMENTED(FATAL);
 #else
-  // JNI
-  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
-  // Alloc
-  ResetQuickAllocEntryPoints(qpoints);
+  DefaultInitEntryPoints(jpoints, qpoints);
 
   // Cast
   qpoints->pInstanceofNonTrivial = art_quick_assignable_from_code;
   qpoints->pCheckCast = art_quick_check_cast;
 
-  // DexCache
-  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
-  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
-  qpoints->pInitializeType = art_quick_initialize_type;
-  qpoints->pResolveString = art_quick_resolve_string;
-
-  // Field
-  qpoints->pSet8Instance = art_quick_set8_instance;
-  qpoints->pSet8Static = art_quick_set8_static;
-  qpoints->pSet16Instance = art_quick_set16_instance;
-  qpoints->pSet16Static = art_quick_set16_static;
-  qpoints->pSet32Instance = art_quick_set32_instance;
-  qpoints->pSet32Static = art_quick_set32_static;
-  qpoints->pSet64Instance = art_quick_set64_instance;
-  qpoints->pSet64Static = art_quick_set64_static;
-  qpoints->pSetObjInstance = art_quick_set_obj_instance;
-  qpoints->pSetObjStatic = art_quick_set_obj_static;
-  qpoints->pGetByteInstance = art_quick_get_byte_instance;
-  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
-  qpoints->pGetShortInstance = art_quick_get_short_instance;
-  qpoints->pGetCharInstance = art_quick_get_char_instance;
-  qpoints->pGet32Instance = art_quick_get32_instance;
-  qpoints->pGet64Instance = art_quick_get64_instance;
-  qpoints->pGetObjInstance = art_quick_get_obj_instance;
-  qpoints->pGetByteStatic = art_quick_get_byte_static;
-  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
-  qpoints->pGetShortStatic = art_quick_get_short_static;
-  qpoints->pGetCharStatic = art_quick_get_char_static;
-  qpoints->pGet32Static = art_quick_get32_static;
-  qpoints->pGet64Static = art_quick_get64_static;
-  qpoints->pGetObjStatic = art_quick_get_obj_static;
-
-  // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
-  qpoints->pAputObject = art_quick_aput_obj;
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
-  // JNI
-  qpoints->pJniMethodStart = JniMethodStart;
-  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
-  qpoints->pJniMethodEnd = JniMethodEnd;
-  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
-  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
-  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
-  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
-  // Locks
-  qpoints->pLockObject = art_quick_lock_object;
-  qpoints->pUnlockObject = art_quick_unlock_object;
-
   // More math.
   qpoints->pCos = cos;
   qpoints->pSin = sin;
@@ -132,35 +80,6 @@
   qpoints->pStringCompareTo = art_quick_string_compareto;
   qpoints->pMemcpy = art_quick_memcpy;
 
-  // Invocation
-  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
-  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
-  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
-  qpoints->pInvokeDirectTrampolineWithAccessCheck =
-      art_quick_invoke_direct_trampoline_with_access_check;
-  qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
-      art_quick_invoke_interface_trampoline_with_access_check;
-  qpoints->pInvokeStaticTrampolineWithAccessCheck =
-      art_quick_invoke_static_trampoline_with_access_check;
-  qpoints->pInvokeSuperTrampolineWithAccessCheck =
-      art_quick_invoke_super_trampoline_with_access_check;
-  qpoints->pInvokeVirtualTrampolineWithAccessCheck =
-      art_quick_invoke_virtual_trampoline_with_access_check;
-
-  // Thread
-  qpoints->pTestSuspend = art_quick_test_suspend;
-
-  // Throws
-  qpoints->pDeliverException = art_quick_deliver_exception;
-  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
-  qpoints->pThrowDivZero = art_quick_throw_div_zero;
-  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
-  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
-  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
-  // Deoptimize
-  qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
   // Read barrier.
   qpoints->pReadBarrierJni = ReadBarrierJni;
   qpoints->pReadBarrierMark = art_quick_read_barrier_mark;
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 26e668e..562ee2d 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -994,6 +994,14 @@
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_lock_object
 
+DEFINE_FUNCTION art_quick_lock_object_no_inline
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+    movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
+    call SYMBOL(artLockObjectFromCode)    // artLockObjectFromCode(object, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME   // restore frame up to return address
+    RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_lock_object_no_inline
+
 DEFINE_FUNCTION art_quick_unlock_object
     testl %edi, %edi                      // null check object/edi
     jz   .Lslow_unlock
@@ -1037,6 +1045,14 @@
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_unlock_object
 
+DEFINE_FUNCTION art_quick_unlock_object_no_inline
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+    movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
+    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME   // restore frame up to return address
+    RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_unlock_object_no_inline
+
 DEFINE_FUNCTION art_quick_check_cast
     PUSH rdi                          // Save args for exc
     PUSH rsi
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 34d19d1..06156f5 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -276,7 +276,7 @@
 
       // Ensure that we won't be accidentally calling quick compiled code when -Xint.
       if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
-        CHECK(!runtime->UseJit());
+        CHECK(!runtime->UseJitCompilation());
         const void* oat_quick_code = runtime->GetClassLinker()->GetOatMethodQuickCodeFor(this);
         CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
             << "Don't call compiled code when -Xint " << PrettyMethod(this);
@@ -481,7 +481,7 @@
   // to the JIT code, but this would require taking the JIT code cache lock to notify
   // it, which we do not want at this level.
   Runtime* runtime = Runtime::Current();
-  if (runtime->GetJit() != nullptr) {
+  if (runtime->UseJitCompilation()) {
     if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
       SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
     }
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 08f0285..a012a5a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -41,6 +41,7 @@
 namespace mirror {
 class Array;
 class Class;
+class IfTable;
 class PointerArray;
 }  // namespace mirror
 
@@ -50,66 +51,151 @@
 // with the last entry being null to make an assembly implementation of a lookup
 // faster.
 class ImtConflictTable {
+  enum MethodIndex {
+    kMethodInterface,
+    kMethodImplementation,
+    kMethodCount,  // Number of elements in enum.
+  };
+
  public:
   // Build a new table copying `other` and adding the new entry formed of
   // the pair { `interface_method`, `implementation_method` }
   ImtConflictTable(ImtConflictTable* other,
                    ArtMethod* interface_method,
-                   ArtMethod* implementation_method) {
-    size_t index = 0;
-    while (other->entries_[index].interface_method != nullptr) {
-      entries_[index] = other->entries_[index];
-      index++;
+                   ArtMethod* implementation_method,
+                   size_t pointer_size) {
+    const size_t count = other->NumEntries(pointer_size);
+    for (size_t i = 0; i < count; ++i) {
+      SetInterfaceMethod(i, pointer_size, other->GetInterfaceMethod(i, pointer_size));
+      SetImplementationMethod(i, pointer_size, other->GetImplementationMethod(i, pointer_size));
     }
-    entries_[index].interface_method = interface_method;
-    entries_[index].implementation_method = implementation_method;
+    SetInterfaceMethod(count, pointer_size, interface_method);
+    SetImplementationMethod(count, pointer_size, implementation_method);
     // Add the null marker.
-    entries_[index + 1].interface_method = nullptr;
-    entries_[index + 1].implementation_method = nullptr;
+    SetInterfaceMethod(count + 1, pointer_size, nullptr);
+    SetImplementationMethod(count + 1, pointer_size, nullptr);
+  }
+
+  // num_entries excludes the header.
+  ImtConflictTable(size_t num_entries, size_t pointer_size) {
+    SetInterfaceMethod(num_entries, pointer_size, nullptr);
+    SetImplementationMethod(num_entries, pointer_size, nullptr);
+  }
+
+  // Set an entry at an index.
+  void SetInterfaceMethod(size_t index, size_t pointer_size, ArtMethod* method) {
+    SetMethod(index * kMethodCount + kMethodInterface, pointer_size, method);
+  }
+
+  void SetImplementationMethod(size_t index, size_t pointer_size, ArtMethod* method) {
+    SetMethod(index * kMethodCount + kMethodImplementation, pointer_size, method);
+  }
+
+  ArtMethod* GetInterfaceMethod(size_t index, size_t pointer_size) const {
+    return GetMethod(index * kMethodCount + kMethodInterface, pointer_size);
+  }
+
+  ArtMethod* GetImplementationMethod(size_t index, size_t pointer_size) const {
+    return GetMethod(index * kMethodCount + kMethodImplementation, pointer_size);
+  }
+
+  // Visit all of the entries.
+  // NO_THREAD_SAFETY_ANALYSIS for calling with held locks. Visitor is passed a pair of ArtMethod*
+  // and also returns one. The order is <interface, implementation>.
+  template<typename Visitor>
+  void Visit(const Visitor& visitor, size_t pointer_size) NO_THREAD_SAFETY_ANALYSIS {
+    uint32_t table_index = 0;
+    for (;;) {
+      ArtMethod* interface_method = GetInterfaceMethod(table_index, pointer_size);
+      if (interface_method == nullptr) {
+        break;
+      }
+      ArtMethod* implementation_method = GetImplementationMethod(table_index, pointer_size);
+      auto input = std::make_pair(interface_method, implementation_method);
+      std::pair<ArtMethod*, ArtMethod*> updated = visitor(input);
+      if (input.first != updated.first) {
+        SetInterfaceMethod(table_index, pointer_size, updated.first);
+      }
+      if (input.second != updated.second) {
+        SetImplementationMethod(table_index, pointer_size, updated.second);
+      }
+      ++table_index;
+    }
   }
 
   // Lookup the implementation ArtMethod associated to `interface_method`. Return null
   // if not found.
-  ArtMethod* Lookup(ArtMethod* interface_method) const {
+  ArtMethod* Lookup(ArtMethod* interface_method, size_t pointer_size) const {
     uint32_t table_index = 0;
-    ArtMethod* current_interface_method;
-    while ((current_interface_method = entries_[table_index].interface_method) != nullptr) {
-      if (current_interface_method == interface_method) {
-        return entries_[table_index].implementation_method;
+    for (;;) {
+      ArtMethod* current_interface_method = GetInterfaceMethod(table_index, pointer_size);
+      if (current_interface_method == nullptr) {
+        break;
       }
-      table_index++;
+      if (current_interface_method == interface_method) {
+        return GetImplementationMethod(table_index, pointer_size);
+      }
+      ++table_index;
     }
     return nullptr;
   }
 
-  // Compute the size in bytes taken by this table.
-  size_t ComputeSize() const {
+  // Compute the number of entries in this table.
+  size_t NumEntries(size_t pointer_size) const {
     uint32_t table_index = 0;
-    size_t total_size = 0;
-    while ((entries_[table_index].interface_method) != nullptr) {
-      total_size += sizeof(Entry);
-      table_index++;
+    while (GetInterfaceMethod(table_index, pointer_size) != nullptr) {
+      ++table_index;
     }
+    return table_index;
+  }
+
+  // Compute the size in bytes taken by this table.
+  size_t ComputeSize(size_t pointer_size) const {
     // Add the end marker.
-    return total_size + sizeof(Entry);
+    return ComputeSize(NumEntries(pointer_size), pointer_size);
   }
 
   // Compute the size in bytes needed for copying the given `table` and add
   // one more entry.
-  static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table) {
-    return table->ComputeSize() + sizeof(Entry);
+  static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table, size_t pointer_size) {
+    return table->ComputeSize(pointer_size) + EntrySize(pointer_size);
   }
 
-  struct Entry {
-    ArtMethod* interface_method;
-    ArtMethod* implementation_method;
-  };
+  // Compute size with a fixed number of entries.
+  static size_t ComputeSize(size_t num_entries, size_t pointer_size) {
+    return (num_entries + 1) * EntrySize(pointer_size);  // Add one for null terminator.
+  }
+
+  static size_t EntrySize(size_t pointer_size) {
+    return pointer_size * static_cast<size_t>(kMethodCount);
+  }
 
  private:
+  ArtMethod* GetMethod(size_t index, size_t pointer_size) const {
+    if (pointer_size == 8) {
+      return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data64_[index]));
+    } else {
+      DCHECK_EQ(pointer_size, 4u);
+      return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data32_[index]));
+    }
+  }
+
+  void SetMethod(size_t index, size_t pointer_size, ArtMethod* method) {
+    if (pointer_size == 8) {
+      data64_[index] = dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(method));
+    } else {
+      DCHECK_EQ(pointer_size, 4u);
+      data32_[index] = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(method));
+    }
+  }
+
   // Array of entries that the assembly stubs will iterate over. Note that this is
   // not fixed size, and we allocate data prior to calling the constructor
   // of ImtConflictTable.
-  Entry entries_[0];
+  union {
+    uint32_t data32_[0];
+    uint64_t data64_[0];
+  };
 
   DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
 };
@@ -265,6 +351,12 @@
     SetAccessFlags(GetAccessFlags() | kAccSkipAccessChecks);
   }
 
+  // Should this method be run in the interpreter and count locks (e.g., failed structured-
+  // locking verification)?
+  bool MustCountLocks() {
+    return (GetAccessFlags() & kAccMustCountLocks) != 0;
+  }
+
   // Returns true if this method could be overridden by a default method.
   bool IsOverridableByDefaultMethod() SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -351,7 +443,6 @@
 
   // Find the method that this method overrides.
   ArtMethod* FindOverriddenMethod(size_t pointer_size)
-      REQUIRES(Roles::uninterruptible_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Find the method index for this method within other_dexfile. If this method isn't present then
@@ -417,8 +508,8 @@
     return reinterpret_cast<ImtConflictTable*>(GetEntryPointFromJniPtrSize(pointer_size));
   }
 
-  ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table) {
-    SetEntryPointFromJniPtrSize(table, sizeof(void*));
+  ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table, size_t pointer_size) {
+    SetEntryPointFromJniPtrSize(table, pointer_size);
   }
 
   ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) {
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 5b6801f..b84e29f 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -321,24 +321,26 @@
   // and padding between allocations marked as inaccessible.
   size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
   ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
+  uint8_t* ret;
   if (UNLIKELY(rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
-    void* ret = AllocFromNewArena(rounded_bytes);
+    ret = AllocFromNewArena(rounded_bytes);
+    uint8_t* noaccess_begin = ret + bytes;
+    uint8_t* noaccess_end;
     if (ret == arena_head_->Begin()) {
       DCHECK(ptr_ - rounded_bytes == ret);
-      uint8_t* noaccess_begin = ptr_ - rounded_bytes + bytes;
-      MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, end_ - noaccess_begin);
+      noaccess_end = end_;
     } else {
       // We're still using the old arena but `ret` comes from a new one just after it.
       DCHECK(arena_head_->next_ != nullptr);
       DCHECK(ret == arena_head_->next_->Begin());
       DCHECK_EQ(rounded_bytes, arena_head_->next_->GetBytesAllocated());
-      uint8_t* noaccess_begin = arena_head_->next_->Begin() + bytes;
-      MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, arena_head_->next_->End() - noaccess_begin);
+      noaccess_end = arena_head_->next_->End();
     }
-    return ret;
+    MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, noaccess_end - noaccess_begin);
+  } else {
+    ret = ptr_;
+    ptr_ += rounded_bytes;
   }
-  uint8_t* ret = ptr_;
-  ptr_ += rounded_bytes;
   MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
   // Check that the memory is already zeroed out.
   DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; }));
@@ -351,7 +353,7 @@
   pool_->FreeArenaChain(arena_head_);
 }
 
-void* ArenaAllocator::AllocFromNewArena(size_t bytes) {
+uint8_t* ArenaAllocator::AllocFromNewArena(size_t bytes) {
   Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, bytes));
   DCHECK(new_arena != nullptr);
   DCHECK_LE(bytes, new_arena->Size());
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 4b745f0..6c1a898 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -364,7 +364,7 @@
 
  private:
   void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
-  void* AllocFromNewArena(size_t bytes);
+  uint8_t* AllocFromNewArena(size_t bytes);
 
   static constexpr size_t kAlignment = 8;
 
diff --git a/runtime/base/arena_allocator_test.cc b/runtime/base/arena_allocator_test.cc
index c07c0ed..9de3cc4 100644
--- a/runtime/base/arena_allocator_test.cc
+++ b/runtime/base/arena_allocator_test.cc
@@ -41,6 +41,28 @@
   EXPECT_EQ(2U, bv.GetStorageSize());
 }
 
+TEST_F(ArenaAllocatorTest, MakeDefined) {
+  // Regression test to make sure we mark the allocated area defined.
+  ArenaPool pool;
+  static constexpr size_t kSmallArraySize = 10;
+  static constexpr size_t kLargeArraySize = 50;
+  uint32_t* small_array;
+  {
+    // Allocate a small array from an arena and release it.
+    ArenaAllocator arena(&pool);
+    small_array = arena.AllocArray<uint32_t>(kSmallArraySize);
+    ASSERT_EQ(0u, small_array[kSmallArraySize - 1u]);
+  }
+  {
+    // Reuse the previous arena and allocate more than previous allocation including red zone.
+    ArenaAllocator arena(&pool);
+    uint32_t* large_array = arena.AllocArray<uint32_t>(kLargeArraySize);
+    ASSERT_EQ(0u, large_array[kLargeArraySize - 1u]);
+    // Verify that the allocation was made on the same arena.
+    ASSERT_EQ(small_array, large_array);
+  }
+}
+
 TEST_F(ArenaAllocatorTest, LargeAllocations) {
   {
     ArenaPool pool;
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 97280c3..3b5b8b5 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -56,6 +56,7 @@
   bool threads;
   bool verifier;
   bool image;
+  bool systrace_lock_logging;  // Enabled with "-verbose:sys-locks".
 };
 
 // Global log verbosity setting, initialized by InitLogging.
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 7e8a4a4..f3e260b 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -116,9 +116,10 @@
   return resolved_method;
 }
 
-inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(Thread* self,
-                                                                  uint32_t method_idx,
-                                                                  ArtMethod* referrer) {
+inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(
+    uint32_t method_idx,
+    Handle<mirror::DexCache> dex_cache,
+    Handle<mirror::ClassLoader> class_loader) {
   // NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is
   // because if we did so than an invoke-super could be incorrectly dispatched in cases where
   // GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass
@@ -127,15 +128,11 @@
   // interface (either miranda, default or conflict) we would incorrectly assume that is what we
   // want to invoke on, instead of the 'concrete' implementation that the direct superclass
   // contains.
-  mirror::Class* declaring_class = referrer->GetDeclaringClass();
-  StackHandleScope<2> hs(self);
-  Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
-  const DexFile* dex_file = h_dex_cache->GetDexFile();
+  const DexFile* dex_file = dex_cache->GetDexFile();
   const DexFile::MethodId& method = dex_file->GetMethodId(method_idx);
-  mirror::Class* resolved_type = h_dex_cache->GetResolvedType(method.class_idx_);
+  mirror::Class* resolved_type = dex_cache->GetResolvedType(method.class_idx_);
   if (UNLIKELY(resolved_type == nullptr)) {
-    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
-    resolved_type = ResolveType(*dex_file, method.class_idx_, h_dex_cache, class_loader);
+    resolved_type = ResolveType(*dex_file, method.class_idx_, dex_cache, class_loader);
   }
   return resolved_type;
 }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index fa0107a..d29d33a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -687,6 +687,9 @@
     self->AssertNoPendingException();
   }
 
+  // Create conflict tables that depend on the class linker.
+  runtime->FixupConflictTables();
+
   FinishInit(self);
 
   VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
@@ -773,9 +776,13 @@
     bool contains = false;
     for (gc::space::ImageSpace* space : spaces) {
       auto& header = space->GetImageHeader();
-      auto& methods = header.GetMethodsSection();
-      auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
-      contains |= methods.Contains(offset);
+      size_t offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
+
+      const ImageSection& methods = header.GetMethodsSection();
+      contains = contains || methods.Contains(offset);
+
+      const ImageSection& runtime_methods = header.GetRuntimeMethodsSection();
+      contains = contains || runtime_methods.Contains(offset);
     }
     CHECK(contains) << m << " not found";
   }
@@ -1438,20 +1445,14 @@
   if (*out_forward_dex_cache_array) {
     ScopedTrace timing("Fixup ArtMethod dex cache arrays");
     FixupArtMethodArrayVisitor visitor(header);
-    header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
-        &visitor,
-        space->Begin(),
-        sizeof(void*));
+    header.VisitPackedArtMethods(&visitor, space->Begin(), sizeof(void*));
     Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
   }
   if (kVerifyArtMethodDeclaringClasses) {
     ScopedTrace timing("Verify declaring classes");
     ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_);
     VerifyDeclaringClassVisitor visitor;
-    header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
-        &visitor,
-        space->Begin(),
-        sizeof(void*));
+    header.VisitPackedArtMethods(&visitor, space->Begin(), sizeof(void*));
   }
   return true;
 }
@@ -1729,9 +1730,8 @@
 
   // Set entry point to interpreter if in InterpretOnly mode.
   if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
-    const ImageSection& methods = header.GetMethodsSection();
     SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
-    methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
+    header.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
   }
 
   ClassTable* class_table = nullptr;
@@ -1800,10 +1800,7 @@
     // This verification needs to happen after the classes have been added to the class loader.
     // Since it ensures classes are in the class table.
     VerifyClassInTableArtMethodVisitor visitor2(class_table);
-    header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
-        &visitor2,
-        space->Begin(),
-        sizeof(void*));
+    header.VisitPackedArtMethods(&visitor2, space->Begin(), sizeof(void*));
   }
   VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
   return true;
@@ -2035,6 +2032,7 @@
   Runtime* const runtime = Runtime::Current();
   JavaVMExt* const vm = runtime->GetJavaVM();
   vm->DeleteWeakGlobalRef(self, data.weak_root);
+  // Notify the JIT that we need to remove the methods and/or profiling info.
   if (runtime->GetJit() != nullptr) {
     jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
     if (code_cache != nullptr) {
@@ -2752,7 +2750,7 @@
   }
 
   if (runtime->IsNativeDebuggable()) {
-    DCHECK(runtime->UseJit() && runtime->GetJit()->JitAtFirstUse());
+    DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
     // If we are doing native debugging, ignore application's AOT code,
     // since we want to JIT it with extra stackmaps for native debugging.
     // On the other hand, keep all AOT code from the boot image, since the
@@ -5961,16 +5959,49 @@
   }
 }
 
-// Sets imt_ref appropriately for LinkInterfaceMethods.
-// If there is no method in the imt location of imt_ref it will store the given method there.
-// Otherwise it will set the conflict method which will figure out which method to use during
-// runtime.
-static void SetIMTRef(ArtMethod* unimplemented_method,
-                      ArtMethod* imt_conflict_method,
-                      size_t image_pointer_size,
-                      ArtMethod* current_method,
-                      /*out*/ArtMethod** imt_ref)
-    SHARED_REQUIRES(Locks::mutator_lock_) {
+ArtMethod* ClassLinker::AddMethodToConflictTable(mirror::Class* klass,
+                                                 ArtMethod* conflict_method,
+                                                 ArtMethod* interface_method,
+                                                 ArtMethod* method,
+                                                 bool force_new_conflict_method) {
+  ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+  Runtime* const runtime = Runtime::Current();
+  LinearAlloc* linear_alloc = GetAllocatorForClassLoader(klass->GetClassLoader());
+  bool new_entry = conflict_method == runtime->GetImtConflictMethod() || force_new_conflict_method;
+
+  // Create a new entry if the existing one is the shared conflict method.
+  ArtMethod* new_conflict_method = new_entry
+      ? runtime->CreateImtConflictMethod(linear_alloc)
+      : conflict_method;
+
+  // Allocate a new table. Note that we will leak this table at the next conflict,
+  // but that's a tradeoff compared to making the table fixed size.
+  void* data = linear_alloc->Alloc(
+      Thread::Current(), ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table,
+                                                                       image_pointer_size_));
+  if (data == nullptr) {
+    LOG(ERROR) << "Failed to allocate conflict table";
+    return conflict_method;
+  }
+  ImtConflictTable* new_table = new (data) ImtConflictTable(current_table,
+                                                            interface_method,
+                                                            method,
+                                                            image_pointer_size_);
+
+  // Do a fence to ensure threads see the data in the table before it is assigned
+  // to the conflict method.
+  // Note that there is a race in the presence of multiple threads and we may leak
+  // memory from the LinearAlloc, but that's a tradeoff compared to using
+  // atomic operations.
+  QuasiAtomic::ThreadFenceRelease();
+  new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_);
+  return new_conflict_method;
+}
+
+void ClassLinker::SetIMTRef(ArtMethod* unimplemented_method,
+                            ArtMethod* imt_conflict_method,
+                            ArtMethod* current_method,
+                            /*out*/ArtMethod** imt_ref) {
   // Place method in imt if entry is empty, place conflict otherwise.
   if (*imt_ref == unimplemented_method) {
     *imt_ref = current_method;
@@ -5980,9 +6011,9 @@
     // Note that we have checked IsRuntimeMethod, as there may be multiple different
     // conflict methods.
     MethodNameAndSignatureComparator imt_comparator(
-        (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size));
+        (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size_));
     if (imt_comparator.HasSameNameAndSignature(
-          current_method->GetInterfaceMethodIfProxy(image_pointer_size))) {
+          current_method->GetInterfaceMethodIfProxy(image_pointer_size_))) {
       *imt_ref = current_method;
     } else {
       *imt_ref = imt_conflict_method;
@@ -5995,6 +6026,151 @@
   }
 }
 
+void ClassLinker::FillIMTAndConflictTables(mirror::Class* klass) {
+  DCHECK(klass->ShouldHaveEmbeddedImtAndVTable()) << PrettyClass(klass);
+  DCHECK(!klass->IsTemp()) << PrettyClass(klass);
+  ArtMethod* imt[mirror::Class::kImtSize];
+  Runtime* const runtime = Runtime::Current();
+  ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
+  ArtMethod* const conflict_method = runtime->GetImtConflictMethod();
+  std::fill_n(imt, arraysize(imt), unimplemented_method);
+  if (klass->GetIfTable() != nullptr) {
+    FillIMTFromIfTable(klass->GetIfTable(),
+                       unimplemented_method,
+                       conflict_method,
+                       klass,
+                       true,
+                       false,
+                       &imt[0]);
+  }
+  for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+    klass->SetEmbeddedImTableEntry(i, imt[i], image_pointer_size_);
+  }
+}
+
+static inline uint32_t GetIMTIndex(ArtMethod* interface_method)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  return interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+}
+
+ImtConflictTable* ClassLinker::CreateImtConflictTable(size_t count,
+                                                      LinearAlloc* linear_alloc,
+                                                      size_t image_pointer_size) {
+  void* data = linear_alloc->Alloc(Thread::Current(),
+                                   ImtConflictTable::ComputeSize(count,
+                                                                 image_pointer_size));
+  return (data != nullptr) ? new (data) ImtConflictTable(count, image_pointer_size) : nullptr;
+}
+
+ImtConflictTable* ClassLinker::CreateImtConflictTable(size_t count, LinearAlloc* linear_alloc) {
+  return CreateImtConflictTable(count, linear_alloc, image_pointer_size_);
+}
+
+void ClassLinker::FillIMTFromIfTable(mirror::IfTable* if_table,
+                                     ArtMethod* unimplemented_method,
+                                     ArtMethod* imt_conflict_method,
+                                     mirror::Class* klass,
+                                     bool create_conflict_tables,
+                                     bool ignore_copied_methods,
+                                     ArtMethod** imt) {
+  uint32_t conflict_counts[mirror::Class::kImtSize] = {};
+  for (size_t i = 0, length = if_table->Count(); i < length; ++i) {
+    mirror::Class* interface = if_table->GetInterface(i);
+    const size_t num_virtuals = interface->NumVirtualMethods();
+    const size_t method_array_count = if_table->GetMethodArrayCount(i);
+    // Virtual methods can be larger than the if table methods if there are default methods.
+    DCHECK_GE(num_virtuals, method_array_count);
+    if (kIsDebugBuild) {
+      if (klass->IsInterface()) {
+        DCHECK_EQ(method_array_count, 0u);
+      } else {
+        DCHECK_EQ(interface->NumDeclaredVirtualMethods(), method_array_count);
+      }
+    }
+    if (method_array_count == 0) {
+      continue;
+    }
+    auto* method_array = if_table->GetMethodArray(i);
+    for (size_t j = 0; j < method_array_count; ++j) {
+      ArtMethod* implementation_method =
+          method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
+      if (ignore_copied_methods && implementation_method->IsCopied()) {
+        continue;
+      }
+      DCHECK(implementation_method != nullptr);
+      // Miranda methods cannot be used to implement an interface method, but they are safe to put
+      // in the IMT since their entrypoint is the interface trampoline. If we put any copied methods
+      // or interface methods in the IMT here they will not create extra conflicts since we compare
+      // names and signatures in SetIMTRef.
+      ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
+      const uint32_t imt_index = GetIMTIndex(interface_method);
+
+      // There is only any conflicts if all of the interface methods for an IMT slot don't have
+      // the same implementation method, keep track of this to avoid creating a conflict table in
+      // this case.
+
+      // Conflict table size for each IMT slot.
+      ++conflict_counts[imt_index];
+
+      SetIMTRef(unimplemented_method,
+                imt_conflict_method,
+                implementation_method,
+                /*out*/&imt[imt_index]);
+    }
+  }
+
+  if (create_conflict_tables) {
+    // Create the conflict tables.
+    LinearAlloc* linear_alloc = GetAllocatorForClassLoader(klass->GetClassLoader());
+    for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+      size_t conflicts = conflict_counts[i];
+      if (imt[i] == imt_conflict_method) {
+        ImtConflictTable* new_table = CreateImtConflictTable(conflicts, linear_alloc);
+        if (new_table != nullptr) {
+          ArtMethod* new_conflict_method =
+              Runtime::Current()->CreateImtConflictMethod(linear_alloc);
+          new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_);
+          imt[i] = new_conflict_method;
+        } else {
+          LOG(ERROR) << "Failed to allocate conflict table";
+          imt[i] = imt_conflict_method;
+        }
+      } else {
+        DCHECK_NE(imt[i], imt_conflict_method);
+      }
+    }
+
+    for (size_t i = 0, length = if_table->Count(); i < length; ++i) {
+      mirror::Class* interface = if_table->GetInterface(i);
+      const size_t method_array_count = if_table->GetMethodArrayCount(i);
+      // Virtual methods can be larger than the if table methods if there are default methods.
+      if (method_array_count == 0) {
+        continue;
+      }
+      auto* method_array = if_table->GetMethodArray(i);
+      for (size_t j = 0; j < method_array_count; ++j) {
+        ArtMethod* implementation_method =
+            method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
+        if (ignore_copied_methods && implementation_method->IsCopied()) {
+          continue;
+        }
+        DCHECK(implementation_method != nullptr);
+        ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
+        const uint32_t imt_index = GetIMTIndex(interface_method);
+        if (!imt[imt_index]->IsRuntimeMethod() ||
+            imt[imt_index] == unimplemented_method ||
+            imt[imt_index] == imt_conflict_method) {
+          continue;
+        }
+        ImtConflictTable* table = imt[imt_index]->GetImtConflictTable(image_pointer_size_);
+        const size_t num_entries = table->NumEntries(image_pointer_size_);
+        table->SetInterfaceMethod(num_entries, image_pointer_size_, interface_method);
+        table->SetImplementationMethod(num_entries, image_pointer_size_, implementation_method);
+      }
+    }
+  }
+}
+
 // Simple helper function that checks that no subtypes of 'val' are contained within the 'classes'
 // set.
 static bool NotSubinterfaceOfAny(const std::unordered_set<mirror::Class*>& classes,
@@ -6230,48 +6406,28 @@
   }
 }
 
-static void FillImtFromSuperClass(Handle<mirror::Class> klass,
-                                  Handle<mirror::IfTable> iftable,
-                                  ArtMethod* unimplemented_method,
-                                  ArtMethod* imt_conflict_method,
-                                  ArtMethod** out_imt,
-                                  size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_) {
+void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
+                                        ArtMethod* unimplemented_method,
+                                        ArtMethod* imt_conflict_method,
+                                        ArtMethod** imt) {
   DCHECK(klass->HasSuperClass());
   mirror::Class* super_class = klass->GetSuperClass();
   if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
     for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
-      out_imt[i] = super_class->GetEmbeddedImTableEntry(i, pointer_size);
+      imt[i] = super_class->GetEmbeddedImTableEntry(i, image_pointer_size_);
     }
   } else {
     // No imt in the super class, need to reconstruct from the iftable.
     mirror::IfTable* if_table = super_class->GetIfTable();
-    const size_t length = super_class->GetIfTableCount();
-    for (size_t i = 0; i < length; ++i) {
-      mirror::Class* interface = iftable->GetInterface(i);
-      const size_t num_virtuals = interface->NumDeclaredVirtualMethods();
-      const size_t method_array_count = if_table->GetMethodArrayCount(i);
-      DCHECK_EQ(num_virtuals, method_array_count);
-      if (method_array_count == 0) {
-        continue;
-      }
-      auto* method_array = if_table->GetMethodArray(i);
-      for (size_t j = 0; j < num_virtuals; ++j) {
-        auto method = method_array->GetElementPtrSize<ArtMethod*>(j, pointer_size);
-        DCHECK(method != nullptr) << PrettyClass(super_class);
-        // Miranda methods cannot be used to implement an interface method and defaults should be
-        // skipped in case we override it.
-        if (method->IsDefault() || method->IsMiranda()) {
-          continue;
-        }
-        ArtMethod* interface_method = interface->GetVirtualMethod(j, pointer_size);
-        uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
-        auto** imt_ref = &out_imt[imt_index];
-        if (*imt_ref == unimplemented_method) {
-          *imt_ref = method;
-        } else if (*imt_ref != imt_conflict_method) {
-          *imt_ref = imt_conflict_method;
-        }
-      }
+    if (if_table != nullptr) {
+      // Ignore copied methods since we will handle these in LinkInterfaceMethods.
+      FillIMTFromIfTable(if_table,
+                         unimplemented_method,
+                         imt_conflict_method,
+                         klass.Get(),
+                         /*create_conflict_table*/false,
+                         /*ignore_copied_methods*/true,
+                         /*out*/imt);
     }
   }
 }
@@ -6314,13 +6470,10 @@
   const bool extend_super_iftable = has_superclass;
   if (has_superclass && fill_tables) {
     FillImtFromSuperClass(klass,
-                          iftable,
                           unimplemented_method,
                           imt_conflict_method,
-                          out_imt,
-                          image_pointer_size_);
+                          out_imt);
   }
-
   // Allocate method arrays before since we don't want miss visiting miranda method roots due to
   // thread suspension.
   if (fill_tables) {
@@ -6404,7 +6557,7 @@
         auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
         MethodNameAndSignatureComparator interface_name_comparator(
             interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
-        uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+        uint32_t imt_index = GetIMTIndex(interface_method);
         ArtMethod** imt_ptr = &out_imt[imt_index];
         // For each method listed in the interface's method list, find the
         // matching method in our class's method list.  We want to favor the
@@ -6449,7 +6602,6 @@
                 // Place method in imt if entry is empty, place conflict otherwise.
                 SetIMTRef(unimplemented_method,
                           imt_conflict_method,
-                          image_pointer_size_,
                           vtable_method,
                           /*out*/imt_ptr);
               }
@@ -6483,6 +6635,15 @@
             // The method is not overridable by a default method (i.e. it is directly implemented
             // in some class). Therefore move onto the next interface method.
             continue;
+          } else {
+            // If the super-classes method is override-able by a default method we need to keep
+            // track of it since though it is override-able it is not guaranteed to be 'overridden'.
+            // If it turns out not to be overridden and we did not keep track of it we might add it
+            // to the vtable twice, causing corruption in this class and possibly any subclasses.
+            DCHECK(vtable_impl == nullptr || vtable_impl == supers_method)
+                << "vtable_impl was " << PrettyMethod(vtable_impl) << " and not 'nullptr' or "
+                << PrettyMethod(supers_method) << " as expected. IFTable appears to be corrupt!";
+            vtable_impl = supers_method;
           }
         }
         // If we haven't found it yet we should search through the interfaces for default methods.
@@ -6581,7 +6742,6 @@
             method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
             SetIMTRef(unimplemented_method,
                       imt_conflict_method,
-                      image_pointer_size_,
                       current_method,
                       /*out*/imt_ptr);
           }
@@ -7835,6 +7995,7 @@
     VLOG(class_linker) << "Collecting class profile for dex file " << location
                        << " types=" << num_types << " class_defs=" << num_class_defs;
     DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(),
+                                             dex_file->GetBaseLocation(),
                                              dex_file->GetLocationChecksum());
     size_t num_resolved = 0;
     std::unordered_set<uint16_t> class_set;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5de502b..ece171c 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -53,6 +53,7 @@
   class StackTraceElement;
 }  // namespace mirror
 
+class ImtConflictTable;
 template<class T> class Handle;
 template<class T> class MutableHandle;
 class InternTable;
@@ -310,9 +311,9 @@
   // This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be
   // different then the declaring class of the resolved method due to copied
   // miranda/default/conflict methods.
-  mirror::Class* ResolveReferencedClassOfMethod(Thread* self,
-                                                uint32_t method_idx,
-                                                ArtMethod* referrer)
+  mirror::Class* ResolveReferencedClassOfMethod(uint32_t method_idx,
+                                                Handle<mirror::DexCache> dex_cache,
+                                                Handle<mirror::ClassLoader> class_loader)
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
   template <ResolveMode kResolveMode>
@@ -610,6 +611,26 @@
       const std::set<DexCacheResolvedClasses>& classes)
       REQUIRES(!dex_lock_);
 
+  ArtMethod* AddMethodToConflictTable(mirror::Class* klass,
+                                      ArtMethod* conflict_method,
+                                      ArtMethod* interface_method,
+                                      ArtMethod* method,
+                                      bool force_new_conflict_method)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Create a conflict table with a specified capacity.
+  ImtConflictTable* CreateImtConflictTable(size_t count, LinearAlloc* linear_alloc);
+
+  // Static version for when the class linker is not yet created.
+  static ImtConflictTable* CreateImtConflictTable(size_t count,
+                                                  LinearAlloc* linear_alloc,
+                                                  size_t pointer_size);
+
+
+  // Create the IMT and conflict tables for a class.
+  void FillIMTAndConflictTables(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
+
+
   struct DexCacheData {
     // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
     // not work properly.
@@ -1057,6 +1078,28 @@
       REQUIRES(!dex_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Sets imt_ref appropriately for LinkInterfaceMethods.
+  // If there is no method in the imt location of imt_ref it will store the given method there.
+  // Otherwise it will set the conflict method which will figure out which method to use during
+  // runtime.
+  void SetIMTRef(ArtMethod* unimplemented_method,
+                 ArtMethod* imt_conflict_method,
+                 ArtMethod* current_method,
+                 /*out*/ArtMethod** imt_ref) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  void FillIMTFromIfTable(mirror::IfTable* if_table,
+                          ArtMethod* unimplemented_method,
+                          ArtMethod* imt_conflict_method,
+                          mirror::Class* klass,
+                          bool create_conflict_tables,
+                          bool ignore_copied_methods,
+                          ArtMethod** imt) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  void FillImtFromSuperClass(Handle<mirror::Class> klass,
+                             ArtMethod* unimplemented_method,
+                             ArtMethod* imt_conflict_method,
+                             ArtMethod** imt) SHARED_REQUIRES(Locks::mutator_lock_);
+
   std::vector<const DexFile*> boot_class_path_;
   std::vector<std::unique_ptr<const DexFile>> boot_dex_files_;
 
diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h
index 80c12cb..0febbed 100644
--- a/runtime/dex_cache_resolved_classes.h
+++ b/runtime/dex_cache_resolved_classes.h
@@ -26,8 +26,11 @@
 // Data structure for passing around which classes belonging to a dex cache / dex file are resolved.
 class DexCacheResolvedClasses {
  public:
-  DexCacheResolvedClasses(const std::string& dex_location, uint32_t location_checksum)
+  DexCacheResolvedClasses(const std::string& dex_location,
+                          const std::string& base_location,
+                          uint32_t location_checksum)
       : dex_location_(dex_location),
+        base_location_(base_location),
         location_checksum_(location_checksum) {}
 
   // Only compare the key elements, ignore the resolved classes.
@@ -35,6 +38,7 @@
     if (location_checksum_ != other.location_checksum_) {
       return static_cast<int>(location_checksum_ - other.location_checksum_);
     }
+    // Don't need to compare base_location_ since dex_location_ has more info.
     return dex_location_.compare(other.dex_location_);
   }
 
@@ -47,6 +51,10 @@
     return dex_location_;
   }
 
+  const std::string& GetBaseLocation() const {
+    return base_location_;
+  }
+
   uint32_t GetLocationChecksum() const {
     return location_checksum_;
   }
@@ -57,6 +65,7 @@
 
  private:
   const std::string dex_location_;
+  const std::string base_location_;
   const uint32_t location_checksum_;
   // Array of resolved class def indexes.
   mutable std::unordered_set<uint16_t> classes_;
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 4e01d80..f3a0d2f 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -77,6 +77,10 @@
 extern "C" void art_quick_lock_object(art::mirror::Object*);
 extern "C" void art_quick_unlock_object(art::mirror::Object*);
 
+// Lock entrypoints that do not inline any behavior (e.g., thin-locks).
+extern "C" void art_quick_lock_object_no_inline(art::mirror::Object*);
+extern "C" void art_quick_unlock_object_no_inline(art::mirror::Object*);
+
 // Math entrypoints.
 extern "C" int64_t art_quick_d2l(double);
 extern "C" int64_t art_quick_f2l(float);
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
new file mode 100644
index 0000000..5dafa8b
--- /dev/null
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_
+#define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_
+
+#include "base/logging.h"
+#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "quick_alloc_entrypoints.h"
+#include "quick_default_externs.h"
+#include "quick_entrypoints.h"
+
+namespace art {
+
+void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
+  // JNI
+  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
+
+  // Alloc
+  ResetQuickAllocEntryPoints(qpoints);
+
+  // DexCache
+  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
+  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
+  qpoints->pInitializeType = art_quick_initialize_type;
+  qpoints->pResolveString = art_quick_resolve_string;
+
+  // Field
+  qpoints->pSet8Instance = art_quick_set8_instance;
+  qpoints->pSet8Static = art_quick_set8_static;
+  qpoints->pSet16Instance = art_quick_set16_instance;
+  qpoints->pSet16Static = art_quick_set16_static;
+  qpoints->pSet32Instance = art_quick_set32_instance;
+  qpoints->pSet32Static = art_quick_set32_static;
+  qpoints->pSet64Instance = art_quick_set64_instance;
+  qpoints->pSet64Static = art_quick_set64_static;
+  qpoints->pSetObjInstance = art_quick_set_obj_instance;
+  qpoints->pSetObjStatic = art_quick_set_obj_static;
+  qpoints->pGetByteInstance = art_quick_get_byte_instance;
+  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+  qpoints->pGetShortInstance = art_quick_get_short_instance;
+  qpoints->pGetCharInstance = art_quick_get_char_instance;
+  qpoints->pGet32Instance = art_quick_get32_instance;
+  qpoints->pGet64Instance = art_quick_get64_instance;
+  qpoints->pGetObjInstance = art_quick_get_obj_instance;
+  qpoints->pGetByteStatic = art_quick_get_byte_static;
+  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+  qpoints->pGetShortStatic = art_quick_get_short_static;
+  qpoints->pGetCharStatic = art_quick_get_char_static;
+  qpoints->pGet32Static = art_quick_get32_static;
+  qpoints->pGet64Static = art_quick_get64_static;
+  qpoints->pGetObjStatic = art_quick_get_obj_static;
+
+  // Array
+  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+  qpoints->pAputObject = art_quick_aput_obj;
+  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
+
+  // JNI
+  qpoints->pJniMethodStart = JniMethodStart;
+  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
+  qpoints->pJniMethodEnd = JniMethodEnd;
+  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
+  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
+  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
+
+  // Locks
+  if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
+    qpoints->pLockObject = art_quick_lock_object_no_inline;
+    qpoints->pUnlockObject = art_quick_unlock_object_no_inline;
+  } else {
+    qpoints->pLockObject = art_quick_lock_object;
+    qpoints->pUnlockObject = art_quick_unlock_object;
+  }
+
+  // Invocation
+  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
+  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
+  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
+  qpoints->pInvokeDirectTrampolineWithAccessCheck =
+      art_quick_invoke_direct_trampoline_with_access_check;
+  qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
+      art_quick_invoke_interface_trampoline_with_access_check;
+  qpoints->pInvokeStaticTrampolineWithAccessCheck =
+      art_quick_invoke_static_trampoline_with_access_check;
+  qpoints->pInvokeSuperTrampolineWithAccessCheck =
+      art_quick_invoke_super_trampoline_with_access_check;
+  qpoints->pInvokeVirtualTrampolineWithAccessCheck =
+      art_quick_invoke_virtual_trampoline_with_access_check;
+
+  // Thread
+  qpoints->pTestSuspend = art_quick_test_suspend;
+
+  // Throws
+  qpoints->pDeliverException = art_quick_deliver_exception;
+  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
+  qpoints->pThrowDivZero = art_quick_throw_div_zero;
+  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
+  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
+  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
+
+  // Deoptimize
+  qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index f3e8dba..e9cdbb7 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1038,9 +1038,14 @@
       } else {
         DCHECK_EQ(invoke_type, kSuper);
         CHECK(caller != nullptr) << invoke_type;
+        StackHandleScope<2> hs(self);
+        Handle<mirror::DexCache> dex_cache(
+            hs.NewHandle(caller->GetDeclaringClass()->GetDexCache()));
+        Handle<mirror::ClassLoader> class_loader(
+            hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader()));
         // TODO Maybe put this into a mirror::Class function.
         mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod(
-            self, called_method.dex_method_index, caller);
+            called_method.dex_method_index, dex_cache, class_loader);
         if (ref_class->IsInterface()) {
           called = ref_class->FindVirtualMethodForInterfaceSuper(called, sizeof(void*));
         } else {
@@ -2169,7 +2174,8 @@
         imt_index % mirror::Class::kImtSize, sizeof(void*));
     if (LIKELY(conflict_method->IsRuntimeMethod())) {
       ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
-      method = current_table->Lookup(interface_method);
+      DCHECK(current_table != nullptr);
+      method = current_table->Lookup(interface_method, sizeof(void*));
     } else {
       // It seems we aren't really a conflict method!
       method = cls->FindVirtualMethodForInterface(interface_method, sizeof(void*));
@@ -2220,34 +2226,13 @@
   ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
       imt_index % mirror::Class::kImtSize, sizeof(void*));
   if (conflict_method->IsRuntimeMethod()) {
-    ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
-    Runtime* runtime = Runtime::Current();
-    LinearAlloc* linear_alloc = (cls->GetClassLoader() == nullptr)
-        ? runtime->GetLinearAlloc()
-        : cls->GetClassLoader()->GetAllocator();
-    bool is_new_entry = (conflict_method == runtime->GetImtConflictMethod());
-
-    // Create a new entry if the existing one is the shared conflict method.
-    ArtMethod* new_conflict_method = is_new_entry
-        ? runtime->CreateImtConflictMethod(linear_alloc)
-        : conflict_method;
-
-    // Allocate a new table. Note that we will leak this table at the next conflict,
-    // but that's a tradeoff compared to making the table fixed size.
-    void* data = linear_alloc->Alloc(
-        self, ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table));
-    CHECK(data != nullptr) << "Out of memory";
-    ImtConflictTable* new_table = new (data) ImtConflictTable(
-        current_table, interface_method, method);
-
-    // Do a fence to ensure threads see the data in the table before it is assigned
-    // to the conlict method.
-    // Note that there is a race in the presence of multiple threads and we may leak
-    // memory from the LinearAlloc, but that's a tradeoff compared to using
-    // atomic operations.
-    QuasiAtomic::ThreadFenceRelease();
-    new_conflict_method->SetImtConflictTable(new_table);
-    if (is_new_entry) {
+    ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable(
+        cls.Get(),
+        conflict_method,
+        interface_method,
+        method,
+        /*force_new_conflict_method*/false);
+    if (new_conflict_method != conflict_method) {
       // Update the IMT if we create a new conflict method. No fence needed here, as the
       // data is consistent.
       cls->SetEmbeddedImTableEntry(imt_index % mirror::Class::kImtSize,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index c2f772f..df5aa0a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2687,8 +2687,8 @@
     concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
   }
 
-  if ((gc_type == collector::kGcTypeFull) && runtime->UseJit()) {
-    // It's time to clear all inline caches, in case some classes can be unloaded.
+  // It's time to clear all inline caches, in case some classes can be unloaded.
+  if ((gc_type == collector::kGcTypeFull) && (runtime->GetJit() != nullptr)) {
     runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self);
   }
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index d386c74..78c570f 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -880,7 +880,7 @@
 
 class ForwardObjectAdapter {
  public:
-  ALWAYS_INLINE ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
+  ALWAYS_INLINE explicit ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
 
   template <typename T>
   ALWAYS_INLINE T* operator()(T* src) const {
@@ -893,7 +893,7 @@
 
 class ForwardCodeAdapter {
  public:
-  ALWAYS_INLINE ForwardCodeAdapter(const FixupVisitor* visitor)
+  ALWAYS_INLINE explicit ForwardCodeAdapter(const FixupVisitor* visitor)
       : visitor_(visitor) {}
 
   template <typename T>
@@ -914,10 +914,26 @@
         pointer_size_(pointer_size) {}
 
   virtual void Visit(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS {
-    if (fixup_heap_objects_) {
-      method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
+    // TODO: Separate visitor for runtime vs normal methods.
+    if (UNLIKELY(method->IsRuntimeMethod())) {
+      ImtConflictTable* table = method->GetImtConflictTable(pointer_size_);
+      if (table != nullptr) {
+        ImtConflictTable* new_table = ForwardObject(table);
+        if (table != new_table) {
+          method->SetImtConflictTable(new_table, pointer_size_);
+        }
+      }
+      const void* old_code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+      const void* new_code = ForwardCode(old_code);
+      if (old_code != new_code) {
+        method->SetEntryPointFromQuickCompiledCodePtrSize(new_code, pointer_size_);
+      }
+    } else {
+      if (fixup_heap_objects_) {
+        method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
+      }
+      method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
     }
-    method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
   }
 
  private:
@@ -1018,6 +1034,7 @@
   const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects);
   uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
   uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+  FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
   if (fixup_image) {
     // Two pass approach, fix up all classes first, then fix up non class-objects.
     // The visited bitmap is used to ensure that pointer arrays are not forwarded twice.
@@ -1037,7 +1054,6 @@
     ScopedObjectAccess soa(Thread::Current());
     timing.NewTiming("Fixup objects");
     bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor);
-    FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
     // Fixup image roots.
     CHECK(app_image.InSource(reinterpret_cast<uintptr_t>(
         image_header.GetImageRoots<kWithoutReadBarrier>())));
@@ -1104,19 +1120,18 @@
                                          boot_oat,
                                          app_image,
                                          app_oat);
-    image_header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
-        &method_visitor,
-        target_base,
-        pointer_size);
+    image_header.VisitPackedArtMethods(&method_visitor, target_base, pointer_size);
   }
   if (fixup_image) {
     {
       // Only touches objects in the app image, no need for mutator lock.
       TimingLogger::ScopedTiming timing("Fixup fields", &logger);
       FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat);
-      image_header.GetImageSection(ImageHeader::kSectionArtFields).VisitPackedArtFields(
-          &field_visitor,
-          target_base);
+      image_header.VisitPackedArtFields(&field_visitor, target_base);
+    }
+    {
+      TimingLogger::ScopedTiming timing("Fixup conflict tables", &logger);
+      image_header.VisitPackedImtConflictTables(fixup_adapter, target_base, pointer_size);
     }
     // In the app image case, the image methods are actually in the boot image.
     image_header.RelocateImageMethods(boot_image.Delta());
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 3885c60..9895395 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -505,6 +505,7 @@
     // Walk the roots and the heap.
     output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
 
+    simple_roots_.clear();
     runtime->VisitRoots(this);
     runtime->VisitImageRoots(this);
     runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, this);
@@ -884,6 +885,14 @@
                      gc::EqAllocRecordTypesPtr<gc::AllocRecordStackTraceElement>> frames_;
   std::unordered_map<const mirror::Object*, const gc::AllocRecordStackTrace*> allocation_records_;
 
+  // Set used to keep track of what simple root records we have already
+  // emitted, to avoid emitting duplicate entries. The simple root records are
+  // those that contain no other information than the root type and the object
+  // id. A pair of root type and object id is packed into a uint64_t, with
+  // the root type in the upper 32 bits and the object id in the lower 32
+  // bits.
+  std::unordered_set<uint64_t> simple_roots_;
+
   friend class GcRootVisitor;
   DISALLOW_COPY_AND_ASSIGN(Hprof);
 };
@@ -962,10 +971,14 @@
     case HPROF_ROOT_MONITOR_USED:
     case HPROF_ROOT_INTERNED_STRING:
     case HPROF_ROOT_DEBUGGER:
-    case HPROF_ROOT_VM_INTERNAL:
-      __ AddU1(heap_tag);
-      __ AddObjectId(obj);
+    case HPROF_ROOT_VM_INTERNAL: {
+      uint64_t key = (static_cast<uint64_t>(heap_tag) << 32) | PointerToLowMemUInt32(obj);
+      if (simple_roots_.insert(key).second) {
+        __ AddU1(heap_tag);
+        __ AddObjectId(obj);
+      }
       break;
+    }
 
       // ID: object ID
       // ID: JNI global ref ID
diff --git a/runtime/image-inl.h b/runtime/image-inl.h
index e3307d8..ea75a62 100644
--- a/runtime/image-inl.h
+++ b/runtime/image-inl.h
@@ -19,6 +19,8 @@
 
 #include "image.h"
 
+#include "art_method.h"
+
 namespace art {
 
 template <ReadBarrierOption kReadBarrierOption>
@@ -42,6 +44,20 @@
   return image_roots;
 }
 
+template <typename Visitor>
+inline void ImageHeader::VisitPackedImtConflictTables(const Visitor& visitor,
+                                                      uint8_t* base,
+                                                      size_t pointer_size) const {
+  const ImageSection& section = GetImageSection(kSectionIMTConflictTables);
+  for (size_t pos = 0; pos < section.Size(); ) {
+    auto* table = reinterpret_cast<ImtConflictTable*>(base + section.Offset() + pos);
+    table->Visit([&visitor](const std::pair<ArtMethod*, ArtMethod*>& methods) {
+      return std::make_pair(visitor(methods.first), visitor(methods.second));
+    }, pointer_size);
+    pos += table->ComputeSize(pointer_size);
+  }
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_IMAGE_INL_H_
diff --git a/runtime/image.cc b/runtime/image.cc
index 1f54e3e..a9552c2 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '7', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '9', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
@@ -147,9 +147,10 @@
   return os << "size=" << section.Size() << " range=" << section.Offset() << "-" << section.End();
 }
 
-void ImageSection::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const {
-  for (size_t pos = 0; pos < Size(); ) {
-    auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + Offset() + pos);
+void ImageHeader::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const {
+  const ImageSection& fields = GetFieldsSection();
+  for (size_t pos = 0; pos < fields.Size(); ) {
+    auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + fields.Offset() + pos);
     for (size_t i = 0; i < array->size(); ++i) {
       visitor->Visit(&array->At(i, sizeof(ArtField)));
     }
@@ -157,18 +158,25 @@
   }
 }
 
-void ImageSection::VisitPackedArtMethods(ArtMethodVisitor* visitor,
-                                         uint8_t* base,
-                                         size_t pointer_size) const {
+void ImageHeader::VisitPackedArtMethods(ArtMethodVisitor* visitor,
+                                        uint8_t* base,
+                                        size_t pointer_size) const {
   const size_t method_alignment = ArtMethod::Alignment(pointer_size);
   const size_t method_size = ArtMethod::Size(pointer_size);
-  for (size_t pos = 0; pos < Size(); ) {
-    auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + Offset() + pos);
+  const ImageSection& methods = GetMethodsSection();
+  for (size_t pos = 0; pos < methods.Size(); ) {
+    auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + methods.Offset() + pos);
     for (size_t i = 0; i < array->size(); ++i) {
       visitor->Visit(&array->At(i, method_size, method_alignment));
     }
     pos += array->ComputeSize(array->size(), method_size, method_alignment);
   }
+  const ImageSection& runtime_methods = GetRuntimeMethodsSection();
+  for (size_t pos = 0; pos < runtime_methods.Size(); ) {
+    auto* method = reinterpret_cast<ArtMethod*>(base + runtime_methods.Offset() + pos);
+    visitor->Visit(method);
+    pos += method_size;
+  }
 }
 
 }  // namespace art
diff --git a/runtime/image.h b/runtime/image.h
index 8e5dbad..2ea9af7 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -64,12 +64,6 @@
     return offset - offset_ < size_;
   }
 
-  // Visit ArtMethods in the section starting at base.
-  void VisitPackedArtMethods(ArtMethodVisitor* visitor, uint8_t* base, size_t pointer_size) const;
-
-  // Visit ArtMethods in the section starting at base.
-  void VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const;
-
  private:
   uint32_t offset_;
   uint32_t size_;
@@ -200,6 +194,8 @@
     kSectionObjects,
     kSectionArtFields,
     kSectionArtMethods,
+    kSectionRuntimeMethods,
+    kSectionIMTConflictTables,
     kSectionDexCacheArrays,
     kSectionInternedStrings,
     kSectionClassTable,
@@ -211,10 +207,19 @@
   void SetImageMethod(ImageMethod index, ArtMethod* method);
 
   const ImageSection& GetImageSection(ImageSections index) const;
+
   const ImageSection& GetMethodsSection() const {
     return GetImageSection(kSectionArtMethods);
   }
 
+  const ImageSection& GetRuntimeMethodsSection() const {
+    return GetImageSection(kSectionRuntimeMethods);
+  }
+
+  const ImageSection& GetFieldsSection() const {
+    return GetImageSection(ImageHeader::kSectionArtFields);
+  }
+
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   mirror::Object* GetImageRoot(ImageRoot image_root) const
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -265,6 +270,19 @@
     return boot_image_size_ != 0u;
   }
 
+  // Visit ArtMethods in the section starting at base. Includes runtime methods.
+  // TODO: Delete base parameter if it is always equal to GetImageBegin.
+  void VisitPackedArtMethods(ArtMethodVisitor* visitor, uint8_t* base, size_t pointer_size) const;
+
+  // Visit ArtMethods in the section starting at base.
+  // TODO: Delete base parameter if it is always equal to GetImageBegin.
+  void VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const;
+
+  template <typename Visitor>
+  void VisitPackedImtConflictTables(const Visitor& visitor,
+                                    uint8_t* base,
+                                    size_t pointer_size) const;
+
  private:
   static const uint8_t kImageMagic[4];
   static const uint8_t kImageVersion[4];
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 97dbe5d..81a396a 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -303,8 +303,13 @@
 
   shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
 
+  // Lock counting is a special version of accessibility checks, and for simplicity and
+  // reduction of template parameters, we gate it behind access-checks mode.
+  ArtMethod* method = shadow_frame.GetMethod();
+  DCHECK(!method->SkipAccessChecks() || !method->MustCountLocks());
+
   bool transaction_active = Runtime::Current()->IsActiveTransaction();
-  if (LIKELY(shadow_frame.GetMethod()->SkipAccessChecks())) {
+  if (LIKELY(method->SkipAccessChecks())) {
     // Enter the "without access check" interpreter.
     if (kInterpreterImplKind == kMterpImplKind) {
       if (transaction_active) {
@@ -487,6 +492,10 @@
   // Are we executing the first shadow frame?
   bool first = true;
   while (shadow_frame != nullptr) {
+    // We do not want to recover lock state for lock counting when deoptimizing. Currently,
+    // the compiler should not have compiled a method that failed structured-locking checks.
+    DCHECK(!shadow_frame->GetMethod()->MustCountLocks());
+
     self->SetTopOfShadowStack(shadow_frame);
     const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem();
     const uint32_t dex_pc = shadow_frame->GetDexPC();
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index e5b89e2..69376fd 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -95,7 +95,9 @@
   StackHandleScope<1> hs(self);
   Handle<Object> h_ref(hs.NewHandle(ref));
   h_ref->MonitorEnter(self);
-  frame->GetLockCountData().AddMonitor<kMonitorCounting>(self, h_ref.Get());
+  if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+    frame->GetLockCountData().AddMonitor(self, h_ref.Get());
+  }
 }
 
 template <bool kMonitorCounting>
@@ -107,7 +109,19 @@
   StackHandleScope<1> hs(self);
   Handle<Object> h_ref(hs.NewHandle(ref));
   h_ref->MonitorExit(self);
-  frame->GetLockCountData().RemoveMonitorOrThrow<kMonitorCounting>(self, h_ref.Get());
+  if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+    frame->GetLockCountData().RemoveMonitorOrThrow(self, h_ref.Get());
+  }
+}
+
+template <bool kMonitorCounting>
+static inline bool DoMonitorCheckOnExit(Thread* self, ShadowFrame* frame)
+    NO_THREAD_SAFETY_ANALYSIS
+    REQUIRES(!Roles::uninterruptible_) {
+  if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+    return frame->GetLockCountData().CheckAllMonitorsReleasedOrThrow(self);
+  }
+  return true;
 }
 
 void AbortTransactionF(Thread* self, const char* fmt, ...)
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 13cfb98..f03036b 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -104,8 +104,7 @@
   } HANDLE_INSTRUCTION_END();
 
 #define HANDLE_MONITOR_CHECKS()                                                                   \
-  if (!shadow_frame.GetLockCountData().                                                           \
-          CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self)) {                        \
+  if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) {                       \
     HANDLE_PENDING_EXCEPTION();                                                                   \
   }
 
@@ -2584,7 +2583,7 @@
                                                                   instrumentation);
     if (found_dex_pc == DexFile::kDexNoIndex) {
       // Structured locking is to be enforced for abnormal termination, too.
-      shadow_frame.GetLockCountData().CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self);
+      DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);
       return JValue(); /* Handled in caller. */
     } else {
       int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 4323d4f..18330ba 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -34,8 +34,7 @@
                                                                   instrumentation);             \
     if (found_dex_pc == DexFile::kDexNoIndex) {                                                 \
       /* Structured locking is to be enforced for abnormal termination, too. */                 \
-      shadow_frame.GetLockCountData().                                                          \
-          CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self);                        \
+      DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);                        \
       if (interpret_one_instruction) {                                                          \
         /* Signal mterp to return to caller */                                                  \
         shadow_frame.SetDexPC(DexFile::kDexNoIndex);                                            \
@@ -57,8 +56,7 @@
   } while (false)
 
 #define HANDLE_MONITOR_CHECKS()                                                                   \
-  if (!shadow_frame.GetLockCountData().                                                           \
-          CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self)) {                        \
+  if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) {                       \
     HANDLE_PENDING_EXCEPTION();                                                                   \
   }
 
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index e46f9cd..f78e1bc 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -12985,6 +12985,7 @@
  * not-taken path.  All Dalvik not-taken conditional branch offsets are 2.
  */
 .L_check_not_taken_osr:
+    EXPORT_PC
     movl    rSELF, %eax
     movl    %eax, OUT_ARG0(%esp)
     leal    OFF_FP_SHADOWFRAME(rFP), %ecx
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index 62dce6e..031cec8 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -11961,6 +11961,7 @@
  * not-taken path.  All Dalvik not-taken conditional branch offsets are 2.
  */
 .L_check_not_taken_osr:
+    EXPORT_PC
     movq    rSELF, OUT_ARG0
     leaq    OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
     movl    $2, OUT_32_ARG2
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index fa03e78..e8c8ca8 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -234,6 +234,7 @@
  * not-taken path.  All Dalvik not-taken conditional branch offsets are 2.
  */
 .L_check_not_taken_osr:
+    EXPORT_PC
     movl    rSELF, %eax
     movl    %eax, OUT_ARG0(%esp)
     leal    OFF_FP_SHADOWFRAME(rFP), %ecx
diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S
index 54d0cb1..f78f163 100644
--- a/runtime/interpreter/mterp/x86_64/footer.S
+++ b/runtime/interpreter/mterp/x86_64/footer.S
@@ -213,6 +213,7 @@
  * not-taken path.  All Dalvik not-taken conditional branch offsets are 2.
  */
 .L_check_not_taken_osr:
+    EXPORT_PC
     movq    rSELF, OUT_ARG0
     leaq    OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
     movl    $$2, OUT_32_ARG2
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index a41fd45..79c3203 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -318,6 +318,7 @@
     }
     JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
     delete raw_vm->GetRuntime();
+    android::ResetNativeLoader();
     return JNI_OK;
   }
 
@@ -950,6 +951,11 @@
     LOG(WARNING) << "CreateJavaVM failed";
     return JNI_ERR;
   }
+
+  // Initialize native loader. This step makes sure we have
+  // everything set up before we start using JNI.
+  android::InitializeNativeLoader();
+
   *p_env = Thread::Current()->GetJniEnv();
   *p_vm = runtime->GetJavaVM();
   return JNI_OK;
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index c36543f..e9317a5 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -51,7 +51,7 @@
 
 JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) {
   auto* jit_options = new JitOptions;
-  jit_options->use_jit_ = options.GetOrDefault(RuntimeArgumentMap::UseJIT);
+  jit_options->use_jit_compilation_ = options.GetOrDefault(RuntimeArgumentMap::UseJitCompilation);
 
   jit_options->code_cache_initial_capacity_ =
       options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheInitialCapacity);
@@ -102,14 +102,26 @@
         static_cast<size_t>(1));
   }
 
+  if (options.Exists(RuntimeArgumentMap::JITInvokeTransitionWeight)) {
+    if (jit_options->invoke_transition_weight_ > jit_options->warmup_threshold_) {
+      LOG(FATAL) << "Invoke transition weight is above the warmup threshold.";
+    } else if (jit_options->invoke_transition_weight_  == 0) {
+      LOG(FATAL) << "Invoke transition ratio cannot be 0.";
+    }
+    jit_options->invoke_transition_weight_ =
+        *options.Get(RuntimeArgumentMap::JITInvokeTransitionWeight);
+  } else {
+    jit_options->invoke_transition_weight_ = std::max(
+        jit_options->warmup_threshold_ / Jit::kDefaultInvokeTransitionWeightRatio,
+        static_cast<size_t>(1));;
+  }
+
   return jit_options;
 }
 
 bool Jit::ShouldUsePriorityThreadWeight() {
-  // TODO(calin): verify that IsSensitiveThread covers only the cases we are interested on.
-  // In particular if apps can set StrictMode policies for any of their threads, case in which
-  // we need to find another way to track sensitive threads.
-  return Runtime::Current()->InJankPerceptibleProcessState() && Thread::IsSensitiveThread();
+  return Runtime::Current()->InJankPerceptibleProcessState()
+      && Thread::Current()->IsJitSensitiveThread();
 }
 
 void Jit::DumpInfo(std::ostream& os) {
@@ -132,9 +144,11 @@
              cumulative_timings_("JIT timings"),
              memory_use_("Memory used for compilation", 16),
              lock_("JIT memory use lock"),
+             use_jit_compilation_(true),
              save_profiling_info_(false) {}
 
 Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
+  DCHECK(options->UseJitCompilation() || options->GetSaveProfilingInfo());
   std::unique_ptr<Jit> jit(new Jit);
   jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
   if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
@@ -148,6 +162,7 @@
   if (jit->GetCodeCache() == nullptr) {
     return nullptr;
   }
+  jit->use_jit_compilation_ = options->UseJitCompilation();
   jit->save_profiling_info_ = options->GetSaveProfilingInfo();
   VLOG(jit) << "JIT created with initial_capacity="
       << PrettySize(options->GetCodeCacheInitialCapacity())
@@ -160,8 +175,7 @@
   jit->warm_method_threshold_ = options->GetWarmupThreshold();
   jit->osr_method_threshold_ = options->GetOsrThreshold();
   jit->priority_thread_weight_ = options->GetPriorityThreadWeight();
-  jit->transition_weight_ = std::max(
-      jit->warm_method_threshold_ / kDefaultTransitionRatio, static_cast<size_t>(1));
+  jit->invoke_transition_weight_ = options->GetInvokeTransitionWeight();
 
   jit->CreateThreadPool();
 
@@ -227,6 +241,7 @@
 }
 
 bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) {
+  DCHECK(Runtime::Current()->UseJitCompilation());
   DCHECK(!method->IsRuntimeMethod());
 
   // Don't compile the method if it has breakpoints.
@@ -331,8 +346,12 @@
 }
 
 void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) {
+  if (!Runtime::Current()->UseJitCompilation()) {
+    // No need to notify if we only use the JIT to save profiles.
+    return;
+  }
   jit::Jit* jit = Runtime::Current()->GetJit();
-  if (jit != nullptr && jit->generate_debug_info_) {
+  if (jit->generate_debug_info_) {
     DCHECK(jit->jit_types_loaded_ != nullptr);
     jit->jit_types_loaded_(jit->jit_compiler_handle_, &type, 1);
   }
@@ -606,22 +625,24 @@
     }
     // Avoid jumping more than one state at a time.
     new_count = std::min(new_count, hot_method_threshold_ - 1);
-  } else if (starting_count < hot_method_threshold_) {
-    if ((new_count >= hot_method_threshold_) &&
-        !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
-      DCHECK(thread_pool_ != nullptr);
-      thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
-    }
-    // Avoid jumping more than one state at a time.
-    new_count = std::min(new_count, osr_method_threshold_ - 1);
-  } else if (starting_count < osr_method_threshold_) {
-    if (!with_backedges) {
-      // If the samples don't contain any back edge, we don't increment the hotness.
-      return;
-    }
-    if ((new_count >= osr_method_threshold_) &&  !code_cache_->IsOsrCompiled(method)) {
-      DCHECK(thread_pool_ != nullptr);
-      thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+  } else if (use_jit_compilation_) {
+    if (starting_count < hot_method_threshold_) {
+      if ((new_count >= hot_method_threshold_) &&
+          !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
+        DCHECK(thread_pool_ != nullptr);
+        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+      }
+      // Avoid jumping more than one state at a time.
+      new_count = std::min(new_count, osr_method_threshold_ - 1);
+    } else if (starting_count < osr_method_threshold_) {
+      if (!with_backedges) {
+        // If the samples don't contain any back edge, we don't increment the hotness.
+        return;
+      }
+      if ((new_count >= osr_method_threshold_) &&  !code_cache_->IsOsrCompiled(method)) {
+        DCHECK(thread_pool_ != nullptr);
+        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+      }
     }
   }
   // Update hotness counter
@@ -629,7 +650,8 @@
 }
 
 void Jit::MethodEntered(Thread* thread, ArtMethod* method) {
-  if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) {
+  Runtime* runtime = Runtime::Current();
+  if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) {
     // The compiler requires a ProfilingInfo object.
     ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
     JitCompileTask compile_task(method, JitCompileTask::kCompile);
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 8198c18..f3a6240 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -44,7 +44,7 @@
   static constexpr bool kStressMode = kIsDebugBuild;
   static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 10000;
   static constexpr size_t kDefaultPriorityThreadWeightRatio = 1000;
-  static constexpr size_t kDefaultTransitionRatio = 100;
+  static constexpr size_t kDefaultInvokeTransitionWeightRatio = 500;
 
   virtual ~Jit();
   static Jit* Create(JitOptions* options, std::string* error_msg);
@@ -87,6 +87,15 @@
     return priority_thread_weight_;
   }
 
+  // Returns false if we only need to save profile information and not compile methods.
+  bool UseJitCompilation() const {
+    return use_jit_compilation_;
+  }
+
+  bool SaveProfilingInfo() const {
+    return save_profiling_info_;
+  }
+
   // Wait until there is no more pending compilation tasks.
   void WaitForCompilationToFinish(Thread* self);
 
@@ -106,12 +115,12 @@
 
   void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    AddSamples(self, caller, transition_weight_, false);
+    AddSamples(self, caller, invoke_transition_weight_, false);
   }
 
   void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    AddSamples(self, callee, transition_weight_, false);
+    AddSamples(self, callee, invoke_transition_weight_, false);
   }
 
   // Starts the profile saver if the config options allow profile recording.
@@ -179,13 +188,14 @@
 
   std::unique_ptr<jit::JitCodeCache> code_cache_;
 
+  bool use_jit_compilation_;
   bool save_profiling_info_;
   static bool generate_debug_info_;
   uint16_t hot_method_threshold_;
   uint16_t warm_method_threshold_;
   uint16_t osr_method_threshold_;
   uint16_t priority_thread_weight_;
-  uint16_t transition_weight_;
+  uint16_t invoke_transition_weight_;
   std::unique_ptr<ThreadPool> thread_pool_;
 
   DISALLOW_COPY_AND_ASSIGN(Jit);
@@ -206,6 +216,9 @@
   uint16_t GetPriorityThreadWeight() const {
     return priority_thread_weight_;
   }
+  size_t GetInvokeTransitionWeight() const {
+    return invoke_transition_weight_;
+  }
   size_t GetCodeCacheInitialCapacity() const {
     return code_cache_initial_capacity_;
   }
@@ -218,33 +231,34 @@
   bool GetSaveProfilingInfo() const {
     return save_profiling_info_;
   }
-  bool UseJIT() const {
-    return use_jit_;
+  bool UseJitCompilation() const {
+    return use_jit_compilation_;
   }
-  void SetUseJIT(bool b) {
-    use_jit_ = b;
+  void SetUseJitCompilation(bool b) {
+    use_jit_compilation_ = b;
   }
   void SetSaveProfilingInfo(bool b) {
     save_profiling_info_ = b;
   }
   void SetJitAtFirstUse() {
-    use_jit_ = true;
+    use_jit_compilation_ = true;
     compile_threshold_ = 0;
   }
 
  private:
-  bool use_jit_;
+  bool use_jit_compilation_;
   size_t code_cache_initial_capacity_;
   size_t code_cache_max_capacity_;
   size_t compile_threshold_;
   size_t warmup_threshold_;
   size_t osr_threshold_;
   uint16_t priority_thread_weight_;
+  size_t invoke_transition_weight_;
   bool dump_info_on_shutdown_;
   bool save_profiling_info_;
 
   JitOptions()
-      : use_jit_(false),
+      : use_jit_compilation_(false),
         code_cache_initial_capacity_(0),
         code_cache_max_capacity_(0),
         compile_threshold_(0),
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index a79bcf0..c99d363 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -406,7 +406,8 @@
   line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>();
 
   if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
-    *error = "DexFileKey has an invalid size: " + std::to_string(dex_location_size);
+    *error = "DexFileKey has an invalid size: " +
+        std::to_string(static_cast<uint32_t>(dex_location_size));
     return kProfileLoadBadData;
   }
 
@@ -644,7 +645,8 @@
   for (auto&& pair : info_) {
     const std::string& profile_key = pair.first;
     const DexFileData& data = pair.second;
-    DexCacheResolvedClasses classes(profile_key, data.checksum);
+    // TODO: Is it OK to use the same location for both base and dex location here?
+    DexCacheResolvedClasses classes(profile_key, profile_key, data.checksum);
     classes.AddClasses(data.class_set.begin(), data.class_set.end());
     ret.insert(classes);
   }
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 7a9d250..e8462a1 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -145,20 +145,25 @@
 
 void ProfileSaver::FetchAndCacheResolvedClasses() {
   ScopedTrace trace(__PRETTY_FUNCTION__);
-
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   std::set<DexCacheResolvedClasses> resolved_classes =
       class_linker->GetResolvedClasses(/*ignore boot classes*/ true);
   MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
   uint64_t total_number_of_profile_entries_cached = 0;
+
   for (const auto& it : tracked_dex_base_locations_) {
-      std::set<DexCacheResolvedClasses> resolved_classes_for_location;
+    std::set<DexCacheResolvedClasses> resolved_classes_for_location;
     const std::string& filename = it.first;
     const std::set<std::string>& locations = it.second;
 
     for (const DexCacheResolvedClasses& classes : resolved_classes) {
-      if (locations.find(classes.GetDexLocation()) != locations.end()) {
+      if (locations.find(classes.GetBaseLocation()) != locations.end()) {
+        VLOG(profiler) << "Added classes for location " << classes.GetBaseLocation()
+                       << " (" << classes.GetDexLocation() << ")";
         resolved_classes_for_location.insert(classes);
+      } else {
+        VLOG(profiler) << "Location not found " << classes.GetBaseLocation()
+                       << " (" << classes.GetDexLocation() << ")";
       }
     }
     ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
@@ -247,12 +252,17 @@
 
 void* ProfileSaver::RunProfileSaverThread(void* arg) {
   Runtime* runtime = Runtime::Current();
-  ProfileSaver* profile_saver = reinterpret_cast<ProfileSaver*>(arg);
 
-  CHECK(runtime->AttachCurrentThread("Profile Saver",
-                                     /*as_daemon*/true,
-                                     runtime->GetSystemThreadGroup(),
-                                     /*create_peer*/true));
+  bool attached = runtime->AttachCurrentThread("Profile Saver",
+                                               /*as_daemon*/true,
+                                               runtime->GetSystemThreadGroup(),
+                                               /*create_peer*/true);
+  if (!attached) {
+    CHECK(runtime->IsShuttingDown(Thread::Current()));
+    return nullptr;
+  }
+
+  ProfileSaver* profile_saver = reinterpret_cast<ProfileSaver*>(arg);
   profile_saver->Run();
 
   runtime->DetachCurrentThread();
@@ -285,7 +295,7 @@
                          const std::vector<std::string>& code_paths,
                          const std::string& foreign_dex_profile_path,
                          const std::string& app_data_dir) {
-  DCHECK(Runtime::Current()->UseJit());
+  DCHECK(Runtime::Current()->SaveProfileInfo());
   DCHECK(!output_filename.empty());
   DCHECK(jit_code_cache != nullptr);
 
@@ -520,4 +530,32 @@
     << max_number_of_profile_entries_cached_ << '\n';
 }
 
+
+void ProfileSaver::ForceProcessProfiles() {
+  ProfileSaver* saver = nullptr;
+  {
+    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+    saver = instance_;
+  }
+  // TODO(calin): this is not actually thread safe as the instance_ may have been deleted,
+  // but we only use this in testing when we now this won't happen.
+  // Refactor the way we handle the instance so that we don't end up in this situation.
+  if (saver != nullptr) {
+    saver->ProcessProfilingInfo();
+  }
+}
+
+bool ProfileSaver::HasSeenMethod(const std::string& profile,
+                                 const DexFile* dex_file,
+                                 uint16_t method_idx) {
+  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+  if (instance_ != nullptr) {
+    ProfileCompilationInfo* info = instance_->GetCachedProfiledInfo(profile);
+    if (info != nullptr) {
+      return info->ContainsMethod(MethodReference(dex_file, method_idx));
+    }
+  }
+  return false;
+}
+
 }   // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 0a222bf..4f3cdc2 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -49,6 +49,12 @@
   // If the profile saver is running, dumps statistics to the `os`. Otherwise it does nothing.
   static void DumpInstanceInfo(std::ostream& os);
 
+  // Just for testing purpose.
+  static void ForceProcessProfiles();
+  static bool HasSeenMethod(const std::string& profile,
+                            const DexFile* dex_file,
+                            uint16_t method_idx);
+
  private:
   ProfileSaver(const std::string& output_filename,
                jit::JitCodeCache* jit_code_cache,
@@ -65,7 +71,10 @@
   void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_);
   // Processes the existing profiling info from the jit code cache and returns
   // true if it needed to be saved to disk.
-  bool ProcessProfilingInfo();
+  bool ProcessProfilingInfo()
+    REQUIRES(!Locks::profiler_lock_)
+    REQUIRES(!Locks::mutator_lock_);
+
   // Returns true if the saver is shutting down (ProfileSaver::Stop() has been called).
   bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
 
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 6dd182a..fd7a125 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -60,10 +60,13 @@
 // This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
 // if any particular method needs to be a default conflict. Used to figure out at runtime if
 // invoking this method will throw an exception.
-static constexpr uint32_t kAccDefaultConflict =      0x00800000;  // method (runtime)
+static constexpr uint32_t kAccDefaultConflict =       0x00800000;  // method (runtime)
 
 // Set by the verifier for a method we do not want the compiler to compile.
-static constexpr uint32_t kAccCompileDontBother =    0x01000000;  // method (runtime)
+static constexpr uint32_t kAccCompileDontBother =     0x01000000;  // method (runtime)
+
+// Set by the verifier for a method that could not be verified to follow structured locking.
+static constexpr uint32_t kAccMustCountLocks =        0x02000000;  // method (runtime)
 
 // Special runtime-only flags.
 // Interface and all its super-interfaces with default methods have been recursively initialized.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 3680c78..f4bc222 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -215,6 +215,85 @@
   obj_ = GcRoot<mirror::Object>(object);
 }
 
+// Note: Adapted from CurrentMethodVisitor in thread.cc. We must not resolve here.
+
+struct NthCallerWithDexPcVisitor FINAL : public StackVisitor {
+  explicit NthCallerWithDexPcVisitor(Thread* thread, size_t frame)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFramesNoResolve),
+        method_(nullptr),
+        dex_pc_(0),
+        current_frame_number_(0),
+        wanted_frame_number_(frame) {}
+  bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    ArtMethod* m = GetMethod();
+    if (m == nullptr || m->IsRuntimeMethod()) {
+      // Runtime method, upcall, or resolution issue. Skip.
+      return true;
+    }
+
+    // Is this the requested frame?
+    if (current_frame_number_ == wanted_frame_number_) {
+      method_ = m;
+      dex_pc_ = GetDexPc(false /* abort_on_error*/);
+      return false;
+    }
+
+    // Look for more.
+    current_frame_number_++;
+    return true;
+  }
+
+  ArtMethod* method_;
+  uint32_t dex_pc_;
+
+ private:
+  size_t current_frame_number_;
+  const size_t wanted_frame_number_;
+};
+
+// This function is inlined and just helps to not have the VLOG and ATRACE check at all the
+// potential tracing points.
+void Monitor::AtraceMonitorLock(Thread* self, mirror::Object* obj, bool is_wait) {
+  if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging) && ATRACE_ENABLED())) {
+    AtraceMonitorLockImpl(self, obj, is_wait);
+  }
+}
+
+void Monitor::AtraceMonitorLockImpl(Thread* self, mirror::Object* obj, bool is_wait) {
+  // Wait() requires a deeper call stack to be useful. Otherwise you'll see "Waiting at
+  // Object.java". Assume that we'll wait a nontrivial amount, so it's OK to do a longer
+  // stack walk than if !is_wait.
+  NthCallerWithDexPcVisitor visitor(self, is_wait ? 1U : 0U);
+  visitor.WalkStack(false);
+  const char* prefix = is_wait ? "Waiting on " : "Locking ";
+
+  const char* filename;
+  int32_t line_number;
+  TranslateLocation(visitor.method_, visitor.dex_pc_, &filename, &line_number);
+
+  // It would be nice to have a stable "ID" for the object here. However, the only stable thing
+  // would be the identity hashcode. But we cannot use IdentityHashcode here: For one, there are
+  // times when it is unsafe to make that call (see stack dumping for an explanation). More
+  // importantly, we would have to give up on thin-locking when adding systrace locks, as the
+  // identity hashcode is stored in the lockword normally (so can't be used with thin-locks).
+  //
+  // Because of thin-locks we also cannot use the monitor id (as there is no monitor). Monitor ids
+  // also do not have to be stable, as the monitor may be deflated.
+  std::string tmp = StringPrintf("%s %d at %s:%d",
+      prefix,
+      (obj == nullptr ? -1 : static_cast<int32_t>(reinterpret_cast<uintptr_t>(obj))),
+      (filename != nullptr ? filename : "null"),
+      line_number);
+  ATRACE_BEGIN(tmp.c_str());
+}
+
+void Monitor::AtraceMonitorUnlock() {
+  if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
+    ATRACE_END();
+  }
+}
+
 std::string Monitor::PrettyContentionInfo(const std::string& owner_name,
                                           pid_t owner_tid,
                                           ArtMethod* owners_method,
@@ -228,8 +307,8 @@
   std::ostringstream oss;
   oss << "monitor contention with owner " << owner_name << " (" << owner_tid << ")";
   if (owners_method != nullptr) {
-    oss << " owner method=" << PrettyMethod(owners_method);
-    oss << " from " << owners_filename << ":" << owners_line_number;
+    oss << " at " << PrettyMethod(owners_method);
+    oss << "(" << owners_filename << ":" << owners_line_number << ")";
   }
   oss << " waiters=" << num_waiters;
   return oss.str();
@@ -246,10 +325,10 @@
       if (lock_profiling_threshold_ != 0) {
         locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
       }
-      return;
+      break;
     } else if (owner_ == self) {  // Recursive.
       lock_count_++;
-      return;
+      break;
     }
     // Contended.
     const bool log_contention = (lock_profiling_threshold_ != 0);
@@ -284,8 +363,9 @@
             const char* filename;
             int32_t line_number;
             TranslateLocation(m, pc, &filename, &line_number);
-            oss << " blocking from " << (filename != nullptr ? filename : "null")
-                << ":" << line_number;
+            oss << " blocking from "
+                << PrettyMethod(m) << "(" << (filename != nullptr ? filename : "null") << ":"
+                << line_number << ")";
             ATRACE_BEGIN(oss.str().c_str());
           }
           monitor_contenders_.Wait(self);  // Still contended so wait.
@@ -318,6 +398,8 @@
             }
             if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
               if (wait_ms > kLongWaitMs && owners_method != nullptr) {
+                uint32_t pc;
+                ArtMethod* m = self->GetCurrentMethod(&pc);
                 // TODO: We should maybe check that original_owner is still a live thread.
                 LOG(WARNING) << "Long "
                     << PrettyContentionInfo(original_owner_name,
@@ -325,7 +407,7 @@
                                             owners_method,
                                             owners_dex_pc,
                                             num_waiters)
-                    << " for " << PrettyDuration(MsToNs(wait_ms));
+                    << " in " << PrettyMethod(m) << " for " << PrettyDuration(MsToNs(wait_ms));
               }
               const char* owners_filename;
               int32_t owners_line_number;
@@ -348,6 +430,8 @@
     monitor_lock_.Lock(self);  // Reacquire locks in order.
     --num_waiters_;
   }
+
+  AtraceMonitorLock(self, GetObject(), false /* is_wait */);
 }
 
 static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...)
@@ -457,6 +541,7 @@
     }
     if (owner == self) {
       // We own the monitor, so nobody else can be in here.
+      AtraceMonitorUnlock();
       if (lock_count_ == 0) {
         owner_ = nullptr;
         locking_method_ = nullptr;
@@ -523,6 +608,11 @@
   uintptr_t saved_dex_pc = locking_dex_pc_;
   locking_dex_pc_ = 0;
 
+  AtraceMonitorUnlock();  // For the implict Unlock() just above. This will only end the deepest
+                          // nesting, but that is enough for the visualization, and corresponds to
+                          // the single Lock() we do afterwards.
+  AtraceMonitorLock(self, GetObject(), true /* is_wait */);
+
   bool was_interrupted = false;
   {
     // Update thread state. If the GC wakes up, it'll ignore us, knowing
@@ -586,6 +676,8 @@
     self->ThrowNewException("Ljava/lang/InterruptedException;", nullptr);
   }
 
+  AtraceMonitorUnlock();  // End Wait().
+
   // Re-acquire the monitor and lock.
   Lock(self);
   monitor_lock_.Lock(self);
@@ -775,6 +867,7 @@
       case LockWord::kUnlocked: {
         LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0, lock_word.ReadBarrierState()));
         if (h_obj->CasLockWordWeakSequentiallyConsistent(lock_word, thin_locked)) {
+          AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */);
           // CasLockWord enforces more than the acquire ordering we need here.
           return h_obj.Get();  // Success!
         }
@@ -790,10 +883,12 @@
                                                           lock_word.ReadBarrierState()));
             if (!kUseReadBarrier) {
               h_obj->SetLockWord(thin_locked, true);
+              AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */);
               return h_obj.Get();  // Success!
             } else {
               // Use CAS to preserve the read barrier state.
               if (h_obj->CasLockWordWeakSequentiallyConsistent(lock_word, thin_locked)) {
+                AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */);
                 return h_obj.Get();  // Success!
               }
             }
@@ -830,7 +925,7 @@
         continue;  // Start from the beginning.
       default: {
         LOG(FATAL) << "Invalid monitor state " << lock_word.GetState();
-        return h_obj.Get();
+        UNREACHABLE();
       }
     }
   }
@@ -869,11 +964,17 @@
           if (!kUseReadBarrier) {
             DCHECK_EQ(new_lw.ReadBarrierState(), 0U);
             h_obj->SetLockWord(new_lw, true);
+            if (ATRACE_ENABLED()) {
+              ATRACE_END();
+            }
             // Success!
             return true;
           } else {
             // Use CAS to preserve the read barrier state.
             if (h_obj->CasLockWordWeakSequentiallyConsistent(lock_word, new_lw)) {
+              if (ATRACE_ENABLED()) {
+                ATRACE_END();
+              }
               // Success!
               return true;
             }
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 8c7496b..7b4b8f9 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -250,6 +250,17 @@
 
   uint32_t GetOwnerThreadId() REQUIRES(!monitor_lock_);
 
+  // Support for systrace output of monitor operations.
+  ALWAYS_INLINE static void AtraceMonitorLock(Thread* self,
+                                              mirror::Object* obj,
+                                              bool is_wait)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  static void AtraceMonitorLockImpl(Thread* self,
+                                    mirror::Object* obj,
+                                    bool is_wait)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  ALWAYS_INLINE static void AtraceMonitorUnlock();
+
   static uint32_t lock_profiling_threshold_;
 
   Mutex monitor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc
index ce38e4f..a47a4b2 100644
--- a/runtime/monitor_pool.cc
+++ b/runtime/monitor_pool.cc
@@ -28,7 +28,11 @@
 }  // namespace mirror
 
 MonitorPool::MonitorPool()
-    : num_chunks_(0), capacity_(0), first_free_(nullptr) {
+    : current_chunk_list_index_(0), num_chunks_(0), current_chunk_list_capacity_(0),
+    first_free_(nullptr) {
+  for (size_t i = 0; i < kMaxChunkLists; ++i) {
+    monitor_chunks_[i] = nullptr;  // Not absolutely required, but ...
+  }
   AllocateChunk();  // Get our first chunk.
 }
 
@@ -37,24 +41,19 @@
 void MonitorPool::AllocateChunk() {
   DCHECK(first_free_ == nullptr);
 
-  // Do we need to resize?
-  if (num_chunks_ == capacity_) {
-    if (capacity_ == 0U) {
-      // Initialization.
-      capacity_ = kInitialChunkStorage;
-      uintptr_t* new_backing = new uintptr_t[capacity_]();
-      DCHECK(monitor_chunks_.LoadRelaxed() == nullptr);
-      monitor_chunks_.StoreRelaxed(new_backing);
-    } else {
-      size_t new_capacity = 2 * capacity_;
-      uintptr_t* new_backing = new uintptr_t[new_capacity]();
-      uintptr_t* old_backing = monitor_chunks_.LoadRelaxed();
-      memcpy(new_backing, old_backing, sizeof(uintptr_t) * capacity_);
-      monitor_chunks_.StoreRelaxed(new_backing);
-      capacity_ = new_capacity;
-      old_chunk_arrays_.push_back(std::unique_ptr<uintptr_t[]>(old_backing));
-      VLOG(monitor) << "Resizing to capacity " << capacity_;
-    }
+  // Do we need to allocate another chunk list?
+  if (num_chunks_ == current_chunk_list_capacity_) {
+    if (current_chunk_list_capacity_ != 0U) {
+      ++current_chunk_list_index_;
+      CHECK_LT(current_chunk_list_index_, kMaxChunkLists) << "Out of space for inflated monitors";
+      VLOG(monitor) << "Expanding to capacity "
+          << 2 * ChunkListCapacity(current_chunk_list_index_) - kInitialChunkStorage;
+    }  // else we're initializing
+    current_chunk_list_capacity_ = ChunkListCapacity(current_chunk_list_index_);
+    uintptr_t* new_list = new uintptr_t[current_chunk_list_capacity_]();
+    DCHECK(monitor_chunks_[current_chunk_list_index_] == nullptr);
+    monitor_chunks_[current_chunk_list_index_] = new_list;
+    num_chunks_ = 0;
   }
 
   // Allocate the chunk.
@@ -65,7 +64,7 @@
   CHECK_EQ(0U, reinterpret_cast<uintptr_t>(chunk) % kMonitorAlignment);
 
   // Add the chunk.
-  *(monitor_chunks_.LoadRelaxed() + num_chunks_) = reinterpret_cast<uintptr_t>(chunk);
+  monitor_chunks_[current_chunk_list_index_][num_chunks_] = reinterpret_cast<uintptr_t>(chunk);
   num_chunks_++;
 
   // Set up the free list
@@ -73,8 +72,8 @@
                                              (kChunkCapacity - 1) * kAlignedMonitorSize);
   last->next_free_ = nullptr;
   // Eagerly compute id.
-  last->monitor_id_ = OffsetToMonitorId((num_chunks_ - 1) * kChunkSize +
-                                        (kChunkCapacity - 1) * kAlignedMonitorSize);
+  last->monitor_id_ = OffsetToMonitorId(current_chunk_list_index_* (kMaxListSize * kChunkSize)
+      + (num_chunks_ - 1) * kChunkSize + (kChunkCapacity - 1) * kAlignedMonitorSize);
   for (size_t i = 0; i < kChunkCapacity - 1; ++i) {
     Monitor* before = reinterpret_cast<Monitor*>(reinterpret_cast<uintptr_t>(last) -
                                                  kAlignedMonitorSize);
@@ -91,21 +90,19 @@
 
 void MonitorPool::FreeInternal() {
   // This is on shutdown with NO_THREAD_SAFETY_ANALYSIS, can't/don't need to lock.
-  uintptr_t* backing = monitor_chunks_.LoadRelaxed();
-  DCHECK(backing != nullptr);
-  DCHECK_GT(capacity_, 0U);
-  DCHECK_GT(num_chunks_, 0U);
-
-  for (size_t i = 0; i < capacity_; ++i) {
-    if (i < num_chunks_) {
-      DCHECK_NE(backing[i], 0U);
-      allocator_.deallocate(reinterpret_cast<uint8_t*>(backing[i]), kChunkSize);
-    } else {
-      DCHECK_EQ(backing[i], 0U);
+  DCHECK_NE(current_chunk_list_capacity_, 0UL);
+  for (size_t i = 0; i <= current_chunk_list_index_; ++i) {
+    DCHECK_NE(monitor_chunks_[i], static_cast<uintptr_t*>(nullptr));
+    for (size_t j = 0; j < ChunkListCapacity(i); ++j) {
+      if (i < current_chunk_list_index_ || j < num_chunks_) {
+        DCHECK_NE(monitor_chunks_[i][j], 0U);
+        allocator_.deallocate(reinterpret_cast<uint8_t*>(monitor_chunks_[i][j]), kChunkSize);
+      } else {
+        DCHECK_EQ(monitor_chunks_[i][j], 0U);
+      }
     }
+    delete[] monitor_chunks_[i];
   }
-
-  delete[] backing;
 }
 
 Monitor* MonitorPool::CreateMonitorInPool(Thread* self, Thread* owner, mirror::Object* obj,
diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h
index 875b3fe..99810e0 100644
--- a/runtime/monitor_pool.h
+++ b/runtime/monitor_pool.h
@@ -128,12 +128,17 @@
   void ReleaseMonitorToPool(Thread* self, Monitor* monitor);
   void ReleaseMonitorsToPool(Thread* self, MonitorList::Monitors* monitors);
 
-  // Note: This is safe as we do not ever move chunks.
+  // Note: This is safe as we do not ever move chunks.  All needed entries in the monitor_chunks_
+  // data structure are read-only once we get here.  Updates happen-before this call because
+  // the lock word was stored with release semantics and we read it with acquire semantics to
+  // retrieve the id.
   Monitor* LookupMonitor(MonitorId mon_id) {
     size_t offset = MonitorIdToOffset(mon_id);
     size_t index = offset / kChunkSize;
+    size_t top_index = index / kMaxListSize;
+    size_t list_index = index % kMaxListSize;
     size_t offset_in_chunk = offset % kChunkSize;
-    uintptr_t base = *(monitor_chunks_.LoadRelaxed()+index);
+    uintptr_t base = monitor_chunks_[top_index][list_index];
     return reinterpret_cast<Monitor*>(base + offset_in_chunk);
   }
 
@@ -142,28 +147,37 @@
     return base_addr <= mon_ptr && (mon_ptr - base_addr < kChunkSize);
   }
 
-  // Note: This is safe as we do not ever move chunks.
   MonitorId ComputeMonitorIdInPool(Monitor* mon, Thread* self) {
     MutexLock mu(self, *Locks::allocated_monitor_ids_lock_);
-    for (size_t index = 0; index < num_chunks_; ++index) {
-      uintptr_t chunk_addr = *(monitor_chunks_.LoadRelaxed() + index);
-      if (IsInChunk(chunk_addr, mon)) {
-        return OffsetToMonitorId(
-            reinterpret_cast<uintptr_t>(mon) - chunk_addr + index * kChunkSize);
+    for (size_t i = 0; i <= current_chunk_list_index_; ++i) {
+      for (size_t j = 0; j < ChunkListCapacity(i); ++j) {
+        if (j >= num_chunks_ && i == current_chunk_list_index_) {
+          break;
+        }
+        uintptr_t chunk_addr = monitor_chunks_[i][j];
+        if (IsInChunk(chunk_addr, mon)) {
+          return OffsetToMonitorId(
+              reinterpret_cast<uintptr_t>(mon) - chunk_addr
+              + i * (kMaxListSize * kChunkSize) + j * kChunkSize);
+        }
       }
     }
     LOG(FATAL) << "Did not find chunk that contains monitor.";
     return 0;
   }
 
-  static size_t MonitorIdToOffset(MonitorId id) {
+  static constexpr size_t MonitorIdToOffset(MonitorId id) {
     return id << 3;
   }
 
-  static MonitorId OffsetToMonitorId(size_t offset) {
+  static constexpr MonitorId OffsetToMonitorId(size_t offset) {
     return static_cast<MonitorId>(offset >> 3);
   }
 
+  static constexpr size_t ChunkListCapacity(size_t index) {
+    return kInitialChunkStorage << index;
+  }
+
   // TODO: There are assumptions in the code that monitor addresses are 8B aligned (>>3).
   static constexpr size_t kMonitorAlignment = 8;
   // Size of a monitor, rounded up to a multiple of alignment.
@@ -174,20 +188,47 @@
   // Chunk size that is referenced in the id. We can collapse this to the actually used storage
   // in a chunk, i.e., kChunkCapacity * kAlignedMonitorSize, but this will mean proper divisions.
   static constexpr size_t kChunkSize = kPageSize;
-  // The number of initial chunks storable in monitor_chunks_. The number is large enough to make
-  // resizing unlikely, but small enough to not waste too much memory.
-  static constexpr size_t kInitialChunkStorage = 8U;
+  static_assert(IsPowerOfTwo(kChunkSize), "kChunkSize must be power of 2");
+  // The number of chunks of storage that can be referenced by the initial chunk list.
+  // The total number of usable monitor chunks is typically 255 times this number, so it
+  // should be large enough that we don't run out. We run out of address bits if it's > 512.
+  // Currently we set it a bit smaller, to save half a page per process.  We make it tiny in
+  // debug builds to catch growth errors. The only value we really expect to tune.
+  static constexpr size_t kInitialChunkStorage = kIsDebugBuild ? 1U : 256U;
+  static_assert(IsPowerOfTwo(kInitialChunkStorage), "kInitialChunkStorage must be power of 2");
+  // The number of lists, each containing pointers to storage chunks.
+  static constexpr size_t kMaxChunkLists = 8;  //  Dictated by 3 bit index. Don't increase above 8.
+  static_assert(IsPowerOfTwo(kMaxChunkLists), "kMaxChunkLists must be power of 2");
+  static constexpr size_t kMaxListSize = kInitialChunkStorage << (kMaxChunkLists - 1);
+  // We lose 3 bits in monitor id due to 3 bit monitor_chunks_ index, and gain it back from
+  // the 3 bit alignment constraint on monitors:
+  static_assert(kMaxListSize * kChunkSize < (1 << LockWord::kMonitorIdSize),
+      "Monitor id bits don't fit");
+  static_assert(IsPowerOfTwo(kMaxListSize), "kMaxListSize must be power of 2");
 
-  // List of memory chunks. Each chunk is kChunkSize.
-  Atomic<uintptr_t*> monitor_chunks_;
-  // Number of chunks stored.
+  // Array of pointers to lists (again arrays) of pointers to chunks containing monitors.
+  // Zeroth entry points to a list (array) of kInitialChunkStorage pointers to chunks.
+  // Each subsequent list as twice as large as the preceding one.
+  // Monitor Ids are interpreted as follows:
+  //     Top 3 bits (of 28): index into monitor_chunks_.
+  //     Next 16 bits: index into the chunk list, i.e. monitor_chunks_[i].
+  //     Last 9 bits: offset within chunk, expressed as multiple of kMonitorAlignment.
+  // If we set kInitialChunkStorage to 512, this would allow us to use roughly 128K chunks of
+  // monitors, which is 0.5GB of monitors.  With this maximum setting, the largest chunk list
+  // contains 64K entries, and we make full use of the available index space. With a
+  // kInitialChunkStorage value of 256, this is proportionately reduced to 0.25GB of monitors.
+  // Updates to monitor_chunks_ are guarded by allocated_monitor_ids_lock_ .
+  // No field in this entire data structure is ever updated once a monitor id whose lookup
+  // requires it has been made visible to another thread.  Thus readers never race with
+  // updates, in spite of the fact that they acquire no locks.
+  uintptr_t* monitor_chunks_[kMaxChunkLists];  //  uintptr_t is really a Monitor* .
+  // Highest currently used index in monitor_chunks_ . Used for newly allocated chunks.
+  size_t current_chunk_list_index_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
+  // Number of chunk pointers stored in monitor_chunks_[current_chunk_list_index_] so far.
   size_t num_chunks_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
-  // Number of chunks storable.
-  size_t capacity_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
-
-  // To avoid race issues when resizing, we keep all the previous arrays.
-  std::vector<std::unique_ptr<uintptr_t[]>> old_chunk_arrays_
-      GUARDED_BY(Locks::allocated_monitor_ids_lock_);
+  // After the initial allocation, this is always equal to
+  // ChunkListCapacity(current_chunk_list_index_).
+  size_t current_chunk_list_capacity_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
 
   typedef TrackingAllocator<uint8_t, kAllocatorTagMonitorPool> Allocator;
   Allocator allocator_;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index f355c2a..5ba8df7 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -212,6 +212,10 @@
   Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, static_cast<size_t>(bytes));
 }
 
+static void VMRuntime_registerSensitiveThread(JNIEnv*, jobject) {
+  Runtime::Current()->RegisterSensitiveThread();
+}
+
 static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
   if (UNLIKELY(bytes < 0)) {
     ScopedObjectAccess soa(env);
@@ -643,6 +647,7 @@
   NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
   NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
+  NATIVE_METHOD(VMRuntime, registerSensitiveThread, "()V"),
   NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"),
   NATIVE_METHOD(VMRuntime, requestConcurrentGC, "()V"),
   NATIVE_METHOD(VMRuntime, requestHeapTrim, "()V"),
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c8d4291..b25a1bb 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -153,7 +153,7 @@
       .Define("-Xusejit:_")
           .WithType<bool>()
           .WithValueMap({{"false", false}, {"true", true}})
-          .IntoKey(M::UseJIT)
+          .IntoKey(M::UseJitCompilation)
       .Define("-Xjitinitialsize:_")
           .WithType<MemoryKiB>()
           .IntoKey(M::JITCodeCacheInitialCapacity)
@@ -172,6 +172,9 @@
       .Define("-Xjitprithreadweight:_")
           .WithType<unsigned int>()
           .IntoKey(M::JITPriorityThreadWeight)
+      .Define("-Xjittransitionweight:_")
+          .WithType<unsigned int>()
+          .IntoKey(M::JITInvokeTransitionWeight)
       .Define("-Xjitsaveprofilinginfo")
           .WithValue(true)
           .IntoKey(M::JITSaveProfilingInfo)
@@ -471,6 +474,11 @@
     LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
   }
 
+  if (args.GetOrDefault(M::UseJitCompilation) && args.GetOrDefault(M::Interpret)) {
+    Usage("-Xusejit:true and -Xint cannot be specified together");
+    Exit(0);
+  }
+
   // Set a default boot class path if we didn't get an explicit one via command line.
   if (getenv("BOOTCLASSPATH") != nullptr) {
     args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index c7ccee2..1dea562 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -434,7 +434,7 @@
 bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
                                              InlineMethod* result) {
   DCHECK(verifier != nullptr);
-  if (!Runtime::Current()->UseJit()) {
+  if (!Runtime::Current()->UseJitCompilation()) {
     DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr);
   }
 
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index a785ecb..a3e1f00 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -509,7 +509,7 @@
   // Compiled code made an explicit deoptimization.
   ArtMethod* deopt_method = visitor.GetSingleFrameDeoptMethod();
   DCHECK(deopt_method != nullptr);
-  if (Runtime::Current()->UseJit()) {
+  if (Runtime::Current()->UseJitCompilation()) {
     Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor(
         deopt_method, visitor.GetSingleFrameDeoptQuickMethodHeader());
   } else {
@@ -611,7 +611,7 @@
 // Prints out methods with their type of frame.
 class DumpFramesWithTypeStackVisitor FINAL : public StackVisitor {
  public:
-  DumpFramesWithTypeStackVisitor(Thread* self, bool show_details = false)
+  explicit DumpFramesWithTypeStackVisitor(Thread* self, bool show_details = false)
       SHARED_REQUIRES(Locks::mutator_lock_)
       : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
         show_details_(show_details) {}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a4d31ef..bb19cbd 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -558,15 +558,21 @@
 
   started_ = true;
 
-  if (jit_options_->UseJIT()) {
+  // Create the JIT either if we have to use JIT compilation or save profiling info.
+  // TODO(calin): We use the JIT class as a proxy for JIT compilation and for
+  // recoding profiles. Maybe we should consider changing the name to be more clear it's
+  // not only about compiling. b/28295073.
+  if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
     std::string error_msg;
     if (!IsZygote()) {
     // If we are the zygote then we need to wait until after forking to create the code cache
     // due to SELinux restrictions on r/w/x memory regions.
       CreateJit();
-    } else if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
-      // Try to load compiler pre zygote to reduce PSS. b/27744947
-      LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
+    } else if (jit_options_->UseJitCompilation()) {
+      if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
+        // Try to load compiler pre zygote to reduce PSS. b/27744947
+        LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
+      }
     }
   }
 
@@ -713,7 +719,11 @@
   // before fork aren't attributed to an app.
   heap_->ResetGcPerformanceInfo();
 
-  if (!is_system_server && !safe_mode_ && jit_options_->UseJIT() && jit_.get() == nullptr) {
+
+  if (!is_system_server &&
+      !safe_mode_ &&
+      (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
+      jit_.get() == nullptr) {
     // Note that when running ART standalone (not zygote, nor zygote fork),
     // the jit may have already been created.
     CreateJit();
@@ -1016,7 +1026,8 @@
     // this case.
     // If runtime_options doesn't have UseJIT set to true then CreateFromRuntimeArguments returns
     // null and we don't create the jit.
-    jit_options_->SetUseJIT(false);
+    jit_options_->SetUseJitCompilation(false);
+    jit_options_->SetSaveProfilingInfo(false);
   }
 
   // Allocate a global table of boxed lambda objects <-> closures.
@@ -1613,18 +1624,19 @@
   }
 }
 
-static ImtConflictTable::Entry empty_entry = { nullptr, nullptr };
-
 ArtMethod* Runtime::CreateImtConflictMethod(LinearAlloc* linear_alloc) {
-  auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod(linear_alloc);
+  ClassLinker* const class_linker = GetClassLinker();
+  ArtMethod* method = class_linker->CreateRuntimeMethod(linear_alloc);
   // When compiling, the code pointer will get set later when the image is loaded.
+  const size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
   if (IsAotCompiler()) {
-    size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
     method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
   } else {
     method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
-    method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
   }
+  // Create empty conflict table.
+  method->SetImtConflictTable(class_linker->CreateImtConflictTable(/*count*/0u, linear_alloc),
+                              pointer_size);
   return method;
 }
 
@@ -1632,9 +1644,6 @@
   CHECK(method != nullptr);
   CHECK(method->IsRuntimeMethod());
   imt_conflict_method_ = method;
-  if (!IsAotCompiler()) {
-    method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
-  }
 }
 
 ArtMethod* Runtime::CreateResolutionMethod() {
@@ -1915,9 +1924,8 @@
 
 void Runtime::CreateJit() {
   CHECK(!IsAotCompiler());
-  if (GetInstrumentation()->IsForcedInterpretOnly()) {
-    // Don't create JIT if forced interpret only.
-    return;
+  if (kIsDebugBuild && GetInstrumentation()->IsForcedInterpretOnly()) {
+    DCHECK(!jit_options_->UseJitCompilation());
   }
   std::string error_msg;
   jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
@@ -1944,8 +1952,20 @@
   CHECK(method != nullptr);
   CHECK(method->IsRuntimeMethod());
   imt_unimplemented_method_ = method;
-  if (!IsAotCompiler()) {
-    method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
+}
+
+void Runtime::FixupConflictTables() {
+  // We can only do this after the class linker is created.
+  const size_t pointer_size = GetClassLinker()->GetImagePointerSize();
+  if (imt_unimplemented_method_->GetImtConflictTable(pointer_size) == nullptr) {
+    imt_unimplemented_method_->SetImtConflictTable(
+        ClassLinker::CreateImtConflictTable(/*count*/0u, GetLinearAlloc(), pointer_size),
+        pointer_size);
+  }
+  if (imt_conflict_method_->GetImtConflictTable(pointer_size) == nullptr) {
+    imt_conflict_method_->SetImtConflictTable(
+          ClassLinker::CreateImtConflictTable(/*count*/0u, GetLinearAlloc(), pointer_size),
+          pointer_size);
   }
 }
 
@@ -1981,4 +2001,18 @@
   GetHeap()->UpdateProcessState(old_process_state, process_state);
 }
 
+void Runtime::RegisterSensitiveThread() const {
+  Thread::SetJitSensitiveThread();
+}
+
+// Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
+bool Runtime::UseJitCompilation() const {
+  return (jit_ != nullptr) && jit_->UseJitCompilation();
+}
+
+// Returns true if profile saving is enabled. GetJit() will be not null in this case.
+bool Runtime::SaveProfileInfo() const {
+  return (jit_ != nullptr) && jit_->SaveProfilingInfo();
+}
+
 }  // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ae25dd1..1394462 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -127,7 +127,7 @@
 
   // IsAotCompiler for compilers that don't have a running runtime. Only dex2oat currently.
   bool IsAotCompiler() const {
-    return !UseJit() && IsCompiler();
+    return !UseJitCompilation() && IsCompiler();
   }
 
   // IsCompiler is any runtime which has a running compiler, either dex2oat or JIT.
@@ -383,6 +383,7 @@
     return imt_conflict_method_ != nullptr;
   }
 
+  void FixupConflictTables();
   void SetImtConflictMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
   void SetImtUnimplementedMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -451,9 +452,11 @@
   jit::Jit* GetJit() {
     return jit_.get();
   }
-  bool UseJit() const {
-    return jit_.get() != nullptr;
-  }
+
+  // Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
+  bool UseJitCompilation() const;
+  // Returns true if profile saving is enabled. GetJit() will be not null in this case.
+  bool SaveProfileInfo() const;
 
   void PreZygoteFork();
   bool InitZygote();
@@ -635,6 +638,8 @@
     return process_state_ == kProcessStateJankPerceptible;
   }
 
+  void RegisterSensitiveThread() const;
+
   void SetZygoteNoThreadSection(bool val) {
     zygote_no_threads_ = val;
   }
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 6433c33..2a96703 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -66,12 +66,13 @@
 RUNTIME_OPTIONS_KEY (Unit,                LowMemoryMode)
 RUNTIME_OPTIONS_KEY (bool,                UseTLAB,                        (kUseTlab || kUseReadBarrier))
 RUNTIME_OPTIONS_KEY (bool,                EnableHSpaceCompactForOOM,      true)
-RUNTIME_OPTIONS_KEY (bool,                UseJIT,                         false)
+RUNTIME_OPTIONS_KEY (bool,                UseJitCompilation,              false)
 RUNTIME_OPTIONS_KEY (bool,                DumpNativeStackOnSigQuit,       true)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITCompileThreshold,            jit::Jit::kDefaultCompileThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITWarmupThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITOsrThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITPriorityThreadWeight)
+RUNTIME_OPTIONS_KEY (unsigned int,        JITInvokeTransitionWeight)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           JITCodeCacheInitialCapacity,    jit::JitCodeCache::kInitialCapacity)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           JITCodeCacheMaxCapacity,        jit::JitCodeCache::kMaxCapacity)
 RUNTIME_OPTIONS_KEY (bool,                JITSaveProfilingInfo,           false)
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 56ef5aa..a5ca527 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -637,8 +637,8 @@
 
   // If we are the JIT then we may have just compiled the method after the
   // IsQuickToInterpreterBridge check.
-  jit::Jit* const jit = Runtime::Current()->GetJit();
-  if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime->UseJitCompilation() && runtime->GetJit()->GetCodeCache()->ContainsPc(code)) {
     return;
   }
 
@@ -678,8 +678,10 @@
           if (space->IsImageSpace()) {
             auto* image_space = space->AsImageSpace();
             const auto& header = image_space->GetImageHeader();
-            const auto* methods = &header.GetMethodsSection();
-            if (methods->Contains(reinterpret_cast<const uint8_t*>(method) - image_space->Begin())) {
+            const ImageSection& methods = header.GetMethodsSection();
+            const ImageSection& runtime_methods = header.GetRuntimeMethodsSection();
+            const size_t offset =  reinterpret_cast<const uint8_t*>(method) - image_space->Begin();
+            if (methods.Contains(offset) || runtime_methods.Contains(offset)) {
               in_image = true;
               break;
             }
@@ -948,7 +950,7 @@
   }
 }
 
-void LockCountData::AddMonitorInternal(Thread* self, mirror::Object* obj) {
+void LockCountData::AddMonitor(Thread* self, mirror::Object* obj) {
   if (obj == nullptr) {
     return;
   }
@@ -965,7 +967,7 @@
   monitors_->push_back(obj);
 }
 
-void LockCountData::RemoveMonitorInternal(Thread* self, const mirror::Object* obj) {
+void LockCountData::RemoveMonitorOrThrow(Thread* self, const mirror::Object* obj) {
   if (obj == nullptr) {
     return;
   }
@@ -998,7 +1000,7 @@
   obj->MonitorExit(self);
 }
 
-bool LockCountData::CheckAllMonitorsReleasedInternal(Thread* self) {
+bool LockCountData::CheckAllMonitorsReleasedOrThrow(Thread* self) {
   DCHECK(self != nullptr);
   if (monitors_ != nullptr) {
     if (!monitors_->empty()) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 7301184..e77ab46 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -80,39 +80,18 @@
  public:
   // Add the given object to the list of monitors, that is, objects that have been locked. This
   // will not throw (but be skipped if there is an exception pending on entry).
-  template <bool kLockCounting>
-  void AddMonitor(Thread* self, mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
-    DCHECK(self != nullptr);
-    if (!kLockCounting) {
-      return;
-    }
-    AddMonitorInternal(self, obj);
-  }
+  void AddMonitor(Thread* self, mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Try to remove the given object from the monitor list, indicating an unlock operation.
   // This will throw an IllegalMonitorStateException (clearing any already pending exception), in
   // case that there wasn't a lock recorded for the object.
-  template <bool kLockCounting>
   void RemoveMonitorOrThrow(Thread* self,
-                            const mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
-    DCHECK(self != nullptr);
-    if (!kLockCounting) {
-      return;
-    }
-    RemoveMonitorInternal(self, obj);
-  }
+                            const mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Check whether all acquired monitors have been released. This will potentially throw an
   // IllegalMonitorStateException, clearing any already pending exception. Returns true if the
   // check shows that everything is OK wrt/ lock counting, false otherwise.
-  template <bool kLockCounting>
-  bool CheckAllMonitorsReleasedOrThrow(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) {
-    DCHECK(self != nullptr);
-    if (!kLockCounting) {
-      return true;
-    }
-    return CheckAllMonitorsReleasedInternal(self);
-  }
+  bool CheckAllMonitorsReleasedOrThrow(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_);
 
   template <typename T, typename... Args>
   void VisitMonitors(T visitor, Args&&... args) SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -125,12 +104,6 @@
   }
 
  private:
-  // Internal implementations.
-  void AddMonitorInternal(Thread* self, mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
-  void RemoveMonitorInternal(Thread* self, const mirror::Object* obj)
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  bool CheckAllMonitorsReleasedInternal(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_);
-
   // Stores references to the locked-on objects. As noted, this should be visited during thread
   // marking.
   std::unique_ptr<std::vector<mirror::Object*>> monitors_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 7922b60..fb24828 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -89,6 +89,7 @@
 ConditionVariable* Thread::resume_cond_ = nullptr;
 const size_t Thread::kStackOverflowImplicitCheckSize = GetStackOverflowReservedBytes(kRuntimeISA);
 bool (*Thread::is_sensitive_thread_hook_)() = nullptr;
+Thread* Thread::jit_sensitive_thread_ = nullptr;
 
 static constexpr bool kVerifyImageObjectsMarked = kIsDebugBuild;
 
@@ -739,7 +740,7 @@
   {
     MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
     if (runtime->IsShuttingDownLocked()) {
-      LOG(ERROR) << "Thread attaching while runtime is shutting down: " << thread_name;
+      LOG(WARNING) << "Thread attaching while runtime is shutting down: " << thread_name;
       return nullptr;
     } else {
       Runtime::Current()->StartThreadBirth();
@@ -3010,7 +3011,6 @@
   return count;
 }
 
-
 void Thread::DeoptimizeWithDeoptimizationException(JValue* result) {
   DCHECK_EQ(GetException(), Thread::GetDeoptimizationException());
   ClearException();
@@ -3031,4 +3031,11 @@
   interpreter::EnterInterpreterFromDeoptimize(this, shadow_frame, from_code, result);
 }
 
+void Thread::SetException(mirror::Throwable* new_exception) {
+  CHECK(new_exception != nullptr);
+  // TODO: DCHECK(!IsExceptionPending());
+  tlsPtr_.exception = new_exception;
+  // LOG(ERROR) << new_exception->Dump();
+}
+
 }  // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index ed42e46..582a0cd 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -363,12 +363,7 @@
   void AssertNoPendingException() const;
   void AssertNoPendingExceptionForNewException(const char* msg) const;
 
-  void SetException(mirror::Throwable* new_exception)
-      SHARED_REQUIRES(Locks::mutator_lock_) {
-    CHECK(new_exception != nullptr);
-    // TODO: DCHECK(!IsExceptionPending());
-    tlsPtr_.exception = new_exception;
-  }
+  void SetException(mirror::Throwable* new_exception) SHARED_REQUIRES(Locks::mutator_lock_);
 
   void ClearException() SHARED_REQUIRES(Locks::mutator_lock_) {
     tlsPtr_.exception = nullptr;
@@ -1098,6 +1093,12 @@
     return debug_disallow_read_barrier_;
   }
 
+  // Returns true if the current thread is the jit sensitive thread.
+  bool IsJitSensitiveThread() const {
+    return this == jit_sensitive_thread_;
+  }
+
+  // Returns true if StrictMode events are traced for the current thread.
   static bool IsSensitiveThread() {
     if (is_sensitive_thread_hook_ != nullptr) {
       return (*is_sensitive_thread_hook_)();
@@ -1180,6 +1181,16 @@
   ALWAYS_INLINE void PassActiveSuspendBarriers()
       REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_);
 
+  // Registers the current thread as the jit sensitive thread. Should be called just once.
+  static void SetJitSensitiveThread() {
+    if (jit_sensitive_thread_ == nullptr) {
+      jit_sensitive_thread_ = Thread::Current();
+    } else {
+      LOG(WARNING) << "Attempt to set the sensitive thread twice. Tid:"
+          << Thread::Current()->GetTid();
+    }
+  }
+
   static void SetSensitiveThreadHook(bool (*is_sensitive_thread_hook)()) {
     is_sensitive_thread_hook_ = is_sensitive_thread_hook;
   }
@@ -1229,6 +1240,8 @@
   // Hook passed by framework which returns true
   // when StrictMode events are traced for the current thread.
   static bool (*is_sensitive_thread_hook_)();
+  // Stores the jit sensitive thread (which for now is the UI thread).
+  static Thread* jit_sensitive_thread_;
 
   /***********************************************************************************************/
   // Thread local storage. Fields are grouped by size to enable 32 <-> 64 searching to account for
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 8802e62..d05ae42 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -401,8 +401,13 @@
         method->SetAccessFlags(method->GetAccessFlags() | kAccCompileDontBother);
       }
     }
-    if (method != nullptr && verifier.HasInstructionThatWillThrow()) {
-      method->SetAccessFlags(method->GetAccessFlags() | kAccCompileDontBother);
+    if (method != nullptr) {
+      if (verifier.HasInstructionThatWillThrow()) {
+        method->SetAccessFlags(method->GetAccessFlags() | kAccCompileDontBother);
+      }
+      if ((verifier.encountered_failure_types_ & VerifyError::VERIFY_ERROR_LOCKING) != 0) {
+        method->SetAccessFlags(method->GetAccessFlags() | kAccMustCountLocks);
+      }
     }
   } else {
     // Bad method data.
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 15683b0..17a6049 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -23,6 +23,7 @@
 
 public class Main {
     static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar";
+    static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
     static String nativeLibraryName;
 
     public static void main(String[] args) throws Exception {
@@ -32,7 +33,7 @@
             throw new AssertionError("Couldn't find path class loader class");
         }
         Constructor constructor =
-            pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+            pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
         try {
             testUnloadClass(constructor);
             testUnloadLoader(constructor);
@@ -49,7 +50,7 @@
             // Test that the oat files are unloaded.
             testOatFilesUnloaded(getPid());
         } catch (Exception e) {
-            System.out.println(e);
+            e.printStackTrace();
         }
     }
 
@@ -118,7 +119,7 @@
     private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
         WeakReference<ClassLoader> loader =
             new WeakReference((ClassLoader) constructor.newInstance(
-                DEX_FILE, ClassLoader.getSystemClassLoader()));
+                DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()));
         WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
         intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get());
         boolean isNull = loader.get() == null;
@@ -128,7 +129,7 @@
     private static void testNoUnloadInstance(Constructor constructor) throws Exception {
         WeakReference<ClassLoader> loader =
             new WeakReference((ClassLoader) constructor.newInstance(
-                DEX_FILE, ClassLoader.getSystemClassLoader()));
+                DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()));
         WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
         Object o = intHolder.get().newInstance();
         Runtime.getRuntime().gc();
@@ -138,7 +139,7 @@
 
     private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception {
         ClassLoader loader = (ClassLoader) constructor.newInstance(
-            DEX_FILE, ClassLoader.getSystemClassLoader());
+            DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
         Class intHolder = loader.loadClass("IntHolder");
         Method getValue = intHolder.getDeclaredMethod("getValue");
         Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
@@ -155,7 +156,7 @@
                                                                 boolean waitForCompilation)
         throws Exception {
         ClassLoader loader = (ClassLoader) constructor.newInstance(
-            DEX_FILE, ClassLoader.getSystemClassLoader());
+            DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
         Class intHolder = loader.loadClass("IntHolder");
         Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
         setValue.invoke(intHolder, 2);
@@ -177,7 +178,7 @@
     private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
         throws Exception {
         ClassLoader loader = (ClassLoader) constructor.newInstance(
-            DEX_FILE, ClassLoader.getSystemClassLoader());
+            DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
         Class intHolder = loader.loadClass("IntHolder");
         Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
         loadLibrary.invoke(intHolder, nativeLibraryName);
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index edb8a68..a4561b8 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -500,9 +500,9 @@
     assertIntEquals(a + $noinline$IntShl(b, 16),  a + (b << 16));
     assertIntEquals(a + $noinline$IntShl(b, 30),  a + (b << 30));
     assertIntEquals(a + $noinline$IntShl(b, 31),  a + (b << 31));
-    assertIntEquals(a + $noinline$IntShl(b, 32),  a + (b << 32));
-    assertIntEquals(a + $noinline$IntShl(b, 62),  a + (b << 62));
-    assertIntEquals(a + $noinline$IntShl(b, 63),  a + (b << 63));
+    assertIntEquals(a + $noinline$IntShl(b, 32),  a + (b << $opt$inline$IntConstant32()));
+    assertIntEquals(a + $noinline$IntShl(b, 62),  a + (b << $opt$inline$IntConstant62()));
+    assertIntEquals(a + $noinline$IntShl(b, 63),  a + (b << $opt$inline$IntConstant63()));
 
     assertIntEquals(a - $noinline$IntShr(b, 1),   a - (b >>  1));
     assertIntEquals(a - $noinline$IntShr(b, 6),   a - (b >>  6));
@@ -513,9 +513,9 @@
     assertIntEquals(a - $noinline$IntShr(b, 16),  a - (b >> 16));
     assertIntEquals(a - $noinline$IntShr(b, 30),  a - (b >> 30));
     assertIntEquals(a - $noinline$IntShr(b, 31),  a - (b >> 31));
-    assertIntEquals(a - $noinline$IntShr(b, 32),  a - (b >> 32));
-    assertIntEquals(a - $noinline$IntShr(b, 62),  a - (b >> 62));
-    assertIntEquals(a - $noinline$IntShr(b, 63),  a - (b >> 63));
+    assertIntEquals(a - $noinline$IntShr(b, 32),  a - (b >> $opt$inline$IntConstant32()));
+    assertIntEquals(a - $noinline$IntShr(b, 62),  a - (b >> $opt$inline$IntConstant62()));
+    assertIntEquals(a - $noinline$IntShr(b, 63),  a - (b >> $opt$inline$IntConstant63()));
 
     assertIntEquals(a ^ $noinline$IntUshr(b, 1),   a ^ (b >>>  1));
     assertIntEquals(a ^ $noinline$IntUshr(b, 6),   a ^ (b >>>  6));
@@ -526,11 +526,17 @@
     assertIntEquals(a ^ $noinline$IntUshr(b, 16),  a ^ (b >>> 16));
     assertIntEquals(a ^ $noinline$IntUshr(b, 30),  a ^ (b >>> 30));
     assertIntEquals(a ^ $noinline$IntUshr(b, 31),  a ^ (b >>> 31));
-    assertIntEquals(a ^ $noinline$IntUshr(b, 32),  a ^ (b >>> 32));
-    assertIntEquals(a ^ $noinline$IntUshr(b, 62),  a ^ (b >>> 62));
-    assertIntEquals(a ^ $noinline$IntUshr(b, 63),  a ^ (b >>> 63));
+    assertIntEquals(a ^ $noinline$IntUshr(b, 32),  a ^ (b >>> $opt$inline$IntConstant32()));
+    assertIntEquals(a ^ $noinline$IntUshr(b, 62),  a ^ (b >>> $opt$inline$IntConstant62()));
+    assertIntEquals(a ^ $noinline$IntUshr(b, 63),  a ^ (b >>> $opt$inline$IntConstant63()));
   }
 
+  // Hiding constants outside the range [0, 32) used for int shifts from Jack.
+  // (Jack extracts only the low 5 bits.)
+  public static int $opt$inline$IntConstant32() { return 32; }
+  public static int $opt$inline$IntConstant62() { return 62; }
+  public static int $opt$inline$IntConstant63() { return 63; }
+
 
   static long $noinline$LongShl(long b, long c) {
     if (doThrow) throw new Error();
diff --git a/test/557-checker-instruction-simplifier-ror/src/Main.java b/test/557-checker-instruction-simplifier-ror/src/Main.java
index 027f262..6d8b74d 100644
--- a/test/557-checker-instruction-simplifier-ror/src/Main.java
+++ b/test/557-checker-instruction-simplifier-ror/src/Main.java
@@ -175,7 +175,7 @@
 
   //  (i >>> #distance) | (i << #-distance)
 
-  /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier (before)
+  /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier_after_bce (before)
   /// CHECK:          <<ArgValue:i\d+>>     ParameterValue
   /// CHECK:          <<Const2:i\d+>>       IntConstant 2
   /// CHECK:          <<ConstNeg2:i\d+>>    IntConstant -2
@@ -184,19 +184,23 @@
   /// CHECK:          <<Or:i\d+>>           Or [<<UShr>>,<<Shl>>]
   /// CHECK:                                Return [<<Or>>]
 
-  /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier (after)
+  /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier_after_bce (after)
   /// CHECK:          <<ArgValue:i\d+>>     ParameterValue
   /// CHECK:          <<Const2:i\d+>>       IntConstant 2
   /// CHECK:          <<Ror:i\d+>>          Ror [<<ArgValue>>,<<Const2>>]
   /// CHECK:                                Return [<<Ror>>]
 
-  /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier (after)
+  /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier_after_bce (after)
   /// CHECK-NOT:      UShr
   /// CHECK-NOT:      Shl
   public static int ror_int_constant_c_negc(int value) {
-    return (value >>> 2) | (value << -2);
+    return (value >>> 2) | (value << $opt$inline$IntConstantM2());
   }
 
+  // Hiding constants outside the range [0, 32) used for int shifts from Jack.
+  // (Jack extracts only the low 5 bits.)
+  public static int $opt$inline$IntConstantM2() { return -2; }
+
   //  (j >>> #distance) | (j << #-distance)
 
   /// CHECK-START: long Main.ror_long_constant_c_negc(long) instruction_simplifier (before)
diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc
index 167a575..7b2c6cb 100644
--- a/test/566-polymorphic-inlining/polymorphic_inline.cc
+++ b/test/566-polymorphic-inlining/polymorphic_inline.cc
@@ -60,6 +60,7 @@
 
   do_checks(cls, "testInvokeVirtual");
   do_checks(cls, "testInvokeInterface");
+  do_checks(cls, "$noinline$testInlineToSameTarget");
 }
 
 }  // namespace art
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
index 7283e86..286f0d9 100644
--- a/test/566-polymorphic-inlining/src/Main.java
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -25,6 +25,12 @@
     }
   }
 
+  public static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new Error("Expected " + expected  + ", got " + actual);
+    }
+  }
+
   public static void main(String[] args) throws Exception {
     System.loadLibrary(args[0]);
     Main[] mains = new Main[3];
@@ -41,6 +47,8 @@
       testInvokeVirtual(mains[1]);
       testInvokeInterface(itfs[0]);
       testInvokeInterface(itfs[1]);
+      $noinline$testInlineToSameTarget(mains[0]);
+      $noinline$testInlineToSameTarget(mains[1]);
     }
 
     ensureJittedAndPolymorphicInline();
@@ -56,6 +64,10 @@
     // This will trigger a deoptimization of the compiled code.
     assertEquals(OtherSubclass.class, testInvokeVirtual(mains[2]));
     assertEquals(OtherSubclass.class, testInvokeInterface(itfs[2]));
+
+    // Run this once to make sure we execute the JITted code.
+    $noinline$testInlineToSameTarget(mains[0]);
+    assertEquals(20001, counter);
   }
 
   public Class sameInvokeVirtual() {
@@ -76,9 +88,20 @@
     return m.sameInvokeVirtual();
   }
 
+  public static void $noinline$testInlineToSameTarget(Main m) {
+    if (doThrow) throw new Error("");
+    m.increment();
+  }
+
   public Object field = new Object();
 
   public static native void ensureJittedAndPolymorphicInline();
+
+  public void increment() {
+    counter++;
+  }
+  public static int counter = 0;
+  public static boolean doThrow = false;
 }
 
 class Subclass extends Main {
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
index 2a5b2c9..bd8f0a9 100644
--- a/test/570-checker-osr/osr.cc
+++ b/test/570-checker-osr/osr.cc
@@ -75,7 +75,7 @@
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInInterpreter(JNIEnv* env,
                                                                 jclass,
                                                                 jstring method_name) {
-  if (!Runtime::Current()->UseJit()) {
+  if (!Runtime::Current()->UseJitCompilation()) {
     // The return value is irrelevant if we're not using JIT.
     return false;
   }
@@ -111,7 +111,7 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv* env,
                                                                    jclass,
                                                                    jstring method_name) {
-  if (!Runtime::Current()->UseJit()) {
+  if (!Runtime::Current()->UseJitCompilation()) {
     return;
   }
   ScopedUtfChars chars(env, method_name);
@@ -151,7 +151,7 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv* env,
                                                              jclass,
                                                              jstring method_name) {
-  if (!Runtime::Current()->UseJit()) {
+  if (!Runtime::Current()->UseJitCompilation()) {
     return;
   }
   ScopedUtfChars chars(env, method_name);
diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java
index 200b54a..15c232d 100644
--- a/test/570-checker-osr/src/Main.java
+++ b/test/570-checker-osr/src/Main.java
@@ -61,6 +61,18 @@
       throw new Error("Unexpected return value");
     }
 
+    $noinline$inlineCache2(new Main(), /* isSecondInvocation */ false);
+    if ($noinline$inlineCache2(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) {
+      throw new Error("Unexpected return value");
+    }
+
+    // Test polymorphic inline cache to the same target (inlineCache3).
+    $noinline$inlineCache3(new Main(), /* isSecondInvocation */ false);
+    $noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ false);
+    if ($noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ true) != null) {
+      throw new Error("Unexpected return value");
+    }
+
     $noinline$stackOverflow(new Main(), /* isSecondInvocation */ false);
     $noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true);
 
@@ -147,10 +159,76 @@
     return other.returnClass();
   }
 
+  public static Class $noinline$inlineCache2(Main m, boolean isSecondInvocation) {
+    // If we are running in non-JIT mode, or were unlucky enough to get this method
+    // already JITted, just return the expected value.
+    if (!isInInterpreter("$noinline$inlineCache2")) {
+      return SubMain.class;
+    }
+
+    ensureHasProfilingInfo("$noinline$inlineCache2");
+
+    // Ensure that we have OSR code to jump to.
+    if (isSecondInvocation) {
+      ensureHasOsrCode("$noinline$inlineCache2");
+    }
+
+    // This call will be optimized in the OSR compiled code
+    // to check and deoptimize if m is not of type 'Main'.
+    Main other = m.inlineCache2();
+
+    // Jump to OSR compiled code. The second run
+    // of this method will have 'm' as a SubMain, and the compiled
+    // code we are jumping to will have wrongly optimize other as being null.
+    if (isSecondInvocation) {
+      while (!isInOsrCode("$noinline$inlineCache2")) {}
+    }
+
+    // We used to wrongly optimize this code and assume 'other' was always null.
+    return (other == null) ? null : other.returnClass();
+  }
+
+  public static Class $noinline$inlineCache3(Main m, boolean isSecondInvocation) {
+    // If we are running in non-JIT mode, or were unlucky enough to get this method
+    // already JITted, just return the expected value.
+    if (!isInInterpreter("$noinline$inlineCache3")) {
+      return null;
+    }
+
+    ensureHasProfilingInfo("$noinline$inlineCache3");
+
+    // Ensure that we have OSR code to jump to.
+    if (isSecondInvocation) {
+      ensureHasOsrCode("$noinline$inlineCache3");
+    }
+
+    // This call will be optimized in the OSR compiled code
+    // to check and deoptimize if m is not of type 'Main'.
+    Main other = m.inlineCache3();
+
+    // Jump to OSR compiled code. The second run
+    // of this method will have 'm' as a SubMain, and the compiled
+    // code we are jumping to will have wrongly optimize other as being null.
+    if (isSecondInvocation) {
+      while (!isInOsrCode("$noinline$inlineCache3")) {}
+    }
+
+    // We used to wrongly optimize this code and assume 'other' was always null.
+    return (other == null) ? null : other.returnClass();
+  }
+
   public Main inlineCache() {
     return new Main();
   }
 
+  public Main inlineCache2() {
+    return null;
+  }
+
+  public Main inlineCache3() {
+    return null;
+  }
+
   public Class returnClass() {
     return Main.class;
   }
@@ -235,6 +313,10 @@
     return new SubMain();
   }
 
+  public Main inlineCache2() {
+    return new SubMain();
+  }
+
   public void otherInlineCache() {
     return;
   }
diff --git a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
index 366c7b9..ef53ee8 100644
--- a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
+++ b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
@@ -67,3 +67,57 @@
    return p3
 
 .end method
+
+## CHECK-START: int IrreducibleLoop.liveness2(boolean, boolean, boolean, int) builder (after)
+## CHECK-DAG:     Mul loop:<<Loop:B\d+>>
+## CHECK-DAG:     Not loop:<<Loop>>
+
+## CHECK-START: int IrreducibleLoop.liveness2(boolean, boolean, boolean, int) liveness (after)
+## CHECK-DAG:     Mul liveness:<<LPreEntry2:\d+>>
+## CHECK-DAG:     Not liveness:<<LBackEdge1:\d+>>
+## CHECK-EVAL:    <<LBackEdge1>> < <<LPreEntry2>>
+
+.method public liveness2(ZZZI)I
+    .registers 10
+
+    const v1, 1
+
+    :header1
+    if-eqz p0, :body1
+
+    :exit
+    return p3
+
+    :body1
+    # The test will generate an incorrect linear order when the following IF swaps
+    # its successors. To do that, load a boolean value and compare NotEqual to 1.
+    sget-boolean v2, LIrreducibleLoop;->f:Z
+    const v3, 1
+    if-ne v2, v3, :pre_header2
+
+    :pre_entry2
+    # This constant has a use in a phi in :back_edge2 and a back edge use in
+    # :back_edge1. Because the linear order is wrong, the back edge use has
+    # a lower liveness than the phi use.
+    const v0, 42
+    mul-int/2addr p3, p3
+    goto :back_edge2
+
+    :back_edge2
+    add-int/2addr p3, v0
+    add-int/2addr v0, v1
+    goto :header2
+
+    :header2
+    if-eqz p2, :back_edge2
+
+    :back_edge1
+    not-int p3, p3
+    goto :header1
+
+    :pre_header2
+    const v0, 42
+    goto :header2
+.end method
+
+.field public static f:Z
diff --git a/test/595-error-class/expected.txt b/test/595-error-class/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/595-error-class/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/595-error-class/info.txt b/test/595-error-class/info.txt
new file mode 100644
index 0000000..a58b8b3
--- /dev/null
+++ b/test/595-error-class/info.txt
@@ -0,0 +1 @@
+Regression test on merging array type with error component type.
diff --git a/test/595-error-class/smali/error.smali b/test/595-error-class/smali/error.smali
new file mode 100644
index 0000000..925c34b
--- /dev/null
+++ b/test/595-error-class/smali/error.smali
@@ -0,0 +1,23 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public final LAnError;
+
+.super LSuperOfAnError;
+
+# Override a final method to put this class in the error state.
+.method public foo()V
+  .registers 1
+  return-void
+.end method
diff --git a/test/595-error-class/smali/merge.smali b/test/595-error-class/smali/merge.smali
new file mode 100644
index 0000000..2f8b415
--- /dev/null
+++ b/test/595-error-class/smali/merge.smali
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMerge;
+.super Ljava/lang/Object;
+
+# Method that selects between x = new Integer[] or new AnError[],
+# Reference type propagation should correctly see error in component type.
+.method public static select(Z)Ljava/lang/Object;
+    .registers 2
+    const/16 v0, 10
+    if-eqz v1, :Skip
+    new-array v0, v0, [LAnError;
+    goto :Done
+:Skip
+    new-array v0, v0, [Ljava/lang/Integer;
+:Done
+    return-object v0
+.end method
diff --git a/test/595-error-class/smali/super.smali b/test/595-error-class/smali/super.smali
new file mode 100644
index 0000000..da7467d
--- /dev/null
+++ b/test/595-error-class/smali/super.smali
@@ -0,0 +1,22 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSuperOfAnError;
+
+.super Ljava/lang/Object;
+
+.method public final foo()V
+  .registers 1
+  return-void
+.end method
diff --git a/test/595-error-class/src/Main.java b/test/595-error-class/src/Main.java
new file mode 100644
index 0000000..655fa43
--- /dev/null
+++ b/test/595-error-class/src/Main.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+
+public class Main {
+
+  public static void main(String args[]) throws Throwable {
+    Class<?> c = Class.forName("Merge");
+    Method m = c.getMethod("select", boolean.class);
+    Object x = m.invoke(null, true);
+    if (x == null) {
+      throw new Error("Did not get array");
+    }
+    System.out.println("passed");
+  }
+}
diff --git a/test/595-profile-saving/expected.txt b/test/595-profile-saving/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/595-profile-saving/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/595-profile-saving/info.txt b/test/595-profile-saving/info.txt
new file mode 100644
index 0000000..5d318f5
--- /dev/null
+++ b/test/595-profile-saving/info.txt
@@ -0,0 +1 @@
+Check that profile recording works even when JIT compilation is not enabled.
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
new file mode 100644
index 0000000..a7e6f8b
--- /dev/null
+++ b/test/595-profile-saving/profile-saving.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file.h"
+
+#include "jit/offline_profiling_info.h"
+#include "jit/profile_saver.h"
+#include "jni.h"
+#include "method_reference.h"
+#include "mirror/class-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
+#include "thread.h"
+
+namespace art {
+namespace {
+
+class CreateProfilingInfoVisitor : public StackVisitor {
+ public:
+  explicit CreateProfilingInfoVisitor(Thread* thread, const char* method_name)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+        method_name_(method_name) {}
+
+  bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+    ArtMethod* m = GetMethod();
+    std::string m_name(m->GetName());
+
+    if (m_name.compare(method_name_) == 0) {
+      ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
+      method_index_ = m->GetDexMethodIndex();
+      return false;
+    }
+    return true;
+  }
+
+  int method_index_ = -1;
+  const char* const method_name_;
+};
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
+                                                                jclass,
+                                                                jstring method_name) {
+  ScopedUtfChars chars(env, method_name);
+  CHECK(chars.c_str() != nullptr);
+  ScopedObjectAccess soa(Thread::Current());
+  CreateProfilingInfoVisitor visitor(soa.Self(), chars.c_str());
+  visitor.WalkStack();
+  return visitor.method_index_;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
+  ProfileSaver::ForceProcessProfiles();
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(
+      JNIEnv* env, jclass cls, jstring filename, jint method_index) {
+  ScopedUtfChars filename_chars(env, filename);
+  CHECK(filename_chars.c_str() != nullptr);
+  ScopedObjectAccess soa(Thread::Current());
+  const DexFile* dex_file = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetDexFile();
+  return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
+                                     dex_file,
+                                     static_cast<uint16_t>(method_index));
+}
+
+}  // namespace
+}  // namespace art
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
new file mode 100644
index 0000000..f12fac9
--- /dev/null
+++ b/test/595-profile-saving/run
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Use
+# --compiler-filter=interpret-only to make sure that the test is not compiled AOT
+# -XOatFileManagerCompilerFilter:interpret-only  to make sure the test is not compiled
+#   when loaded (by PathClassLoader)
+# -Xjitsaveprofilinginfo to enable profile saving
+# -Xusejit:false to disable jit and only test profiles.
+exec ${RUN} \
+  -Xcompiler-option --compiler-filter=interpret-only \
+  --runtime-option -XOatFileManagerCompilerFilter:interpret-only \
+  --runtime-option -Xjitsaveprofilinginfo \
+  --runtime-option -Xusejit:false \
+  "${@}"
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
new file mode 100644
index 0000000..039503f
--- /dev/null
+++ b/test/595-profile-saving/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+public class Main {
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+
+    File file = null;
+    try {
+      file = createTempFile();
+      // String codePath = getDexBaseLocation();
+      String codePath = System.getenv("DEX_LOCATION") + "/595-profile-saving.jar";
+      VMRuntime.registerAppInfo(file.getPath(),
+                                System.getenv("DEX_LOCATION"),
+                                new String[] {codePath},
+                                /* foreignProfileDir */ null);
+
+      int methodIdx = $opt$noinline$testProfile();
+      ensureProfileProcessing();
+      if (!presentInProfile(file.getPath(), methodIdx)) {
+        throw new RuntimeException("Method with index " + methodIdx + " not in the profile");
+      }
+    } finally {
+      if (file != null) {
+        file.delete();
+      }
+    }
+  }
+
+  public static int $opt$noinline$testProfile() {
+    if (doThrow) throw new Error();
+    // Make sure we have a profile info for this method without the need to loop.
+    return ensureProfilingInfo("$opt$noinline$testProfile");
+  }
+
+  // Return the dex method index.
+  public static native int ensureProfilingInfo(String methodName);
+  // Ensures the profile saver does its usual processing.
+  public static native void ensureProfileProcessing();
+  // Checks if the profiles saver knows about the method.
+  public static native boolean presentInProfile(String profile, int methodIdx);
+
+  public static boolean doThrow = false;
+  private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+  private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+  static native String getProfileInfoDump(
+      String filename);
+
+  private static File createTempFile() throws Exception {
+    try {
+      return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+    } catch (IOException e) {
+      System.setProperty("java.io.tmpdir", "/data/local/tmp");
+      try {
+        return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+      } catch (IOException e2) {
+        System.setProperty("java.io.tmpdir", "/sdcard");
+        return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+      }
+    }
+  }
+
+  private static class VMRuntime {
+    private static final Method registerAppInfoMethod;
+    static {
+      try {
+        Class<? extends Object> c = Class.forName("dalvik.system.VMRuntime");
+        registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+            String.class, String.class, String[].class, String.class);
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    public static void registerAppInfo(String profile, String appDir,
+                                       String[] codePaths, String foreignDir) throws Exception {
+      registerAppInfoMethod.invoke(null, profile, appDir, codePaths, foreignDir);
+    }
+  }
+}
diff --git a/test/803-no-super/expected.txt b/test/803-no-super/expected.txt
new file mode 100644
index 0000000..5036991
--- /dev/null
+++ b/test/803-no-super/expected.txt
@@ -0,0 +1,2 @@
+java.lang.ClassNotFoundException: NoSuper1
+Done!
diff --git a/test/803-no-super/info.txt b/test/803-no-super/info.txt
new file mode 100644
index 0000000..0178a44
--- /dev/null
+++ b/test/803-no-super/info.txt
@@ -0,0 +1,3 @@
+Regression test that temp (erroneous) classes don't get conflict tables created.
+
+Obviously needs to run under Dalvik or ART.
diff --git a/test/803-no-super/smali/nosuper1.smali b/test/803-no-super/smali/nosuper1.smali
new file mode 100644
index 0000000..df2eaa5
--- /dev/null
+++ b/test/803-no-super/smali/nosuper1.smali
@@ -0,0 +1,3 @@
+.class public LNoSuper1;
+
+.super LNoClass;
diff --git a/test/803-no-super/src/Main.java b/test/803-no-super/src/Main.java
new file mode 100644
index 0000000..a07e042
--- /dev/null
+++ b/test/803-no-super/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Attempt to load class with no superclass.
+ */
+public class Main {
+    public static void main(String[] args) throws Exception {
+        try {
+            Class<?> c = Class.forName("NoSuper1");
+        } catch (Exception e) {
+            System.out.println(e);
+        }
+        System.out.println("Done!");
+    }
+}
diff --git a/test/960-default-smali/expected.txt b/test/960-default-smali/expected.txt
index 7671eed..f3db93f 100644
--- a/test/960-default-smali/expected.txt
+++ b/test/960-default-smali/expected.txt
@@ -82,3 +82,19 @@
 J-interface   Greeter.SayHiTwice()='Hi Hi '
 J-virtual           J.SayHiTwice()='Hi Hi '
 End testing for type J
+Testing for type K
+K-interface       Foo.bar()='foobar'
+K-virtual           K.bar()='foobar'
+End testing for type K
+Testing for type L
+L-interface       Foo.bar()='foobar'
+L-virtual           K.bar()='foobar'
+L-virtual           L.bar()='foobar'
+End testing for type L
+Testing for type M
+M-interface       Foo.bar()='BAZ!'
+M-interface     Fooer.bar()='BAZ!'
+M-virtual           K.bar()='BAZ!'
+M-virtual           L.bar()='BAZ!'
+M-virtual           M.bar()='BAZ!'
+End testing for type M
diff --git a/test/960-default-smali/src/Foo.java b/test/960-default-smali/src/Foo.java
new file mode 100644
index 0000000..ed5b35f
--- /dev/null
+++ b/test/960-default-smali/src/Foo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+interface Foo {
+  public default String bar() {
+    return "foobar";
+  }
+}
diff --git a/test/960-default-smali/src/Fooer.java b/test/960-default-smali/src/Fooer.java
new file mode 100644
index 0000000..d8a5f61
--- /dev/null
+++ b/test/960-default-smali/src/Fooer.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Fooer extends Foo {
+  public String bar();
+}
diff --git a/test/960-default-smali/src/K.java b/test/960-default-smali/src/K.java
new file mode 100644
index 0000000..4426be7
--- /dev/null
+++ b/test/960-default-smali/src/K.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class K implements Foo { }
diff --git a/test/960-default-smali/src/L.java b/test/960-default-smali/src/L.java
new file mode 100644
index 0000000..c08ab72
--- /dev/null
+++ b/test/960-default-smali/src/L.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class L extends K { }
diff --git a/test/960-default-smali/src/M.java b/test/960-default-smali/src/M.java
new file mode 100644
index 0000000..affe7e9
--- /dev/null
+++ b/test/960-default-smali/src/M.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class M extends L implements Fooer {
+  public String bar() {
+    return "BAZ!";
+  }
+}
diff --git a/test/960-default-smali/src/classes.xml b/test/960-default-smali/src/classes.xml
index 0aa41f7..f3e50c5 100644
--- a/test/960-default-smali/src/classes.xml
+++ b/test/960-default-smali/src/classes.xml
@@ -81,6 +81,27 @@
       <implements> </implements>
       <methods> </methods>
     </class>
+
+    <class name="K" super="java/lang/Object">
+      <implements>
+        <item>Foo</item>
+      </implements>
+      <methods> </methods>
+    </class>
+
+    <class name="L" super="K">
+      <implements> </implements>
+      <methods> </methods>
+    </class>
+
+    <class name="M" super="L">
+      <implements>
+        <item>Fooer</item>
+      </implements>
+      <methods>
+        <method>bar</method>
+      </methods>
+    </class>
   </classes>
 
   <interfaces>
@@ -123,5 +144,22 @@
         <method type="abstract">GetPlace</method>
       </methods>
     </interface>
+
+    <interface name="Foo" super="java/lang/Object">
+      <implements>
+      </implements>
+      <methods>
+        <method type="default">bar</method>
+      </methods>
+    </interface>
+
+    <interface name="Fooer" super="java/lang/Object">
+      <implements>
+        <item>Foo</item>
+      </implements>
+      <methods>
+        <method type="abstract">bar</method>
+      </methods>
+    </interface>
   </interfaces>
 </data>
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index e547c72..464da2e 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -41,7 +41,8 @@
   497-inlining-and-class-loader/clear_dex_cache.cc \
   543-env-long-ref/env_long_ref.cc \
   566-polymorphic-inlining/polymorphic_inline.cc \
-  570-checker-osr/osr.cc
+  570-checker-osr/osr.cc \
+  595-profile-saving/profile-saving.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 2eb52bc..304c2a9 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -46,9 +46,14 @@
 done
 
 if [[ $mode == "host" ]]; then
-  make_command="make $j_arg $showcommands build-art-host-tests $common_targets ${out_dir}/host/linux-x86/lib/libjavacoretests.so ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
+  make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
+  make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "
+  make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
 elif [[ $mode == "target" ]]; then
-  make_command="make $j_arg $showcommands build-art-target-tests $common_targets libjavacrypto libjavacoretests linker toybox toolbox sh ${out_dir}/host/linux-x86/bin/adb libstdc++"
+  make_command="make $j_arg $showcommands build-art-target-tests $common_targets"
+  make_command+=" libjavacrypto libjavacoretests linker toybox toolbox sh"
+  make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ "
+  make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt"
 fi
 
 echo "Executing $make_command"
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 38b6ea6..f25fb98 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -243,48 +243,6 @@
           "org.apache.harmony.tests.java.util.prefs.FilePreferencesImplTest#testPutGet"]
 },
 {
-  description: "libnativehelper_compat_libc++ loading issue",
-  result: EXEC_FAILED,
-  modes: [device],
-  names: ["dalvik.system.JniTest#testGetSuperclass",
-          "dalvik.system.JniTest#testPassingBooleans",
-          "dalvik.system.JniTest#testPassingBytes",
-          "dalvik.system.JniTest#testPassingChars",
-          "dalvik.system.JniTest#testPassingClass",
-          "dalvik.system.JniTest#testPassingDoubles",
-          "dalvik.system.JniTest#testPassingFloats",
-          "dalvik.system.JniTest#testPassingInts",
-          "dalvik.system.JniTest#testPassingLongs",
-          "dalvik.system.JniTest#testPassingObjectReferences",
-          "dalvik.system.JniTest#testPassingShorts",
-          "dalvik.system.JniTest#testPassingThis",
-          "libcore.util.NativeAllocationRegistryTest#testBadSize",
-          "libcore.util.NativeAllocationRegistryTest#testEarlyFree",
-          "libcore.util.NativeAllocationRegistryTest#testNativeAllocationAllocatorAndNoSharedRegistry",
-          "libcore.util.NativeAllocationRegistryTest#testNativeAllocationAllocatorAndSharedRegistry",
-          "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndNoSharedRegistry",
-          "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndSharedRegistry",
-          "libcore.util.NativeAllocationRegistryTest#testNullArguments"]
-},
-{
-  description: "libnativehelper_compat_libc++.so not found by dlopen on ARM64",
-  result: EXEC_FAILED,
-  modes: [device],
-  bug: 28082914,
-  names: ["libcore.java.lang.ThreadTest#testContextClassLoaderIsInherited",
-          "libcore.java.lang.ThreadTest#testContextClassLoaderIsNotNull",
-          "libcore.java.lang.ThreadTest#testGetAllStackTracesIncludesAllGroups",
-          "libcore.java.lang.ThreadTest#testGetStackTrace",
-          "libcore.java.lang.ThreadTest#testJavaContextClassLoader",
-          "libcore.java.lang.ThreadTest#testLeakingStartedThreads",
-          "libcore.java.lang.ThreadTest#testLeakingUnstartedThreads",
-          "libcore.java.lang.ThreadTest#testNativeThreadNames",
-          "libcore.java.lang.ThreadTest#testThreadInterrupted",
-          "libcore.java.lang.ThreadTest#testThreadSleep",
-          "libcore.java.lang.ThreadTest#testThreadSleepIllegalArguments",
-          "libcore.java.lang.ThreadTest#testThreadWakeup"]
-},
-{
   description: "Only work with --mode=activity",
   result: EXEC_FAILED,
   names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ]
diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt
new file mode 100644
index 0000000..4b01796
--- /dev/null
+++ b/tools/public.libraries.buildbot.txt
@@ -0,0 +1,8 @@
+libart.so
+libartd.so
+libbacktrace.so
+libc.so
+libc++.so
+libdl.so
+libm.so
+libnativehelper.so