Merge "Revert "Revert "Add an option to disable native stack dumping on SIGQUIT."""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index e171289..8681642 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -170,6 +170,7 @@
 ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex MainUncompressed MultiDexUncompressed
 ART_GTEST_oat_test_DEX_DEPS := Main
+ART_GTEST_oat_writer_test_DEX_DEPS := Main
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
 ART_GTEST_patchoat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_proxy_test_DEX_DEPS := Interfaces
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index 60dfdce..95ab123 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -28,6 +28,7 @@
 
 #include "base/file_utils.h"
 #include "base/logging.h"
+#include "base/mutex.h"
 #include "base/stringpiece.h"
 #include "noop_compiler_callbacks.h"
 #include "runtime.h"
@@ -303,6 +304,7 @@
 template <typename Args = CmdlineArgs>
 struct CmdlineMain {
   int Main(int argc, char** argv) {
+    Locks::Init();
     InitLogging(argv, Runtime::Abort);
     std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
     args_ = args.get();
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 3cb9731..235a2aa 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -20,12 +20,13 @@
 
 #include "gtest/gtest.h"
 
+#include "base/mutex.h"
+#include "base/utils.h"
 #include "jdwp_provider.h"
 #include "experimental_flags.h"
 #include "parsed_options.h"
 #include "runtime.h"
 #include "runtime_options.h"
-#include "utils.h"
 
 #define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
                                         reinterpret_cast<void*>(nullptr));
@@ -126,6 +127,7 @@
   using RuntimeParser = ParsedOptions::RuntimeParser;
 
   static void SetUpTestCase() {
+    art::Locks::Init();
     art::InitLogging(nullptr, art::Runtime::Abort);  // argv = null
   }
 
diff --git a/cmdline/cmdline_result.h b/cmdline/cmdline_result.h
index e41043a..0ae1145 100644
--- a/cmdline/cmdline_result.h
+++ b/cmdline/cmdline_result.h
@@ -18,7 +18,7 @@
 #define ART_CMDLINE_CMDLINE_RESULT_H_
 
 #include <assert.h>
-#include <utils.h>
+#include "base/utils.h"
 
 namespace art {
 // Result of an attempt to process the command line arguments. If fails, specifies
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 40c676c..c4d538f 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -256,7 +256,14 @@
         instrumentation: true,
         profile_file: "art/dex2oat.profdata",
         benchmarks: ["dex2oat"],
-    }
+    },
+    target: {
+        android: {
+            lto: {
+                 thin: true,
+            },
+        },
+    },
 }
 
 art_cc_library {
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index a203133..d3e3a51 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -21,6 +21,7 @@
 #include "art_method-inl.h"
 #include "base/callee_save_type.h"
 #include "base/enums.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "compiled_method-inl.h"
 #include "dex/descriptors_names.h"
@@ -36,7 +37,6 @@
 #include "oat_quick_method_header.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index 7c7ae71..646040f 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -19,10 +19,10 @@
 #include <android-base/logging.h>
 
 #include "base/macros.h"
+#include "base/utils.h"
 #include "dex/code_item_accessors-inl.h"
 #include "driver/compiler_driver.h"
 #include "optimizing/optimizing_compiler.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/compiler.h b/compiler/compiler.h
index a17e2b5..f2ec3a9 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -18,8 +18,8 @@
 #define ART_COMPILER_COMPILER_H_
 
 #include "base/mutex.h"
+#include "base/os.h"
 #include "dex/dex_file.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index 5405759..9a7c604 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -26,12 +26,12 @@
 #include <set>
 #include <string>
 
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
 #include "gtest/gtest.h"
 #include "linker/elf_builder.h"
 #include "linker/file_output_stream.h"
-#include "os.h"
 
 namespace art {
 namespace dwarf {
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index 1310e8d..7a8e291 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -20,12 +20,12 @@
 #include <map>
 #include <unordered_set>
 
+#include "base/utils.h"
 #include "debug/debug_info.h"
 #include "debug/method_debug_info.h"
 #include "dex/dex_file-inl.h"
 #include "dex/code_item_accessors.h"
 #include "linker/elf_builder.h"
-#include "utils.h"
 
 namespace art {
 namespace debug {
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc
index 48477ab..a26a985 100644
--- a/compiler/driver/compiled_method_storage.cc
+++ b/compiler/driver/compiled_method_storage.cc
@@ -21,10 +21,10 @@
 
 #include <android-base/logging.h>
 
+#include "base/utils.h"
 #include "compiled_method.h"
 #include "linker/linker_patch.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 #include "utils/dedupe_set-inl.h"
 #include "utils/swap_space.h"
 
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index a3bb4ec..8db892b 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -29,6 +29,8 @@
 #include "base/array_ref.h"
 #include "base/bit_utils.h"
 #include "base/mutex.h"
+#include "base/os.h"
+#include "base/quasi_atomic.h"
 #include "base/safe_map.h"
 #include "base/timing_logger.h"
 #include "class_reference.h"
@@ -39,7 +41,6 @@
 #include "dex/dex_to_dex_compiler.h"
 #include "driver/compiled_method_storage.h"
 #include "method_reference.h"
-#include "os.h"
 #include "thread_pool.h"
 #include "utils/atomic_dex_ref_map.h"
 #include "utils/dex_cache_arrays_layout.h"
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 18b0913..05d8805 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -22,10 +22,10 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/utils.h"
 #include "compiler_filter.h"
 #include "globals.h"
 #include "optimizing/register_allocator.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 2e315b5..c90c37d 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -16,10 +16,10 @@
 
 #include "dex_compilation_unit.h"
 
+#include "base/utils.h"
 #include "dex/code_item_accessors-inl.h"
 #include "dex/descriptors_names.h"
 #include "mirror/dex_cache.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index fc44927..d001cfe 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -27,6 +27,7 @@
 #include "base/enums.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
+#include "base/utils.h"
 #include "calling_convention.h"
 #include "class_linker.h"
 #include "debug/dwarf/debug_frame_opcode_writer.h"
@@ -37,7 +38,6 @@
 #include "jni_env_ext.h"
 #include "memory_region.h"
 #include "thread.h"
-#include "utils.h"
 #include "utils/arm/managed_register_arm.h"
 #include "utils/arm64/managed_register_arm64.h"
 #include "utils/assembler.h"
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 52a0796..b268204 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -60,13 +60,12 @@
     case LinkerPatch::Type::kCallRelative:
     case LinkerPatch::Type::kBakerReadBarrierBranch:
       return false;
+    case LinkerPatch::Type::kDataBimgRelRo:
     case LinkerPatch::Type::kMethodRelative:
     case LinkerPatch::Type::kMethodBssEntry:
     case LinkerPatch::Type::kTypeRelative:
-    case LinkerPatch::Type::kTypeClassTable:
     case LinkerPatch::Type::kTypeBssEntry:
     case LinkerPatch::Type::kStringRelative:
-    case LinkerPatch::Type::kStringInternTable:
     case LinkerPatch::Type::kStringBssEntry:
       return patch.LiteralOffset() == patch.PcInsnOffset();
   }
@@ -271,10 +270,9 @@
       shift = 0u;  // No shift for ADD.
     } else {
       // LDR/STR 32-bit or 64-bit with imm12 == 0 (unset).
-      DCHECK(patch.GetType() == LinkerPatch::Type::kMethodBssEntry ||
-             patch.GetType() == LinkerPatch::Type::kTypeClassTable ||
+      DCHECK(patch.GetType() == LinkerPatch::Type::kDataBimgRelRo ||
+             patch.GetType() == LinkerPatch::Type::kMethodBssEntry ||
              patch.GetType() == LinkerPatch::Type::kTypeBssEntry ||
-             patch.GetType() == LinkerPatch::Type::kStringInternTable ||
              patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType();
       DCHECK_EQ(insn & 0xbfbffc00, 0xb9000000) << std::hex << insn;
     }
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
index a5f6099..3da7a43 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -529,6 +529,8 @@
         stream_(output),
         rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
         text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0),
+        data_bimg_rel_ro_(
+            this, ".data.bimg.rel.ro", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
         bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
         dex_(this, ".dex", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
         dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize),
@@ -552,6 +554,7 @@
         loaded_size_(0u),
         virtual_address_(0) {
     text_.phdr_flags_ = PF_R | PF_X;
+    data_bimg_rel_ro_.phdr_flags_ = PF_R | PF_W;  // Shall be made read-only at run time.
     bss_.phdr_flags_ = PF_R | PF_W;
     dex_.phdr_flags_ = PF_R;
     dynamic_.phdr_flags_ = PF_R | PF_W;
@@ -566,6 +569,7 @@
   BuildIdSection* GetBuildId() { return &build_id_; }
   Section* GetRoData() { return &rodata_; }
   Section* GetText() { return &text_; }
+  Section* GetDataBimgRelRo() { return &data_bimg_rel_ro_; }
   Section* GetBss() { return &bss_; }
   Section* GetDex() { return &dex_; }
   StringSection* GetStrTab() { return &strtab_; }
@@ -694,6 +698,7 @@
   void PrepareDynamicSection(const std::string& elf_file_path,
                              Elf_Word rodata_size,
                              Elf_Word text_size,
+                             Elf_Word data_bimg_rel_ro_size,
                              Elf_Word bss_size,
                              Elf_Word bss_methods_offset,
                              Elf_Word bss_roots_offset,
@@ -707,6 +712,9 @@
     // Allocate all pre-dynamic sections.
     rodata_.AllocateVirtualMemory(rodata_size);
     text_.AllocateVirtualMemory(text_size);
+    if (data_bimg_rel_ro_size != 0) {
+      data_bimg_rel_ro_.AllocateVirtualMemory(data_bimg_rel_ro_size);
+    }
     if (bss_size != 0) {
       bss_.AllocateVirtualMemory(bss_size);
     }
@@ -735,6 +743,24 @@
       Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4;
       dynsym_.Add(oatlastword, &rodata_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
     }
+    if (data_bimg_rel_ro_size != 0u) {
+      Elf_Word oatdatabimgrelro = dynstr_.Add("oatdatabimgrelro");
+      dynsym_.Add(oatdatabimgrelro,
+                  &data_bimg_rel_ro_,
+                  data_bimg_rel_ro_.GetAddress(),
+                  data_bimg_rel_ro_size,
+                  STB_GLOBAL,
+                  STT_OBJECT);
+      Elf_Word oatdatabimgrelrolastword = dynstr_.Add("oatdatabimgrelrolastword");
+      Elf_Word oatdatabimgrelrolastword_address =
+          data_bimg_rel_ro_.GetAddress() + data_bimg_rel_ro_size - 4;
+      dynsym_.Add(oatdatabimgrelrolastword,
+                  &data_bimg_rel_ro_,
+                  oatdatabimgrelrolastword_address,
+                  4,
+                  STB_GLOBAL,
+                  STT_OBJECT);
+    }
     DCHECK_LE(bss_roots_offset, bss_size);
     if (bss_size != 0u) {
       Elf_Word oatbss = dynstr_.Add("oatbss");
@@ -1010,6 +1036,7 @@
 
   Section rodata_;
   Section text_;
+  Section data_bimg_rel_ro_;
   Section bss_;
   Section dex_;
   CachedStringSection dynstr_;
diff --git a/compiler/linker/file_output_stream.h b/compiler/linker/file_output_stream.h
index 28296a4..deb051f 100644
--- a/compiler/linker/file_output_stream.h
+++ b/compiler/linker/file_output_stream.h
@@ -17,9 +17,9 @@
 #ifndef ART_COMPILER_LINKER_FILE_OUTPUT_STREAM_H_
 #define ART_COMPILER_LINKER_FILE_OUTPUT_STREAM_H_
 
-#include "output_stream.h"
+#include "base/os.h"
 
-#include "os.h"
+#include "output_stream.h"
 
 namespace art {
 namespace linker {
diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h
index 6f4e774..36051d2 100644
--- a/compiler/linker/linker_patch.h
+++ b/compiler/linker/linker_patch.h
@@ -41,19 +41,27 @@
   // choose to squeeze the Type into fewer than 8 bits, we'll have to declare
   // patch_type_ as an uintN_t and do explicit static_cast<>s.
   enum class Type : uint8_t {
+    kDataBimgRelRo,           // NOTE: Actual patching is instruction_set-dependent.
     kMethodRelative,          // NOTE: Actual patching is instruction_set-dependent.
     kMethodBssEntry,          // NOTE: Actual patching is instruction_set-dependent.
     kCall,
     kCallRelative,            // NOTE: Actual patching is instruction_set-dependent.
     kTypeRelative,            // NOTE: Actual patching is instruction_set-dependent.
-    kTypeClassTable,          // NOTE: Actual patching is instruction_set-dependent.
     kTypeBssEntry,            // NOTE: Actual patching is instruction_set-dependent.
     kStringRelative,          // NOTE: Actual patching is instruction_set-dependent.
-    kStringInternTable,       // NOTE: Actual patching is instruction_set-dependent.
     kStringBssEntry,          // NOTE: Actual patching is instruction_set-dependent.
     kBakerReadBarrierBranch,  // NOTE: Actual patching is instruction_set-dependent.
   };
 
+  static LinkerPatch DataBimgRelRoPatch(size_t literal_offset,
+                                        uint32_t pc_insn_offset,
+                                        uint32_t boot_image_offset) {
+    LinkerPatch patch(literal_offset, Type::kDataBimgRelRo, /* target_dex_file */ nullptr);
+    patch.boot_image_offset_ = boot_image_offset;
+    patch.pc_insn_offset_ = pc_insn_offset;
+    return patch;
+  }
+
   static LinkerPatch RelativeMethodPatch(size_t literal_offset,
                                          const DexFile* target_dex_file,
                                          uint32_t pc_insn_offset,
@@ -100,16 +108,6 @@
     return patch;
   }
 
-  static LinkerPatch TypeClassTablePatch(size_t literal_offset,
-                                         const DexFile* target_dex_file,
-                                         uint32_t pc_insn_offset,
-                                         uint32_t target_type_idx) {
-    LinkerPatch patch(literal_offset, Type::kTypeClassTable, target_dex_file);
-    patch.type_idx_ = target_type_idx;
-    patch.pc_insn_offset_ = pc_insn_offset;
-    return patch;
-  }
-
   static LinkerPatch TypeBssEntryPatch(size_t literal_offset,
                                        const DexFile* target_dex_file,
                                        uint32_t pc_insn_offset,
@@ -130,16 +128,6 @@
     return patch;
   }
 
-  static LinkerPatch StringInternTablePatch(size_t literal_offset,
-                                            const DexFile* target_dex_file,
-                                            uint32_t pc_insn_offset,
-                                            uint32_t target_string_idx) {
-    LinkerPatch patch(literal_offset, Type::kStringInternTable, target_dex_file);
-    patch.string_idx_ = target_string_idx;
-    patch.pc_insn_offset_ = pc_insn_offset;
-    return patch;
-  }
-
   static LinkerPatch StringBssEntryPatch(size_t literal_offset,
                                          const DexFile* target_dex_file,
                                          uint32_t pc_insn_offset,
@@ -172,14 +160,13 @@
 
   bool IsPcRelative() const {
     switch (GetType()) {
+      case Type::kDataBimgRelRo:
       case Type::kMethodRelative:
       case Type::kMethodBssEntry:
       case Type::kCallRelative:
       case Type::kTypeRelative:
-      case Type::kTypeClassTable:
       case Type::kTypeBssEntry:
       case Type::kStringRelative:
-      case Type::kStringInternTable:
       case Type::kStringBssEntry:
       case Type::kBakerReadBarrierBranch:
         return true;
@@ -188,6 +175,11 @@
     }
   }
 
+  uint32_t BootImageOffset() const {
+    DCHECK(patch_type_ == Type::kDataBimgRelRo);
+    return boot_image_offset_;
+  }
+
   MethodReference TargetMethod() const {
     DCHECK(patch_type_ == Type::kMethodRelative ||
            patch_type_ == Type::kMethodBssEntry ||
@@ -198,40 +190,35 @@
 
   const DexFile* TargetTypeDexFile() const {
     DCHECK(patch_type_ == Type::kTypeRelative ||
-           patch_type_ == Type::kTypeClassTable ||
            patch_type_ == Type::kTypeBssEntry);
     return target_dex_file_;
   }
 
   dex::TypeIndex TargetTypeIndex() const {
     DCHECK(patch_type_ == Type::kTypeRelative ||
-           patch_type_ == Type::kTypeClassTable ||
            patch_type_ == Type::kTypeBssEntry);
     return dex::TypeIndex(type_idx_);
   }
 
   const DexFile* TargetStringDexFile() const {
     DCHECK(patch_type_ == Type::kStringRelative ||
-           patch_type_ == Type::kStringInternTable ||
            patch_type_ == Type::kStringBssEntry);
     return target_dex_file_;
   }
 
   dex::StringIndex TargetStringIndex() const {
     DCHECK(patch_type_ == Type::kStringRelative ||
-           patch_type_ == Type::kStringInternTable ||
            patch_type_ == Type::kStringBssEntry);
     return dex::StringIndex(string_idx_);
   }
 
   uint32_t PcInsnOffset() const {
-    DCHECK(patch_type_ == Type::kMethodRelative ||
+    DCHECK(patch_type_ == Type::kDataBimgRelRo ||
+           patch_type_ == Type::kMethodRelative ||
            patch_type_ == Type::kMethodBssEntry ||
            patch_type_ == Type::kTypeRelative ||
-           patch_type_ == Type::kTypeClassTable ||
            patch_type_ == Type::kTypeBssEntry ||
            patch_type_ == Type::kStringRelative ||
-           patch_type_ == Type::kStringInternTable ||
            patch_type_ == Type::kStringBssEntry);
     return pc_insn_offset_;
   }
@@ -263,10 +250,11 @@
   uint32_t literal_offset_ : 24;  // Method code size up to 16MiB.
   Type patch_type_ : 8;
   union {
-    uint32_t cmp1_;             // Used for relational operators.
-    uint32_t method_idx_;       // Method index for Call/Method patches.
-    uint32_t type_idx_;         // Type index for Type patches.
-    uint32_t string_idx_;       // String index for String patches.
+    uint32_t cmp1_;               // Used for relational operators.
+    uint32_t boot_image_offset_;  // Data to write to the .data.bimg.rel.ro entry.
+    uint32_t method_idx_;         // Method index for Call/Method patches.
+    uint32_t type_idx_;           // Type index for Type patches.
+    uint32_t string_idx_;         // String index for String patches.
     uint32_t baker_custom_value1_;
     static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators");
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index ff59173..0fcc9c6 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -51,6 +51,8 @@
 #include "dex/verified_method.h"
 #include "driver/compiler_driver.h"
 #include "graph_visualizer.h"
+#include "image.h"
+#include "gc/space/image_space.h"
 #include "intern_table.h"
 #include "intrinsics.h"
 #include "mirror/array-inl.h"
@@ -722,6 +724,47 @@
   }
 }
 
+static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) {
+  Runtime* runtime = Runtime::Current();
+  DCHECK(runtime->IsAotCompiler());
+  const std::vector<gc::space::ImageSpace*>& boot_image_spaces =
+      runtime->GetHeap()->GetBootImageSpaces();
+  // Check that the `object` is in the expected section of one of the boot image files.
+  DCHECK(std::any_of(boot_image_spaces.begin(),
+                     boot_image_spaces.end(),
+                     [object, section](gc::space::ImageSpace* space) {
+                       uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin());
+                       uintptr_t offset = reinterpret_cast<uintptr_t>(object) - begin;
+                       return space->GetImageHeader().GetImageSection(section).Contains(offset);
+                     }));
+  uintptr_t begin = reinterpret_cast<uintptr_t>(boot_image_spaces.front()->Begin());
+  uintptr_t offset = reinterpret_cast<uintptr_t>(object) - begin;
+  return dchecked_integral_cast<uint32_t>(offset);
+}
+
+// NO_THREAD_SAFETY_ANALYSIS: Avoid taking the mutator lock, boot image classes are non-moveable.
+uint32_t CodeGenerator::GetBootImageOffset(HLoadClass* load_class) NO_THREAD_SAFETY_ANALYSIS {
+  DCHECK_EQ(load_class->GetLoadKind(), HLoadClass::LoadKind::kBootImageRelRo);
+  ObjPtr<mirror::Class> klass = load_class->GetClass().Get();
+  DCHECK(klass != nullptr);
+  return GetBootImageOffsetImpl(klass.Ptr(), ImageHeader::kSectionObjects);
+}
+
+// NO_THREAD_SAFETY_ANALYSIS: Avoid taking the mutator lock, boot image strings are non-moveable.
+uint32_t CodeGenerator::GetBootImageOffset(HLoadString* load_string) NO_THREAD_SAFETY_ANALYSIS {
+  DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kBootImageRelRo);
+  ObjPtr<mirror::String> string = load_string->GetString().Get();
+  DCHECK(string != nullptr);
+  return GetBootImageOffsetImpl(string.Ptr(), ImageHeader::kSectionObjects);
+}
+
+uint32_t CodeGenerator::GetBootImageOffset(HInvokeStaticOrDirect* invoke) {
+  DCHECK_EQ(invoke->GetMethodLoadKind(), HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo);
+  ArtMethod* method = invoke->GetResolvedMethod();
+  DCHECK(method != nullptr);
+  return GetBootImageOffsetImpl(method, ImageHeader::kSectionArtMethods);
+}
+
 void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const {
   // The DCHECKS below check that a register is not specified twice in
   // the summary. The out location can overlap with an input, so we need
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 60de722..7031483 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -556,6 +556,10 @@
                                                         Location runtime_return_location);
   void GenerateLoadClassRuntimeCall(HLoadClass* cls);
 
+  uint32_t GetBootImageOffset(HLoadClass* load_class);
+  uint32_t GetBootImageOffset(HLoadString* load_string);
+  uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke);
+
   static void CreateSystemArrayCopyLocationSummary(HInvoke* invoke);
 
   void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; }
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 1f9c554..a024df8 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -78,6 +78,7 @@
 using helpers::OutputRegister;
 using helpers::QRegisterFrom;
 using helpers::RegisterFrom;
+using helpers::SRegisterFrom;
 using helpers::StackOperandFrom;
 using helpers::VIXLRegCodeFromART;
 using helpers::WRegisterFrom;
@@ -4459,12 +4460,23 @@
       // Load method address from literal pool.
       __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress()));
       break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
+      // Add ADRP with its PC-relative .data.bimg.rel.ro patch.
+      uint32_t boot_image_offset = GetBootImageOffset(invoke);
+      vixl::aarch64::Label* adrp_label = NewBootImageRelRoPatch(boot_image_offset);
+      EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp));
+      // Add LDR with its PC-relative .data.bimg.rel.ro patch.
+      vixl::aarch64::Label* ldr_label = NewBootImageRelRoPatch(boot_image_offset, adrp_label);
+      // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load.
+      EmitLdrOffsetPlaceholder(ldr_label, WRegisterFrom(temp), XRegisterFrom(temp));
+      break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
-      // Add ADRP with its PC-relative DexCache access patch.
+      // Add ADRP with its PC-relative .bss entry patch.
       MethodReference target_method(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex());
       vixl::aarch64::Label* adrp_label = NewMethodBssEntryPatch(target_method);
       EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp));
-      // Add LDR with its PC-relative DexCache access patch.
+      // Add LDR with its PC-relative .bss entry patch.
       vixl::aarch64::Label* ldr_label =
           NewMethodBssEntryPatch(target_method, adrp_label);
       EmitLdrOffsetPlaceholder(ldr_label, XRegisterFrom(temp), XRegisterFrom(temp));
@@ -4559,6 +4571,13 @@
   codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
 }
 
+vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch(
+    uint32_t boot_image_offset,
+    vixl::aarch64::Label* adrp_label) {
+  return NewPcRelativePatch(
+      /* dex_file */ nullptr, boot_image_offset, adrp_label, &boot_image_method_patches_);
+}
+
 vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageMethodPatch(
     MethodReference target_method,
     vixl::aarch64::Label* adrp_label) {
@@ -4681,6 +4700,14 @@
   }
 }
 
+linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
+                                              const DexFile* target_dex_file,
+                                              uint32_t pc_insn_offset,
+                                              uint32_t boot_image_offset) {
+  DCHECK(target_dex_file == nullptr);  // Unused for DataBimgRelRoPatch(), should be null.
+  return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
+}
+
 void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -4700,11 +4727,10 @@
     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
         boot_image_string_patches_, linker_patches);
   } else {
-    DCHECK(boot_image_method_patches_.empty());
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
-        boot_image_type_patches_, linker_patches);
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
-        boot_image_string_patches_, linker_patches);
+    EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
+        boot_image_method_patches_, linker_patches);
+    DCHECK(boot_image_type_patches_.empty());
+    DCHECK(boot_image_string_patches_.empty());
   }
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
       method_bss_entry_patches_, linker_patches);
@@ -4779,7 +4805,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -4888,23 +4914,16 @@
       __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
-    case HLoadClass::LoadKind::kBootImageClassTable: {
+    case HLoadClass::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
-      // Add ADRP with its PC-relative type patch.
-      const DexFile& dex_file = cls->GetDexFile();
-      dex::TypeIndex type_index = cls->GetTypeIndex();
-      vixl::aarch64::Label* adrp_label = codegen_->NewBootImageTypePatch(dex_file, type_index);
+      uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls);
+      // Add ADRP with its PC-relative .data.bimg.rel.ro patch.
+      vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset);
       codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
-      // Add LDR with its PC-relative type patch.
+      // Add LDR with its PC-relative .data.bimg.rel.ro patch.
       vixl::aarch64::Label* ldr_label =
-          codegen_->NewBootImageTypePatch(dex_file, type_index, adrp_label);
+          codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label);
       codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X());
-      // Extract the reference from the slot data, i.e. clear the hash bits.
-      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
-          ComputeModifiedUtf8Hash(dex_file.StringByTypeIdx(type_index)));
-      if (masked_hash != 0) {
-        __ Sub(out.W(), out.W(), Operand(masked_hash));
-      }
       break;
     }
     case HLoadClass::LoadKind::kBssEntry: {
@@ -4914,7 +4933,7 @@
       vixl::aarch64::Register temp = XRegisterFrom(out_loc);
       vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
       codegen_->EmitAdrpPlaceholder(adrp_label, temp);
-      // Add LDR with its PC-relative Class patch.
+      // Add LDR with its PC-relative Class .bss entry patch.
       vixl::aarch64::Label* ldr_label =
           codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label);
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)  /* PC-relative */
@@ -4989,7 +5008,7 @@
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -5055,16 +5074,15 @@
       __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
       return;
     }
-    case HLoadString::LoadKind::kBootImageInternTable: {
+    case HLoadString::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
-      // Add ADRP with its PC-relative String patch.
-      const DexFile& dex_file = load->GetDexFile();
-      const dex::StringIndex string_index = load->GetStringIndex();
-      vixl::aarch64::Label* adrp_label = codegen_->NewBootImageStringPatch(dex_file, string_index);
+      // Add ADRP with its PC-relative .data.bimg.rel.ro patch.
+      uint32_t boot_image_offset = codegen_->GetBootImageOffset(load);
+      vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset);
       codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
-      // Add LDR with its PC-relative String patch.
+      // Add LDR with its PC-relative .data.bimg.rel.ro patch.
       vixl::aarch64::Label* ldr_label =
-          codegen_->NewBootImageStringPatch(dex_file, string_index, adrp_label);
+          codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label);
       codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X());
       return;
     }
@@ -5076,7 +5094,7 @@
       Register temp = XRegisterFrom(out_loc);
       vixl::aarch64::Label* adrp_label = codegen_->NewStringBssEntryPatch(dex_file, string_index);
       codegen_->EmitAdrpPlaceholder(adrp_label, temp);
-      // Add LDR with its .bss entry String patch.
+      // Add LDR with its PC-relative String .bss entry patch.
       vixl::aarch64::Label* ldr_label =
           codegen_->NewStringBssEntryPatch(dex_file, string_index, adrp_label);
       // /* GcRoot<mirror::String> */ out = *(base_address + offset)  /* PC-relative */
@@ -5462,6 +5480,113 @@
   }
 }
 
+// TODO: integrate with HandleBinaryOp?
+static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
+  LocationSummary* locations = new (allocator) LocationSummary(minmax);
+  switch (minmax->GetResultType()) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARM64::GenerateMinMaxInt(LocationSummary* locations,
+                                                      bool is_min,
+                                                      DataType::Type type) {
+  Location op1 = locations->InAt(0);
+  Location op2 = locations->InAt(1);
+  Location out = locations->Out();
+
+  Register op1_reg;
+  Register op2_reg;
+  Register out_reg;
+  if (type == DataType::Type::kInt64) {
+    op1_reg = XRegisterFrom(op1);
+    op2_reg = XRegisterFrom(op2);
+    out_reg = XRegisterFrom(out);
+  } else {
+    DCHECK_EQ(type, DataType::Type::kInt32);
+    op1_reg = WRegisterFrom(op1);
+    op2_reg = WRegisterFrom(op2);
+    out_reg = WRegisterFrom(out);
+  }
+
+  __ Cmp(op1_reg, op2_reg);
+  __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
+}
+
+void InstructionCodeGeneratorARM64::GenerateMinMaxFP(LocationSummary* locations,
+                                                     bool is_min,
+                                                     DataType::Type type) {
+  Location op1 = locations->InAt(0);
+  Location op2 = locations->InAt(1);
+  Location out = locations->Out();
+
+  FPRegister op1_reg;
+  FPRegister op2_reg;
+  FPRegister out_reg;
+  if (type == DataType::Type::kFloat64) {
+    op1_reg = DRegisterFrom(op1);
+    op2_reg = DRegisterFrom(op2);
+    out_reg = DRegisterFrom(out);
+  } else {
+    DCHECK_EQ(type, DataType::Type::kFloat32);
+    op1_reg = SRegisterFrom(op1);
+    op2_reg = SRegisterFrom(op2);
+    out_reg = SRegisterFrom(out);
+  }
+
+  if (is_min) {
+    __ Fmin(out_reg, op1_reg, op2_reg);
+  } else {
+    __ Fmax(out_reg, op1_reg, op2_reg);
+  }
+}
+
+// TODO: integrate with HandleBinaryOp?
+void InstructionCodeGeneratorARM64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
+  DataType::Type type = minmax->GetResultType();
+  switch (type) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      GenerateMinMaxInt(minmax->GetLocations(), is_min, type);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      GenerateMinMaxFP(minmax->GetLocations(), is_min, type);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << type;
+  }
+}
+
+void LocationsBuilderARM64::VisitMin(HMin* min) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+}
+
+void InstructionCodeGeneratorARM64::VisitMin(HMin* min) {
+  GenerateMinMax(min, /*is_min*/ true);
+}
+
+void LocationsBuilderARM64::VisitMax(HMax* max) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+}
+
+void InstructionCodeGeneratorARM64::VisitMax(HMax* max) {
+  GenerateMinMax(max, /*is_min*/ false);
+}
+
 void LocationsBuilderARM64::VisitAbs(HAbs* abs) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
   switch (abs->GetResultType()) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index e34f799..b59ccd9 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -273,6 +273,10 @@
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
   void HandleCondition(HCondition* instruction);
 
+  void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type);
+  void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type);
+  void GenerateMinMax(HBinaryOperation* minmax, bool is_min);
+
   // Generate a heap reference load using one register `out`:
   //
   //   out <- *(out + offset)
@@ -561,7 +565,14 @@
     UNIMPLEMENTED(FATAL);
   }
 
-  // Add a new PC-relative method patch for an instruction and return the label
+  // Add a new boot image relocation patch for an instruction and return the label
+  // to be bound before the instruction. The instruction will be either the
+  // ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label` pointing
+  // to the associated ADRP patch label).
+  vixl::aarch64::Label* NewBootImageRelRoPatch(uint32_t boot_image_offset,
+                                               vixl::aarch64::Label* adrp_label = nullptr);
+
+  // Add a new boot image method patch for an instruction and return the label
   // to be bound before the instruction. The instruction will be either the
   // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
   // to the associated ADRP patch label).
@@ -575,7 +586,7 @@
   vixl::aarch64::Label* NewMethodBssEntryPatch(MethodReference target_method,
                                                vixl::aarch64::Label* adrp_label = nullptr);
 
-  // Add a new PC-relative type patch for an instruction and return the label
+  // Add a new boot image type patch for an instruction and return the label
   // to be bound before the instruction. The instruction will be either the
   // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
   // to the associated ADRP patch label).
@@ -591,7 +602,7 @@
                                              dex::TypeIndex type_index,
                                              vixl::aarch64::Label* adrp_label = nullptr);
 
-  // Add a new PC-relative string patch for an instruction and return the label
+  // Add a new boot image string patch for an instruction and return the label
   // to be bound before the instruction. The instruction will be either the
   // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
   // to the associated ADRP patch label).
@@ -820,7 +831,8 @@
   Uint32ToLiteralMap uint32_literals_;
   // Deduplication map for 64-bit literals, used for non-patchable method address or method code.
   Uint64ToLiteralMap uint64_literals_;
-  // PC-relative method patch info for kBootImageLinkTimePcRelative.
+  // PC-relative method patch info for kBootImageLinkTimePcRelative/BootImageRelRo.
+  // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods).
   ArenaDeque<PcRelativePatchInfo> boot_image_method_patches_;
   // PC-relative method patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_;
@@ -828,7 +840,7 @@
   ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
-  // PC-relative String patch info; type depends on configuration (intern table or boot image PIC).
+  // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PcRelativePatchInfo> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> string_bss_entry_patches_;
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 13518ad..6ebcc67 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -4690,6 +4690,244 @@
   }
 }
 
+static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
+  LocationSummary* locations = new (allocator) LocationSummary(minmax);
+  switch (minmax->GetResultType()) {
+    case DataType::Type::kInt32:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    case DataType::Type::kInt64:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    case DataType::Type::kFloat32:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      locations->AddTemp(Location::RequiresRegister());
+      break;
+    case DataType::Type::kFloat64:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+  Location out_loc = locations->Out();
+
+  vixl32::Register op1 = RegisterFrom(op1_loc);
+  vixl32::Register op2 = RegisterFrom(op2_loc);
+  vixl32::Register out = RegisterFrom(out_loc);
+
+  __ Cmp(op1, op2);
+
+  {
+    ExactAssemblyScope aas(GetVIXLAssembler(),
+                           3 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
+
+    __ ite(is_min ? lt : gt);
+    __ mov(is_min ? lt : gt, out, op1);
+    __ mov(is_min ? ge : le, out, op2);
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+  Location out_loc = locations->Out();
+
+  // Optimization: don't generate any code if inputs are the same.
+  if (op1_loc.Equals(op2_loc)) {
+    DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in location builder.
+    return;
+  }
+
+  vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
+  vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
+  vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
+  vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
+  vixl32::Register out_lo = LowRegisterFrom(out_loc);
+  vixl32::Register out_hi = HighRegisterFrom(out_loc);
+  UseScratchRegisterScope temps(GetVIXLAssembler());
+  const vixl32::Register temp = temps.Acquire();
+
+  DCHECK(op1_lo.Is(out_lo));
+  DCHECK(op1_hi.Is(out_hi));
+
+  // Compare op1 >= op2, or op1 < op2.
+  __ Cmp(out_lo, op2_lo);
+  __ Sbcs(temp, out_hi, op2_hi);
+
+  // Now GE/LT condition code is correct for the long comparison.
+  {
+    vixl32::ConditionType cond = is_min ? ge : lt;
+    ExactAssemblyScope it_scope(GetVIXLAssembler(),
+                                3 * kMaxInstructionSizeInBytes,
+                                CodeBufferCheckScope::kMaximumSize);
+    __ itt(cond);
+    __ mov(cond, out_lo, op2_lo);
+    __ mov(cond, out_hi, op2_hi);
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) {
+  LocationSummary* locations = minmax->GetLocations();
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+  Location out_loc = locations->Out();
+
+  // Optimization: don't generate any code if inputs are the same.
+  if (op1_loc.Equals(op2_loc)) {
+    DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in location builder.
+    return;
+  }
+
+  vixl32::SRegister op1 = SRegisterFrom(op1_loc);
+  vixl32::SRegister op2 = SRegisterFrom(op2_loc);
+  vixl32::SRegister out = SRegisterFrom(out_loc);
+
+  UseScratchRegisterScope temps(GetVIXLAssembler());
+  const vixl32::Register temp1 = temps.Acquire();
+  vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0));
+  vixl32::Label nan, done;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
+
+  DCHECK(op1.Is(out));
+
+  __ Vcmp(op1, op2);
+  __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+  __ B(vs, &nan, /* far_target */ false);  // if un-ordered, go to NaN handling.
+
+  // op1 <> op2
+  vixl32::ConditionType cond = is_min ? gt : lt;
+  {
+    ExactAssemblyScope it_scope(GetVIXLAssembler(),
+                                2 * kMaxInstructionSizeInBytes,
+                                CodeBufferCheckScope::kMaximumSize);
+    __ it(cond);
+    __ vmov(cond, F32, out, op2);
+  }
+  // for <>(not equal), we've done min/max calculation.
+  __ B(ne, final_label, /* far_target */ false);
+
+  // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
+  __ Vmov(temp1, op1);
+  __ Vmov(temp2, op2);
+  if (is_min) {
+    __ Orr(temp1, temp1, temp2);
+  } else {
+    __ And(temp1, temp1, temp2);
+  }
+  __ Vmov(out, temp1);
+  __ B(final_label);
+
+  // handle NaN input.
+  __ Bind(&nan);
+  __ Movt(temp1, High16Bits(kNanFloat));  // 0x7FC0xxxx is a NaN.
+  __ Vmov(out, temp1);
+
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) {
+  LocationSummary* locations = minmax->GetLocations();
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+  Location out_loc = locations->Out();
+
+  // Optimization: don't generate any code if inputs are the same.
+  if (op1_loc.Equals(op2_loc)) {
+    DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in.
+    return;
+  }
+
+  vixl32::DRegister op1 = DRegisterFrom(op1_loc);
+  vixl32::DRegister op2 = DRegisterFrom(op2_loc);
+  vixl32::DRegister out = DRegisterFrom(out_loc);
+  vixl32::Label handle_nan_eq, done;
+  vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
+
+  DCHECK(op1.Is(out));
+
+  __ Vcmp(op1, op2);
+  __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+  __ B(vs, &handle_nan_eq, /* far_target */ false);  // if un-ordered, go to NaN handling.
+
+  // op1 <> op2
+  vixl32::ConditionType cond = is_min ? gt : lt;
+  {
+    ExactAssemblyScope it_scope(GetVIXLAssembler(),
+                                2 * kMaxInstructionSizeInBytes,
+                                CodeBufferCheckScope::kMaximumSize);
+    __ it(cond);
+    __ vmov(cond, F64, out, op2);
+  }
+  // for <>(not equal), we've done min/max calculation.
+  __ B(ne, final_label, /* far_target */ false);
+
+  // handle op1 == op2, max(+0.0,-0.0).
+  if (!is_min) {
+    __ Vand(F64, out, op1, op2);
+    __ B(final_label);
+  }
+
+  // handle op1 == op2, min(+0.0,-0.0), NaN input.
+  __ Bind(&handle_nan_eq);
+  __ Vorr(F64, out, op1, op2);  // assemble op1/-0.0/NaN.
+
+  if (done.IsReferenced()) {
+    __ Bind(&done);
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
+  DataType::Type type = minmax->GetResultType();
+  switch (type) {
+    case DataType::Type::kInt32:
+      GenerateMinMaxInt(minmax->GetLocations(), is_min);
+      break;
+    case DataType::Type::kInt64:
+      GenerateMinMaxLong(minmax->GetLocations(), is_min);
+      break;
+    case DataType::Type::kFloat32:
+      GenerateMinMaxFloat(minmax, is_min);
+      break;
+    case DataType::Type::kFloat64:
+      GenerateMinMaxDouble(minmax, is_min);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << type;
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitMin(HMin* min) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) {
+  GenerateMinMax(min, /*is_min*/ true);
+}
+
+void LocationsBuilderARMVIXL::VisitMax(HMax* max) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) {
+  GenerateMinMax(max, /*is_min*/ false);
+}
+
 void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
   switch (abs->GetResultType()) {
@@ -7088,7 +7326,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -7198,18 +7436,12 @@
       __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
-    case HLoadClass::LoadKind::kBootImageClassTable: {
+    case HLoadClass::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
-          codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+          codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(cls));
       codegen_->EmitMovwMovtPlaceholder(labels, out);
       __ Ldr(out, MemOperand(out, /* offset */ 0));
-      // Extract the reference from the slot data, i.e. clear the hash bits.
-      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
-          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
-      if (masked_hash != 0) {
-        __ Sub(out, out, Operand(masked_hash));
-      }
       break;
     }
     case HLoadClass::LoadKind::kBssEntry: {
@@ -7295,7 +7527,7 @@
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -7359,10 +7591,10 @@
       __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
       return;
     }
-    case HLoadString::LoadKind::kBootImageInternTable: {
+    case HLoadString::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
-          codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
+          codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(load));
       codegen_->EmitMovwMovtPlaceholder(labels, out);
       __ Ldr(out, MemOperand(out, /* offset */ 0));
       return;
@@ -8956,6 +9188,14 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
       break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
+      uint32_t boot_image_offset = GetBootImageOffset(invoke);
+      PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset);
+      vixl32::Register temp_reg = RegisterFrom(temp);
+      EmitMovwMovtPlaceholder(labels, temp_reg);
+      GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
+      break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
       PcRelativePatchInfo* labels = NewMethodBssEntryPatch(
           MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
@@ -9053,6 +9293,13 @@
   }
 }
 
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch(
+    uint32_t boot_image_offset) {
+  return NewPcRelativePatch(/* dex_file */ nullptr,
+                            boot_image_offset,
+                            &boot_image_method_patches_);
+}
+
 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch(
     MethodReference target_method) {
   return NewPcRelativePatch(
@@ -9143,6 +9390,14 @@
   }
 }
 
+linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
+                                              const DexFile* target_dex_file,
+                                              uint32_t pc_insn_offset,
+                                              uint32_t boot_image_offset) {
+  DCHECK(target_dex_file == nullptr);  // Unused for DataBimgRelRoPatch(), should be null.
+  return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
+}
+
 void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -9162,11 +9417,10 @@
     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
         boot_image_string_patches_, linker_patches);
   } else {
-    DCHECK(boot_image_method_patches_.empty());
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
-        boot_image_type_patches_, linker_patches);
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
-        boot_image_string_patches_, linker_patches);
+    EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
+        boot_image_method_patches_, linker_patches);
+    DCHECK(boot_image_type_patches_.empty());
+    DCHECK(boot_image_string_patches_.empty());
   }
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
       method_bss_entry_patches_, linker_patches);
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index bbc715c..2d8f6a6 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -349,6 +349,12 @@
                       bool value_can_be_null);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
 
+  void GenerateMinMaxInt(LocationSummary* locations, bool is_min);
+  void GenerateMinMaxLong(LocationSummary* locations, bool is_min);
+  void GenerateMinMaxFloat(HInstruction* minmax, bool is_min);
+  void GenerateMinMaxDouble(HInstruction* minmax, bool is_min);
+  void GenerateMinMax(HBinaryOperation* minmax, bool is_min);
+
   // Generate a heap reference load using one register `out`:
   //
   //   out <- *(out + offset)
@@ -574,6 +580,7 @@
     vixl::aarch32::Label add_pc_label;
   };
 
+  PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset);
   PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method);
   PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method);
   PcRelativePatchInfo* NewBootImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
@@ -798,7 +805,8 @@
 
   // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
   Uint32ToLiteralMap uint32_literals_;
-  // PC-relative method patch info for kBootImageLinkTimePcRelative.
+  // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo.
+  // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods).
   ArenaDeque<PcRelativePatchInfo> boot_image_method_patches_;
   // PC-relative method patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_;
@@ -806,7 +814,7 @@
   ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
-  // PC-relative String patch info; type depends on configuration (intern table or boot image PIC).
+  // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PcRelativePatchInfo> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> string_bss_entry_patches_;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index eb5f72e..be9ff48 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1597,6 +1597,14 @@
   }
 }
 
+linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
+                                              const DexFile* target_dex_file,
+                                              uint32_t pc_insn_offset,
+                                              uint32_t boot_image_offset) {
+  DCHECK(target_dex_file == nullptr);  // Unused for DataBimgRelRoPatch(), should be null.
+  return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
+}
+
 void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -1615,11 +1623,10 @@
     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
         boot_image_string_patches_, linker_patches);
   } else {
-    DCHECK(boot_image_method_patches_.empty());
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
-        boot_image_type_patches_, linker_patches);
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
-        boot_image_string_patches_, linker_patches);
+    EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
+        boot_image_method_patches_, linker_patches);
+    DCHECK(boot_image_type_patches_.empty());
+    DCHECK(boot_image_string_patches_.empty());
   }
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
       method_bss_entry_patches_, linker_patches);
@@ -1630,6 +1637,13 @@
   DCHECK_EQ(size, linker_patches->size());
 }
 
+CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageRelRoPatch(
+    uint32_t boot_image_offset,
+    const PcRelativePatchInfo* info_high) {
+  return NewPcRelativePatch(
+      /* dex_file */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_);
+}
+
 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageMethodPatch(
     MethodReference target_method,
     const PcRelativePatchInfo* info_high) {
@@ -7725,7 +7739,7 @@
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -7748,7 +7762,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -7835,6 +7849,15 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
       break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
+      uint32_t boot_image_offset = GetBootImageOffset(invoke);
+      PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset);
+      PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high);
+      Register temp_reg = temp.AsRegister<Register>();
+      EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg);
+      __ Lw(temp_reg, TMP, /* placeholder */ 0x5678, &info_low->label);
+      break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
       PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(
           MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
@@ -7956,7 +7979,7 @@
     // We need an extra register for PC-relative literals on R2.
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageAddress:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       if (isR6) {
         break;
@@ -8008,7 +8031,7 @@
     // We need an extra register for PC-relative literals on R2.
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadClass::LoadKind::kBootImageAddress:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       base_or_current_method_reg =
           (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>();
@@ -8065,22 +8088,17 @@
       }
       break;
     }
-    case HLoadClass::LoadKind::kBootImageClassTable: {
+    case HLoadClass::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls);
       CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
-          codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+          codegen_->NewBootImageRelRoPatch(boot_image_offset);
       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
-          codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
+          codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);
       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
                                                      out,
                                                      base_or_current_method_reg);
       __ Lw(out, out, /* placeholder */ 0x5678, &info_low->label);
-      // Extract the reference from the slot data, i.e. clear the hash bits.
-      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
-          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
-      if (masked_hash != 0) {
-        __ Addiu(out, out, -masked_hash);
-      }
       break;
     }
     case HLoadClass::LoadKind::kBssEntry: {
@@ -8171,7 +8189,7 @@
     // We need an extra register for PC-relative literals on R2.
     case HLoadString::LoadKind::kBootImageAddress:
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       if (isR6) {
         break;
@@ -8223,7 +8241,7 @@
     // We need an extra register for PC-relative literals on R2.
     case HLoadString::LoadKind::kBootImageAddress:
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       base_or_current_method_reg =
           (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>();
@@ -8259,12 +8277,13 @@
       }
       return;
     }
-    case HLoadString::LoadKind::kBootImageInternTable: {
+    case HLoadString::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      uint32_t boot_image_offset = codegen_->GetBootImageOffset(load);
       CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
-          codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
+          codegen_->NewBootImageRelRoPatch(boot_image_offset);
       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
-          codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
+          codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);
       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
                                                      out,
                                                      base_or_current_method_reg);
@@ -8779,6 +8798,390 @@
   }
 }
 
+static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
+  LocationSummary* locations = new (allocator) LocationSummary(minmax);
+  switch (minmax->GetResultType()) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateMinMaxInt(LocationSummary* locations,
+                                                     bool is_min,
+                                                     bool isR6,
+                                                     DataType::Type type) {
+  if (isR6) {
+    // Some architectures, such as ARM and MIPS (prior to r6), have a
+    // conditional move instruction which only changes the target
+    // (output) register if the condition is true (MIPS prior to r6 had
+    // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions
+    // always change the target (output) register.  If the condition is
+    // true the output register gets the contents of the "rs" register;
+    // otherwise, the output register is set to zero. One consequence
+    // of this is that to implement something like "rd = c==0 ? rs : rt"
+    // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions.
+    // After executing this pair of instructions one of the output
+    // registers from the pair will necessarily contain zero. Then the
+    // code ORs the output registers from the SELEQZ/SELNEZ instructions
+    // to get the final result.
+    //
+    // The initial test to see if the output register is same as the
+    // first input register is needed to make sure that value in the
+    // first input register isn't clobbered before we've finished
+    // computing the output value. The logic in the corresponding else
+    // clause performs the same task but makes sure the second input
+    // register isn't clobbered in the event that it's the same register
+    // as the output register; the else clause also handles the case
+    // where the output register is distinct from both the first, and the
+    // second input registers.
+    if (type == DataType::Type::kInt64) {
+      Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+      Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+      Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
+      Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
+      Register out_lo = locations->Out().AsRegisterPairLow<Register>();
+      Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+      MipsLabel compare_done;
+
+      if (a_lo == b_lo) {
+        if (out_lo != a_lo) {
+          __ Move(out_lo, a_lo);
+          __ Move(out_hi, a_hi);
+        }
+      } else {
+        __ Slt(TMP, b_hi, a_hi);
+        __ Bne(b_hi, a_hi, &compare_done);
+
+        __ Sltu(TMP, b_lo, a_lo);
+
+        __ Bind(&compare_done);
+
+        if (is_min) {
+          __ Seleqz(AT, a_lo, TMP);
+          __ Selnez(out_lo, b_lo, TMP);  // Safe even if out_lo == a_lo/b_lo
+                                         // because at this point we're
+                                         // done using a_lo/b_lo.
+        } else {
+          __ Selnez(AT, a_lo, TMP);
+          __ Seleqz(out_lo, b_lo, TMP);  // ditto
+        }
+        __ Or(out_lo, out_lo, AT);
+        if (is_min) {
+          __ Seleqz(AT, a_hi, TMP);
+          __ Selnez(out_hi, b_hi, TMP);  // ditto but for out_hi & a_hi/b_hi
+        } else {
+          __ Selnez(AT, a_hi, TMP);
+          __ Seleqz(out_hi, b_hi, TMP);  // ditto but for out_hi & a_hi/b_hi
+        }
+        __ Or(out_hi, out_hi, AT);
+      }
+    } else {
+      DCHECK_EQ(type, DataType::Type::kInt32);
+      Register a = locations->InAt(0).AsRegister<Register>();
+      Register b = locations->InAt(1).AsRegister<Register>();
+      Register out = locations->Out().AsRegister<Register>();
+
+      if (a == b) {
+        if (out != a) {
+          __ Move(out, a);
+        }
+      } else {
+        __ Slt(AT, b, a);
+        if (is_min) {
+          __ Seleqz(TMP, a, AT);
+          __ Selnez(AT, b, AT);
+        } else {
+          __ Selnez(TMP, a, AT);
+          __ Seleqz(AT, b, AT);
+        }
+        __ Or(out, TMP, AT);
+      }
+    }
+  } else {  // !isR6
+    if (type == DataType::Type::kInt64) {
+      Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+      Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+      Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
+      Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
+      Register out_lo = locations->Out().AsRegisterPairLow<Register>();
+      Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+      MipsLabel compare_done;
+
+      if (a_lo == b_lo) {
+        if (out_lo != a_lo) {
+          __ Move(out_lo, a_lo);
+          __ Move(out_hi, a_hi);
+        }
+      } else {
+        __ Slt(TMP, a_hi, b_hi);
+        __ Bne(a_hi, b_hi, &compare_done);
+
+        __ Sltu(TMP, a_lo, b_lo);
+
+        __ Bind(&compare_done);
+
+        if (is_min) {
+          if (out_lo != a_lo) {
+            __ Movn(out_hi, a_hi, TMP);
+            __ Movn(out_lo, a_lo, TMP);
+          }
+          if (out_lo != b_lo) {
+            __ Movz(out_hi, b_hi, TMP);
+            __ Movz(out_lo, b_lo, TMP);
+          }
+        } else {
+          if (out_lo != a_lo) {
+            __ Movz(out_hi, a_hi, TMP);
+            __ Movz(out_lo, a_lo, TMP);
+          }
+          if (out_lo != b_lo) {
+            __ Movn(out_hi, b_hi, TMP);
+            __ Movn(out_lo, b_lo, TMP);
+          }
+        }
+      }
+    } else {
+      DCHECK_EQ(type, DataType::Type::kInt32);
+      Register a = locations->InAt(0).AsRegister<Register>();
+      Register b = locations->InAt(1).AsRegister<Register>();
+      Register out = locations->Out().AsRegister<Register>();
+
+      if (a == b) {
+        if (out != a) {
+          __ Move(out, a);
+        }
+      } else {
+        __ Slt(AT, a, b);
+        if (is_min) {
+          if (out != a) {
+            __ Movn(out, a, AT);
+          }
+          if (out != b) {
+            __ Movz(out, b, AT);
+          }
+        } else {
+          if (out != a) {
+            __ Movz(out, a, AT);
+          }
+          if (out != b) {
+            __ Movn(out, b, AT);
+          }
+        }
+      }
+    }
+  }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateMinMaxFP(LocationSummary* locations,
+                                                    bool is_min,
+                                                    bool isR6,
+                                                    DataType::Type type) {
+  FRegister out = locations->Out().AsFpuRegister<FRegister>();
+  FRegister a = locations->InAt(0).AsFpuRegister<FRegister>();
+  FRegister b = locations->InAt(1).AsFpuRegister<FRegister>();
+
+  if (isR6) {
+    MipsLabel noNaNs;
+    MipsLabel done;
+    FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
+
+    // When Java computes min/max it prefers a NaN to a number; the
+    // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
+    // the inputs is a NaN and the other is a valid number, the MIPS
+    // instruction will return the number; Java wants the NaN value
+    // returned. This is why there is extra logic preceding the use of
+    // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
+    // NaN, return the NaN, otherwise return the min/max.
+    if (type == DataType::Type::kFloat64) {
+      __ CmpUnD(FTMP, a, b);
+      __ Bc1eqz(FTMP, &noNaNs);
+
+      // One of the inputs is a NaN
+      __ CmpEqD(ftmp, a, a);
+      // If a == a then b is the NaN, otherwise a is the NaN.
+      __ SelD(ftmp, a, b);
+
+      if (ftmp != out) {
+        __ MovD(out, ftmp);
+      }
+
+      __ B(&done);
+
+      __ Bind(&noNaNs);
+
+      if (is_min) {
+        __ MinD(out, a, b);
+      } else {
+        __ MaxD(out, a, b);
+      }
+    } else {
+      DCHECK_EQ(type, DataType::Type::kFloat32);
+      __ CmpUnS(FTMP, a, b);
+      __ Bc1eqz(FTMP, &noNaNs);
+
+      // One of the inputs is a NaN
+      __ CmpEqS(ftmp, a, a);
+      // If a == a then b is the NaN, otherwise a is the NaN.
+      __ SelS(ftmp, a, b);
+
+      if (ftmp != out) {
+        __ MovS(out, ftmp);
+      }
+
+      __ B(&done);
+
+      __ Bind(&noNaNs);
+
+      if (is_min) {
+        __ MinS(out, a, b);
+      } else {
+        __ MaxS(out, a, b);
+      }
+    }
+
+    __ Bind(&done);
+
+  } else {  // !isR6
+    MipsLabel ordered;
+    MipsLabel compare;
+    MipsLabel select;
+    MipsLabel done;
+
+    if (type == DataType::Type::kFloat64) {
+      __ CunD(a, b);
+    } else {
+      DCHECK_EQ(type, DataType::Type::kFloat32);
+      __ CunS(a, b);
+    }
+    __ Bc1f(&ordered);
+
+    // a or b (or both) is a NaN. Return one, which is a NaN.
+    if (type == DataType::Type::kFloat64) {
+      __ CeqD(b, b);
+    } else {
+      __ CeqS(b, b);
+    }
+    __ B(&select);
+
+    __ Bind(&ordered);
+
+    // Neither is a NaN.
+    // a == b? (-0.0 compares equal with +0.0)
+    // If equal, handle zeroes, else compare further.
+    if (type == DataType::Type::kFloat64) {
+      __ CeqD(a, b);
+    } else {
+      __ CeqS(a, b);
+    }
+    __ Bc1f(&compare);
+
+    // a == b either bit for bit or one is -0.0 and the other is +0.0.
+    if (type == DataType::Type::kFloat64) {
+      __ MoveFromFpuHigh(TMP, a);
+      __ MoveFromFpuHigh(AT, b);
+    } else {
+      __ Mfc1(TMP, a);
+      __ Mfc1(AT, b);
+    }
+
+    if (is_min) {
+      // -0.0 prevails over +0.0.
+      __ Or(TMP, TMP, AT);
+    } else {
+      // +0.0 prevails over -0.0.
+      __ And(TMP, TMP, AT);
+    }
+
+    if (type == DataType::Type::kFloat64) {
+      __ Mfc1(AT, a);
+      __ Mtc1(AT, out);
+      __ MoveToFpuHigh(TMP, out);
+    } else {
+      __ Mtc1(TMP, out);
+    }
+    __ B(&done);
+
+    __ Bind(&compare);
+
+    if (type == DataType::Type::kFloat64) {
+      if (is_min) {
+        // return (a <= b) ? a : b;
+        __ ColeD(a, b);
+      } else {
+        // return (a >= b) ? a : b;
+        __ ColeD(b, a);  // b <= a
+      }
+    } else {
+      if (is_min) {
+        // return (a <= b) ? a : b;
+        __ ColeS(a, b);
+      } else {
+        // return (a >= b) ? a : b;
+        __ ColeS(b, a);  // b <= a
+      }
+    }
+
+    __ Bind(&select);
+
+    if (type == DataType::Type::kFloat64) {
+      __ MovtD(out, a);
+      __ MovfD(out, b);
+    } else {
+      __ MovtS(out, a);
+      __ MovfS(out, b);
+    }
+
+    __ Bind(&done);
+  }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
+  bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+  DataType::Type type = minmax->GetResultType();
+  switch (type) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      GenerateMinMaxInt(minmax->GetLocations(), is_min, isR6, type);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      GenerateMinMaxFP(minmax->GetLocations(), is_min, isR6, type);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << type;
+  }
+}
+
+void LocationsBuilderMIPS::VisitMin(HMin* min) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+}
+
+void InstructionCodeGeneratorMIPS::VisitMin(HMin* min) {
+  GenerateMinMax(min, /*is_min*/ true);
+}
+
+void LocationsBuilderMIPS::VisitMax(HMax* max) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+}
+
+void InstructionCodeGeneratorMIPS::VisitMax(HMax* max) {
+  GenerateMinMax(max, /*is_min*/ false);
+}
+
 void LocationsBuilderMIPS::VisitAbs(HAbs* abs) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
   switch (abs->GetResultType()) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index d09ab7c..d906896 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -246,6 +246,9 @@
                       bool value_can_be_null);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
 
+  void GenerateMinMaxInt(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type);
+  void GenerateMinMaxFP(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type);
+  void GenerateMinMax(HBinaryOperation*, bool is_min);
   void GenerateAbsFP(LocationSummary* locations, DataType::Type type, bool isR2OrNewer, bool isR6);
 
   // Generate a heap reference load using one register `out`:
@@ -617,6 +620,8 @@
     DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo);
   };
 
+  PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset,
+                                              const PcRelativePatchInfo* info_high = nullptr);
   PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method,
                                                const PcRelativePatchInfo* info_high = nullptr);
   PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method,
@@ -691,7 +696,8 @@
 
   // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
   Uint32ToLiteralMap uint32_literals_;
-  // PC-relative method patch info for kBootImageLinkTimePcRelative.
+  // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo.
+  // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods).
   ArenaDeque<PcRelativePatchInfo> boot_image_method_patches_;
   // PC-relative method patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_;
@@ -699,7 +705,7 @@
   ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
-  // PC-relative String patch info; type depends on configuration (intern table or boot image PIC).
+  // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PcRelativePatchInfo> boot_image_string_patches_;
   // PC-relative String patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> string_bss_entry_patches_;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 9593eec..f8851b4 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1509,6 +1509,14 @@
   }
 }
 
+linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
+                                              const DexFile* target_dex_file,
+                                              uint32_t pc_insn_offset,
+                                              uint32_t boot_image_offset) {
+  DCHECK(target_dex_file == nullptr);  // Unused for DataBimgRelRoPatch(), should be null.
+  return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
+}
+
 void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -1527,11 +1535,10 @@
     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
         boot_image_string_patches_, linker_patches);
   } else {
-    DCHECK(boot_image_method_patches_.empty());
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
-        boot_image_type_patches_, linker_patches);
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
-        boot_image_string_patches_, linker_patches);
+    EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
+        boot_image_method_patches_, linker_patches);
+    DCHECK(boot_image_type_patches_.empty());
+    DCHECK(boot_image_string_patches_.empty());
   }
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
       method_bss_entry_patches_, linker_patches);
@@ -1542,6 +1549,13 @@
   DCHECK_EQ(size, linker_patches->size());
 }
 
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageRelRoPatch(
+    uint32_t boot_image_offset,
+    const PcRelativePatchInfo* info_high) {
+  return NewPcRelativePatch(
+      /* dex_file */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_);
+}
+
 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageMethodPatch(
     MethodReference target_method,
     const PcRelativePatchInfo* info_high) {
@@ -5839,7 +5853,7 @@
   bool fallback_load = false;
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -5866,7 +5880,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -5926,6 +5940,15 @@
                      kLoadDoubleword,
                      DeduplicateUint64Literal(invoke->GetMethodAddress()));
       break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
+      uint32_t boot_image_offset = GetBootImageOffset(invoke);
+      PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset);
+      PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high);
+      EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
+      // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load.
+      __ Lwu(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
+      break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
       PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(
           MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
@@ -6113,20 +6136,15 @@
                      codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
-    case HLoadClass::LoadKind::kBootImageClassTable: {
+    case HLoadClass::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls);
       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
-          codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+          codegen_->NewBootImageRelRoPatch(boot_image_offset);
       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
-          codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
+          codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);
       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
       __ Lwu(out, AT, /* placeholder */ 0x5678);
-      // Extract the reference from the slot data, i.e. clear the hash bits.
-      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
-          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
-      if (masked_hash != 0) {
-        __ Daddiu(out, out, -masked_hash);
-      }
       break;
     }
     case HLoadClass::LoadKind::kBssEntry: {
@@ -6248,12 +6266,13 @@
                      codegen_->DeduplicateBootImageAddressLiteral(address));
       return;
     }
-    case HLoadString::LoadKind::kBootImageInternTable: {
+    case HLoadString::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      uint32_t boot_image_offset = codegen_->GetBootImageOffset(load);
       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
-          codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
+          codegen_->NewBootImageRelRoPatch(boot_image_offset);
       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
-          codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
+          codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);
       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
       __ Lwu(out, AT, /* placeholder */ 0x5678);
       return;
@@ -6665,6 +6684,182 @@
   }
 }
 
+static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
+  LocationSummary* locations = new (allocator) LocationSummary(minmax);
+  switch (minmax->GetResultType()) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorMIPS64::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
+  GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
+  GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
+  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+
+  if (lhs == rhs) {
+    if (out != lhs) {
+      __ Move(out, lhs);
+    }
+  } else {
+    // Some architectures, such as ARM and MIPS (prior to r6), have a
+    // conditional move instruction which only changes the target
+    // (output) register if the condition is true (MIPS prior to r6 had
+    // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
+    // change the target (output) register.  If the condition is true the
+    // output register gets the contents of the "rs" register; otherwise,
+    // the output register is set to zero. One consequence of this is
+    // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
+    // needs to use a pair of SELEQZ/SELNEZ instructions.  After
+    // executing this pair of instructions one of the output registers
+    // from the pair will necessarily contain zero. Then the code ORs the
+    // output registers from the SELEQZ/SELNEZ instructions to get the
+    // final result.
+    //
+    // The initial test to see if the output register is same as the
+    // first input register is needed to make sure that value in the
+    // first input register isn't clobbered before we've finished
+    // computing the output value. The logic in the corresponding else
+    // clause performs the same task but makes sure the second input
+    // register isn't clobbered in the event that it's the same register
+    // as the output register; the else clause also handles the case
+    // where the output register is distinct from both the first, and the
+    // second input registers.
+    if (out == lhs) {
+      __ Slt(AT, rhs, lhs);
+      if (is_min) {
+        __ Seleqz(out, lhs, AT);
+        __ Selnez(AT, rhs, AT);
+      } else {
+        __ Selnez(out, lhs, AT);
+        __ Seleqz(AT, rhs, AT);
+      }
+    } else {
+      __ Slt(AT, lhs, rhs);
+      if (is_min) {
+        __ Seleqz(out, rhs, AT);
+        __ Selnez(AT, lhs, AT);
+      } else {
+        __ Selnez(out, rhs, AT);
+        __ Seleqz(AT, lhs, AT);
+      }
+    }
+    __ Or(out, out, AT);
+  }
+}
+
+void InstructionCodeGeneratorMIPS64::GenerateMinMaxFP(LocationSummary* locations,
+                                                      bool is_min,
+                                                      DataType::Type type) {
+  FpuRegister a = locations->InAt(0).AsFpuRegister<FpuRegister>();
+  FpuRegister b = locations->InAt(1).AsFpuRegister<FpuRegister>();
+  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
+
+  Mips64Label noNaNs;
+  Mips64Label done;
+  FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
+
+  // When Java computes min/max it prefers a NaN to a number; the
+  // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
+  // the inputs is a NaN and the other is a valid number, the MIPS
+  // instruction will return the number; Java wants the NaN value
+  // returned. This is why there is extra logic preceding the use of
+  // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
+  // NaN, return the NaN, otherwise return the min/max.
+  if (type == DataType::Type::kFloat64) {
+    __ CmpUnD(FTMP, a, b);
+    __ Bc1eqz(FTMP, &noNaNs);
+
+    // One of the inputs is a NaN
+    __ CmpEqD(ftmp, a, a);
+    // If a == a then b is the NaN, otherwise a is the NaN.
+    __ SelD(ftmp, a, b);
+
+    if (ftmp != out) {
+      __ MovD(out, ftmp);
+    }
+
+    __ Bc(&done);
+
+    __ Bind(&noNaNs);
+
+    if (is_min) {
+      __ MinD(out, a, b);
+    } else {
+      __ MaxD(out, a, b);
+    }
+  } else {
+    DCHECK_EQ(type, DataType::Type::kFloat32);
+    __ CmpUnS(FTMP, a, b);
+    __ Bc1eqz(FTMP, &noNaNs);
+
+    // One of the inputs is a NaN
+    __ CmpEqS(ftmp, a, a);
+    // If a == a then b is the NaN, otherwise a is the NaN.
+    __ SelS(ftmp, a, b);
+
+    if (ftmp != out) {
+      __ MovS(out, ftmp);
+    }
+
+    __ Bc(&done);
+
+    __ Bind(&noNaNs);
+
+    if (is_min) {
+      __ MinS(out, a, b);
+    } else {
+      __ MaxS(out, a, b);
+    }
+  }
+
+  __ Bind(&done);
+}
+
+void InstructionCodeGeneratorMIPS64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
+  DataType::Type type = minmax->GetResultType();
+  switch (type) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      GenerateMinMaxInt(minmax->GetLocations(), is_min);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      GenerateMinMaxFP(minmax->GetLocations(), is_min, type);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << type;
+  }
+}
+
+void LocationsBuilderMIPS64::VisitMin(HMin* min) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitMin(HMin* min) {
+  GenerateMinMax(min, /*is_min*/ true);
+}
+
+void LocationsBuilderMIPS64::VisitMax(HMax* max) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitMax(HMax* max) {
+  GenerateMinMax(max, /*is_min*/ false);
+}
+
 void LocationsBuilderMIPS64::VisitAbs(HAbs* abs) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
   switch (abs->GetResultType()) {
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index ddeb3eb..d1da1ce 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -242,6 +242,10 @@
                       bool value_can_be_null);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
 
+  void GenerateMinMaxInt(LocationSummary* locations, bool is_min);
+  void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type);
+  void GenerateMinMax(HBinaryOperation* minmax, bool is_min);
+
   // Generate a heap reference load using one register `out`:
   //
   //   out <- *(out + offset)
@@ -586,6 +590,8 @@
     DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo);
   };
 
+  PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset,
+                                              const PcRelativePatchInfo* info_high = nullptr);
   PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method,
                                                const PcRelativePatchInfo* info_high = nullptr);
   PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method,
@@ -655,7 +661,8 @@
   // Deduplication map for 64-bit literals, used for non-patchable method address or method code
   // address.
   Uint64ToLiteralMap uint64_literals_;
-  // PC-relative method patch info for kBootImageLinkTimePcRelative.
+  // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo.
+  // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods).
   ArenaDeque<PcRelativePatchInfo> boot_image_method_patches_;
   // PC-relative method patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_;
@@ -663,7 +670,7 @@
   ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
-  // PC-relative String patch info; type depends on configuration (intern table or boot image PIC).
+  // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PcRelativePatchInfo> boot_image_string_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> string_bss_entry_patches_;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 51b96be..4818084 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -51,6 +51,9 @@
 
 static constexpr int kFakeReturnRegister = Register(8);
 
+static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000);
+static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000);
+
 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
 #define __ down_cast<X86Assembler*>(codegen->GetAssembler())->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86PointerSize, x).Int32Value()
@@ -3802,6 +3805,211 @@
   }
 }
 
+static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
+  LocationSummary* locations = new (allocator) LocationSummary(minmax);
+  switch (minmax->GetResultType()) {
+    case DataType::Type::kInt32:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    case DataType::Type::kInt64:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      // Register to use to perform a long subtract to set cc.
+      locations->AddTemp(Location::RequiresRegister());
+      break;
+    case DataType::Type::kFloat32:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      locations->AddTemp(Location::RequiresRegister());
+      break;
+    case DataType::Type::kFloat64:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86::GenerateMinMaxInt(LocationSummary* locations,
+                                                    bool is_min,
+                                                    DataType::Type type) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+
+  // Shortcut for same input locations.
+  if (op1_loc.Equals(op2_loc)) {
+    // Can return immediately, as op1_loc == out_loc.
+    // Note: if we ever support separate registers, e.g., output into memory, we need to check for
+    //       a copy here.
+    DCHECK(locations->Out().Equals(op1_loc));
+    return;
+  }
+
+  if (type == DataType::Type::kInt64) {
+    // Need to perform a subtract to get the sign right.
+    // op1 is already in the same location as the output.
+    Location output = locations->Out();
+    Register output_lo = output.AsRegisterPairLow<Register>();
+    Register output_hi = output.AsRegisterPairHigh<Register>();
+
+    Register op2_lo = op2_loc.AsRegisterPairLow<Register>();
+    Register op2_hi = op2_loc.AsRegisterPairHigh<Register>();
+
+    // The comparison is performed by subtracting the second operand from
+    // the first operand and then setting the status flags in the same
+    // manner as the SUB instruction."
+    __ cmpl(output_lo, op2_lo);
+
+    // Now use a temp and the borrow to finish the subtraction of op2_hi.
+    Register temp = locations->GetTemp(0).AsRegister<Register>();
+    __ movl(temp, output_hi);
+    __ sbbl(temp, op2_hi);
+
+    // Now the condition code is correct.
+    Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess;
+    __ cmovl(cond, output_lo, op2_lo);
+    __ cmovl(cond, output_hi, op2_hi);
+  } else {
+    DCHECK_EQ(type, DataType::Type::kInt32);
+    Register out = locations->Out().AsRegister<Register>();
+    Register op2 = op2_loc.AsRegister<Register>();
+
+    //  (out := op1)
+    //  out <=? op2
+    //  if out is min jmp done
+    //  out := op2
+    // done:
+
+    __ cmpl(out, op2);
+    Condition cond = is_min ? Condition::kGreater : Condition::kLess;
+    __ cmovl(cond, out, op2);
+  }
+}
+
+void InstructionCodeGeneratorX86::GenerateMinMaxFP(LocationSummary* locations,
+                                                   bool is_min,
+                                                   DataType::Type type) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+  Location out_loc = locations->Out();
+  XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
+
+  // Shortcut for same input locations.
+  if (op1_loc.Equals(op2_loc)) {
+    DCHECK(out_loc.Equals(op1_loc));
+    return;
+  }
+
+  //  (out := op1)
+  //  out <=? op2
+  //  if Nan jmp Nan_label
+  //  if out is min jmp done
+  //  if op2 is min jmp op2_label
+  //  handle -0/+0
+  //  jmp done
+  // Nan_label:
+  //  out := NaN
+  // op2_label:
+  //  out := op2
+  // done:
+  //
+  // This removes one jmp, but needs to copy one input (op1) to out.
+  //
+  // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath?
+
+  XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
+
+  NearLabel nan, done, op2_label;
+  if (type == DataType::Type::kFloat64) {
+    __ ucomisd(out, op2);
+  } else {
+    DCHECK_EQ(type, DataType::Type::kFloat32);
+    __ ucomiss(out, op2);
+  }
+
+  __ j(Condition::kParityEven, &nan);
+
+  __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
+  __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
+
+  // Handle 0.0/-0.0.
+  if (is_min) {
+    if (type == DataType::Type::kFloat64) {
+      __ orpd(out, op2);
+    } else {
+      __ orps(out, op2);
+    }
+  } else {
+    if (type == DataType::Type::kFloat64) {
+      __ andpd(out, op2);
+    } else {
+      __ andps(out, op2);
+    }
+  }
+  __ jmp(&done);
+
+  // NaN handling.
+  __ Bind(&nan);
+  if (type == DataType::Type::kFloat64) {
+    // TODO: Use a constant from the constant table (requires extra input).
+    __ LoadLongConstant(out, kDoubleNaN);
+  } else {
+    Register constant = locations->GetTemp(0).AsRegister<Register>();
+    __ movl(constant, Immediate(kFloatNaN));
+    __ movd(out, constant);
+  }
+  __ jmp(&done);
+
+  // out := op2;
+  __ Bind(&op2_label);
+  if (type == DataType::Type::kFloat64) {
+    __ movsd(out, op2);
+  } else {
+    __ movss(out, op2);
+  }
+
+  // Done.
+  __ Bind(&done);
+}
+
+void InstructionCodeGeneratorX86::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
+  DataType::Type type = minmax->GetResultType();
+  switch (type) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      GenerateMinMaxInt(minmax->GetLocations(), is_min, type);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      GenerateMinMaxFP(minmax->GetLocations(), is_min, type);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << type;
+  }
+}
+
+void LocationsBuilderX86::VisitMin(HMin* min) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+}
+
+void InstructionCodeGeneratorX86::VisitMin(HMin* min) {
+  GenerateMinMax(min, /*is_min*/ true);
+}
+
+void LocationsBuilderX86::VisitMax(HMax* max) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+}
+
+void InstructionCodeGeneratorX86::VisitMax(HMax* max) {
+  GenerateMinMax(max, /*is_min*/ false);
+}
+
 void LocationsBuilderX86::VisitAbs(HAbs* abs) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
   switch (abs->GetResultType()) {
@@ -4624,6 +4832,15 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ movl(temp.AsRegister<Register>(), Immediate(invoke->GetMethodAddress()));
       break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
+      Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke,
+                                                                temp.AsRegister<Register>());
+      __ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset));
+      RecordBootImageRelRoPatch(
+          invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(),
+          GetBootImageOffset(invoke));
+      break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
       Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke,
                                                                 temp.AsRegister<Register>());
@@ -4685,6 +4902,13 @@
   RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
 }
 
+void CodeGeneratorX86::RecordBootImageRelRoPatch(HX86ComputeBaseMethodAddress* method_address,
+                                                 uint32_t boot_image_offset) {
+  boot_image_method_patches_.emplace_back(
+      method_address, /* target_dex_file */ nullptr, boot_image_offset);
+  __ Bind(&boot_image_method_patches_.back().label);
+}
+
 void CodeGeneratorX86::RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke) {
   DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
   HX86ComputeBaseMethodAddress* method_address =
@@ -4754,6 +4978,14 @@
   }
 }
 
+linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
+                                              const DexFile* target_dex_file,
+                                              uint32_t pc_insn_offset,
+                                              uint32_t boot_image_offset) {
+  DCHECK(target_dex_file == nullptr);  // Unused for DataBimgRelRoPatch(), should be null.
+  return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
+}
+
 void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -4772,11 +5004,10 @@
     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
         boot_image_string_patches_, linker_patches);
   } else {
-    DCHECK(boot_image_method_patches_.empty());
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
-        boot_image_type_patches_, linker_patches);
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
-        boot_image_string_patches_, linker_patches);
+    EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
+        boot_image_method_patches_, linker_patches);
+    DCHECK(boot_image_type_patches_.empty());
+    DCHECK(boot_image_string_patches_.empty());
   }
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
       method_bss_entry_patches_, linker_patches);
@@ -6145,7 +6376,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -6183,7 +6414,7 @@
 
   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
       load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
-      load_kind == HLoadClass::LoadKind::kBootImageClassTable ||
+      load_kind == HLoadClass::LoadKind::kBootImageRelRo ||
       load_kind == HLoadClass::LoadKind::kBssEntry) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -6259,17 +6490,12 @@
       __ movl(out, Immediate(address));
       break;
     }
-    case HLoadClass::LoadKind::kBootImageClassTable: {
+    case HLoadClass::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
       Register method_address = locations->InAt(0).AsRegister<Register>();
       __ movl(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
-      codegen_->RecordBootImageTypePatch(cls);
-      // Extract the reference from the slot data, i.e. clear the hash bits.
-      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
-          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
-      if (masked_hash != 0) {
-        __ subl(out, Immediate(masked_hash));
-      }
+      codegen_->RecordBootImageRelRoPatch(cls->InputAt(0)->AsX86ComputeBaseMethodAddress(),
+                                          codegen_->GetBootImageOffset(cls));
       break;
     }
     case HLoadClass::LoadKind::kBssEntry: {
@@ -6349,7 +6575,7 @@
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -6368,7 +6594,7 @@
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
   HLoadString::LoadKind load_kind = load->GetLoadKind();
   if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
-      load_kind == HLoadString::LoadKind::kBootImageInternTable ||
+      load_kind == HLoadString::LoadKind::kBootImageRelRo ||
       load_kind == HLoadString::LoadKind::kBssEntry) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -6422,11 +6648,12 @@
       __ movl(out, Immediate(address));
       return;
     }
-    case HLoadString::LoadKind::kBootImageInternTable: {
+    case HLoadString::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
       Register method_address = locations->InAt(0).AsRegister<Register>();
       __ movl(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
-      codegen_->RecordBootImageStringPatch(load);
+      codegen_->RecordBootImageRelRoPatch(load->InputAt(0)->AsX86ComputeBaseMethodAddress(),
+                                          codegen_->GetBootImageOffset(load));
       return;
     }
     case HLoadString::LoadKind::kBssEntry: {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 51e5bca..9c537a7 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -225,6 +225,9 @@
   void GenerateShlLong(const Location& loc, int shift);
   void GenerateShrLong(const Location& loc, int shift);
   void GenerateUShrLong(const Location& loc, int shift);
+  void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type);
+  void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type);
+  void GenerateMinMax(HBinaryOperation* minmax, bool is_min);
 
   void HandleFieldSet(HInstruction* instruction,
                       const FieldInfo& field_info,
@@ -414,6 +417,8 @@
   void GenerateVirtualCall(
       HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE;
 
+  void RecordBootImageRelRoPatch(HX86ComputeBaseMethodAddress* method_address,
+                                 uint32_t boot_image_offset);
   void RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke);
   void RecordMethodBssEntryPatch(HInvokeStaticOrDirect* invoke);
   void RecordBootImageTypePatch(HLoadClass* load_class);
@@ -631,17 +636,18 @@
   X86Assembler assembler_;
   const X86InstructionSetFeatures& isa_features_;
 
-  // PC-relative method patch info for kBootImageLinkTimePcRelative.
+  // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo.
+  // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods).
   ArenaDeque<X86PcRelativePatchInfo> boot_image_method_patches_;
   // PC-relative method patch info for kBssEntry.
   ArenaDeque<X86PcRelativePatchInfo> method_bss_entry_patches_;
   // PC-relative type patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<X86PcRelativePatchInfo> boot_image_type_patches_;
-  // Type patch locations for kBssEntry.
+  // PC-relative type patch info for kBssEntry.
   ArenaDeque<X86PcRelativePatchInfo> type_bss_entry_patches_;
-  // String patch locations; type depends on configuration (intern table or boot image PIC).
+  // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<X86PcRelativePatchInfo> boot_image_string_patches_;
-  // String patch locations for kBssEntry.
+  // PC-relative String patch info for kBssEntry.
   ArenaDeque<X86PcRelativePatchInfo> string_bss_entry_patches_;
 
   // Patches for string root accesses in JIT compiled code.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 0bb56a2..c378c5b 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -998,6 +998,13 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       Load64BitValue(temp.AsRegister<CpuRegister>(), invoke->GetMethodAddress());
       break;
+    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
+      // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load.
+      __ movl(temp.AsRegister<CpuRegister>(),
+              Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
+      RecordBootImageRelRoPatch(GetBootImageOffset(invoke));
+      break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
       __ movq(temp.AsRegister<CpuRegister>(),
               Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
@@ -1059,6 +1066,11 @@
   RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
 }
 
+void CodeGeneratorX86_64::RecordBootImageRelRoPatch(uint32_t boot_image_offset) {
+  boot_image_method_patches_.emplace_back(/* target_dex_file */ nullptr, boot_image_offset);
+  __ Bind(&boot_image_method_patches_.back().label);
+}
+
 void CodeGeneratorX86_64::RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke) {
   boot_image_method_patches_.emplace_back(
       invoke->GetTargetMethod().dex_file, invoke->GetTargetMethod().index);
@@ -1110,6 +1122,14 @@
   }
 }
 
+linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
+                                              const DexFile* target_dex_file,
+                                              uint32_t pc_insn_offset,
+                                              uint32_t boot_image_offset) {
+  DCHECK(target_dex_file == nullptr);  // Unused for DataBimgRelRoPatch(), should be null.
+  return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
+}
+
 void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
@@ -1128,11 +1148,10 @@
     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
         boot_image_string_patches_, linker_patches);
   } else {
-    DCHECK(boot_image_method_patches_.empty());
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
-        boot_image_type_patches_, linker_patches);
-    EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
-        boot_image_string_patches_, linker_patches);
+    EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
+        boot_image_method_patches_, linker_patches);
+    DCHECK(boot_image_type_patches_.empty());
+    DCHECK(boot_image_string_patches_.empty());
   }
   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
       method_bss_entry_patches_, linker_patches);
@@ -3821,6 +3840,177 @@
   }
 }
 
+static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
+  LocationSummary* locations = new (allocator) LocationSummary(minmax);
+  switch (minmax->GetResultType()) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      // The following is sub-optimal, but all we can do for now. It would be fine to also accept
+      // the second input to be the output (we can simply swap inputs).
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorX86_64::GenerateMinMaxInt(LocationSummary* locations,
+                                                       bool is_min,
+                                                       DataType::Type type) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+
+  // Shortcut for same input locations.
+  if (op1_loc.Equals(op2_loc)) {
+    // Can return immediately, as op1_loc == out_loc.
+    // Note: if we ever support separate registers, e.g., output into memory, we need to check for
+    //       a copy here.
+    DCHECK(locations->Out().Equals(op1_loc));
+    return;
+  }
+
+  CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+  CpuRegister op2 = op2_loc.AsRegister<CpuRegister>();
+
+  //  (out := op1)
+  //  out <=? op2
+  //  if out is min jmp done
+  //  out := op2
+  // done:
+
+  if (type == DataType::Type::kInt64) {
+    __ cmpq(out, op2);
+    __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, /*is64bit*/ true);
+  } else {
+    DCHECK_EQ(type, DataType::Type::kInt32);
+    __ cmpl(out, op2);
+    __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, /*is64bit*/ false);
+  }
+}
+
+void InstructionCodeGeneratorX86_64::GenerateMinMaxFP(LocationSummary* locations,
+                                                      bool is_min,
+                                                      DataType::Type type) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+  Location out_loc = locations->Out();
+  XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
+
+  // Shortcut for same input locations.
+  if (op1_loc.Equals(op2_loc)) {
+    DCHECK(out_loc.Equals(op1_loc));
+    return;
+  }
+
+  //  (out := op1)
+  //  out <=? op2
+  //  if Nan jmp Nan_label
+  //  if out is min jmp done
+  //  if op2 is min jmp op2_label
+  //  handle -0/+0
+  //  jmp done
+  // Nan_label:
+  //  out := NaN
+  // op2_label:
+  //  out := op2
+  // done:
+  //
+  // This removes one jmp, but needs to copy one input (op1) to out.
+  //
+  // TODO: This is straight from Quick. Make NaN an out-of-line slowpath?
+
+  XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
+
+  NearLabel nan, done, op2_label;
+  if (type == DataType::Type::kFloat64) {
+    __ ucomisd(out, op2);
+  } else {
+    DCHECK_EQ(type, DataType::Type::kFloat32);
+    __ ucomiss(out, op2);
+  }
+
+  __ j(Condition::kParityEven, &nan);
+
+  __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
+  __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
+
+  // Handle 0.0/-0.0.
+  if (is_min) {
+    if (type == DataType::Type::kFloat64) {
+      __ orpd(out, op2);
+    } else {
+      __ orps(out, op2);
+    }
+  } else {
+    if (type == DataType::Type::kFloat64) {
+      __ andpd(out, op2);
+    } else {
+      __ andps(out, op2);
+    }
+  }
+  __ jmp(&done);
+
+  // NaN handling.
+  __ Bind(&nan);
+  if (type == DataType::Type::kFloat64) {
+    __ movsd(out, codegen_->LiteralInt64Address(INT64_C(0x7FF8000000000000)));
+  } else {
+    __ movss(out, codegen_->LiteralInt32Address(INT32_C(0x7FC00000)));
+  }
+  __ jmp(&done);
+
+  // out := op2;
+  __ Bind(&op2_label);
+  if (type == DataType::Type::kFloat64) {
+    __ movsd(out, op2);
+  } else {
+    __ movss(out, op2);
+  }
+
+  // Done.
+  __ Bind(&done);
+}
+
+void InstructionCodeGeneratorX86_64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
+  DataType::Type type = minmax->GetResultType();
+  switch (type) {
+    case DataType::Type::kInt32:
+    case DataType::Type::kInt64:
+      GenerateMinMaxInt(minmax->GetLocations(), is_min, type);
+      break;
+    case DataType::Type::kFloat32:
+    case DataType::Type::kFloat64:
+      GenerateMinMaxFP(minmax->GetLocations(), is_min, type);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type for HMinMax " << type;
+  }
+}
+
+void LocationsBuilderX86_64::VisitMin(HMin* min) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+}
+
+void InstructionCodeGeneratorX86_64::VisitMin(HMin* min) {
+  GenerateMinMax(min, /*is_min*/ true);
+}
+
+void LocationsBuilderX86_64::VisitMax(HMax* max) {
+  CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+}
+
+void InstructionCodeGeneratorX86_64::VisitMax(HMax* max) {
+  GenerateMinMax(max, /*is_min*/ false);
+}
+
 void LocationsBuilderX86_64::VisitAbs(HAbs* abs) {
   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
   switch (abs->GetResultType()) {
@@ -5535,7 +5725,7 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadClass::LoadKind::kBootImageClassTable:
+    case HLoadClass::LoadKind::kBootImageRelRo:
     case HLoadClass::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -5643,16 +5833,10 @@
       __ movl(out, Immediate(static_cast<int32_t>(address)));  // Zero-extended.
       break;
     }
-    case HLoadClass::LoadKind::kBootImageClassTable: {
+    case HLoadClass::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
       __ movl(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
-      codegen_->RecordBootImageTypePatch(cls);
-      // Extract the reference from the slot data, i.e. clear the hash bits.
-      int32_t masked_hash = ClassTable::TableSlot::MaskHash(
-          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
-      if (masked_hash != 0) {
-        __ subl(out, Immediate(masked_hash));
-      }
+      codegen_->RecordBootImageRelRoPatch(codegen_->GetBootImageOffset(cls));
       break;
     }
     case HLoadClass::LoadKind::kBssEntry: {
@@ -5717,7 +5901,7 @@
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBootImageInternTable:
+    case HLoadString::LoadKind::kBootImageRelRo:
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
@@ -5783,10 +5967,10 @@
       __ movl(out, Immediate(static_cast<int32_t>(address)));  // Zero-extended.
       return;
     }
-    case HLoadString::LoadKind::kBootImageInternTable: {
+    case HLoadString::LoadKind::kBootImageRelRo: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
       __ movl(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
-      codegen_->RecordBootImageStringPatch(load);
+      codegen_->RecordBootImageRelRoPatch(codegen_->GetBootImageOffset(load));
       return;
     }
     case HLoadString::LoadKind::kBssEntry: {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 1079e94..e8d1efe 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -222,6 +222,10 @@
                       bool value_can_be_null);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
 
+  void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type);
+  void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type);
+  void GenerateMinMax(HBinaryOperation* minmax, bool is_min);
+
   // Generate a heap reference load using one register `out`:
   //
   //   out <- *(out + offset)
@@ -410,6 +414,7 @@
   void GenerateVirtualCall(
       HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE;
 
+  void RecordBootImageRelRoPatch(uint32_t boot_image_offset);
   void RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke);
   void RecordMethodBssEntryPatch(HInvokeStaticOrDirect* invoke);
   void RecordBootImageTypePatch(HLoadClass* load_class);
@@ -604,17 +609,18 @@
   // Used for fixups to the constant area.
   int constant_area_start_;
 
-  // PC-relative method patch info for kBootImageLinkTimePcRelative.
+  // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo.
+  // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods).
   ArenaDeque<PatchInfo<Label>> boot_image_method_patches_;
   // PC-relative method patch info for kBssEntry.
   ArenaDeque<PatchInfo<Label>> method_bss_entry_patches_;
   // PC-relative type patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PatchInfo<Label>> boot_image_type_patches_;
-  // Type patch locations for kBssEntry.
+  // PC-relative type patch info for kBssEntry.
   ArenaDeque<PatchInfo<Label>> type_bss_entry_patches_;
-  // String patch locations; type depends on configuration (intern table or boot image PIC).
+  // PC-relative String patch info for kBootImageLinkTimePcRelative.
   ArenaDeque<PatchInfo<Label>> boot_image_string_patches_;
-  // String patch locations for kBssEntry.
+  // PC-relative String patch info for kBssEntry.
   ArenaDeque<PatchInfo<Label>> string_bss_entry_patches_;
 
   // Patches for string literals in JIT compiled code.
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index ba4040a..a0fd5ff 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -18,6 +18,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/utils.h"
 #include "builder.h"
 #include "codegen_test_utils.h"
 #include "dex/dex_file.h"
@@ -26,7 +27,6 @@
 #include "nodes.h"
 #include "optimizing_unit_test.h"
 #include "register_allocator_linear_scan.h"
-#include "utils.h"
 #include "utils/arm/assembler_arm_vixl.h"
 #include "utils/arm/managed_register_arm.h"
 #include "utils/mips/managed_register_mips.h"
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 71c394e..f05159b 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -17,11 +17,11 @@
 #include "gvn.h"
 
 #include "base/arena_bit_vector.h"
+#include "base/bit_vector-inl.h"
 #include "base/scoped_arena_allocator.h"
 #include "base/scoped_arena_containers.h"
-#include "base/bit_vector-inl.h"
+#include "base/utils.h"
 #include "side_effects_analysis.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index d699d01..0a310ca 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -78,16 +78,10 @@
   DCHECK(instruction != nullptr);
   if (instruction->IsArrayLength()) {
     return true;
-  } else if (instruction->IsInvokeStaticOrDirect()) {
-    switch (instruction->AsInvoke()->GetIntrinsic()) {
-      case Intrinsics::kMathMinIntInt:
-      case Intrinsics::kMathMinLongLong:
-        // Instruction MIN(>=0, >=0) is >= 0.
-        return IsGEZero(instruction->InputAt(0)) &&
-               IsGEZero(instruction->InputAt(1));
-      default:
-        break;
-    }
+  } else if (instruction->IsMin()) {
+    // Instruction MIN(>=0, >=0) is >= 0.
+    return IsGEZero(instruction->InputAt(0)) &&
+           IsGEZero(instruction->InputAt(1));
   } else if (instruction->IsAbs()) {
     // Instruction ABS(>=0) is >= 0.
     // NOTE: ABS(minint) = minint prevents assuming
@@ -101,21 +95,14 @@
 /** Hunts "under the hood" for a suitable instruction at the hint. */
 static bool IsMaxAtHint(
     HInstruction* instruction, HInstruction* hint, /*out*/HInstruction** suitable) {
-  if (instruction->IsInvokeStaticOrDirect()) {
-    switch (instruction->AsInvoke()->GetIntrinsic()) {
-      case Intrinsics::kMathMinIntInt:
-      case Intrinsics::kMathMinLongLong:
-        // For MIN(x, y), return most suitable x or y as maximum.
-        return IsMaxAtHint(instruction->InputAt(0), hint, suitable) ||
-               IsMaxAtHint(instruction->InputAt(1), hint, suitable);
-      default:
-        break;
-    }
+  if (instruction->IsMin()) {
+    // For MIN(x, y), return most suitable x or y as maximum.
+    return IsMaxAtHint(instruction->InputAt(0), hint, suitable) ||
+           IsMaxAtHint(instruction->InputAt(1), hint, suitable);
   } else {
     *suitable = instruction;
     return HuntForDeclaration(instruction) == hint;
   }
-  return false;
 }
 
 /** Post-analysis simplification of a minimum value that makes the bound more useful to clients. */
@@ -364,11 +351,11 @@
   }
 }
 
-bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const {
+bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const {
   HInductionVarAnalysis::InductionInfo *trip =
       induction_analysis_->LookupInfo(loop, GetLoopControl(loop));
   if (trip != nullptr && !IsUnsafeTripCount(trip)) {
-    IsConstant(trip->op_a, kExact, tc);
+    IsConstant(trip->op_a, kExact, trip_count);
     return true;
   }
   return false;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index eac8d2f..3483770 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -120,6 +120,8 @@
   void SimplifyReturnThis(HInvoke* invoke);
   void SimplifyAllocationIntrinsic(HInvoke* invoke);
   void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
+  void SimplifyMin(HInvoke* invoke, DataType::Type type);
+  void SimplifyMax(HInvoke* invoke, DataType::Type type);
   void SimplifyAbs(HInvoke* invoke, DataType::Type type);
 
   CodeGenerator* codegen_;
@@ -2407,6 +2409,20 @@
   invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier);
 }
 
+void InstructionSimplifierVisitor::SimplifyMin(HInvoke* invoke, DataType::Type type) {
+  DCHECK(invoke->IsInvokeStaticOrDirect());
+  HMin* min = new (GetGraph()->GetAllocator())
+      HMin(type, invoke->InputAt(0), invoke->InputAt(1), invoke->GetDexPc());
+  invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, min);
+}
+
+void InstructionSimplifierVisitor::SimplifyMax(HInvoke* invoke, DataType::Type type) {
+  DCHECK(invoke->IsInvokeStaticOrDirect());
+  HMax* max = new (GetGraph()->GetAllocator())
+      HMax(type, invoke->InputAt(0), invoke->InputAt(1), invoke->GetDexPc());
+  invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, max);
+}
+
 void InstructionSimplifierVisitor::SimplifyAbs(HInvoke* invoke, DataType::Type type) {
   DCHECK(invoke->IsInvokeStaticOrDirect());
   HAbs* abs = new (GetGraph()->GetAllocator())
@@ -2497,6 +2513,30 @@
     case Intrinsics::kVarHandleStoreStoreFence:
       SimplifyMemBarrier(instruction, MemBarrierKind::kStoreStore);
       break;
+    case Intrinsics::kMathMinIntInt:
+      SimplifyMin(instruction, DataType::Type::kInt32);
+      break;
+    case Intrinsics::kMathMinLongLong:
+      SimplifyMin(instruction, DataType::Type::kInt64);
+      break;
+    case Intrinsics::kMathMinFloatFloat:
+      SimplifyMin(instruction, DataType::Type::kFloat32);
+      break;
+    case Intrinsics::kMathMinDoubleDouble:
+      SimplifyMin(instruction, DataType::Type::kFloat64);
+      break;
+    case Intrinsics::kMathMaxIntInt:
+      SimplifyMax(instruction, DataType::Type::kInt32);
+      break;
+    case Intrinsics::kMathMaxLongLong:
+      SimplifyMax(instruction, DataType::Type::kInt64);
+      break;
+    case Intrinsics::kMathMaxFloatFloat:
+      SimplifyMax(instruction, DataType::Type::kFloat32);
+      break;
+    case Intrinsics::kMathMaxDoubleDouble:
+      SimplifyMax(instruction, DataType::Type::kFloat64);
+      break;
     case Intrinsics::kMathAbsInt:
       SimplifyAbs(instruction, DataType::Type::kInt32);
       break;
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index acb830e..f8dc316 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -18,6 +18,7 @@
 
 #include "art_field-inl.h"
 #include "art_method-inl.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "dex/invoke_type.h"
 #include "driver/compiler_driver.h"
@@ -26,7 +27,6 @@
 #include "nodes.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 24aff22..1035cbc 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -266,6 +266,14 @@
              << " should have been converted to HIR";                    \
 }
 #define UNREACHABLE_INTRINSICS(Arch)                            \
+UNREACHABLE_INTRINSIC(Arch, MathMinIntInt)                      \
+UNREACHABLE_INTRINSIC(Arch, MathMinLongLong)                    \
+UNREACHABLE_INTRINSIC(Arch, MathMinFloatFloat)                  \
+UNREACHABLE_INTRINSIC(Arch, MathMinDoubleDouble)                \
+UNREACHABLE_INTRINSIC(Arch, MathMaxIntInt)                      \
+UNREACHABLE_INTRINSIC(Arch, MathMaxLongLong)                    \
+UNREACHABLE_INTRINSIC(Arch, MathMaxFloatFloat)                  \
+UNREACHABLE_INTRINSIC(Arch, MathMaxDoubleDouble)                \
 UNREACHABLE_INTRINSIC(Arch, MathAbsInt)                         \
 UNREACHABLE_INTRINSIC(Arch, MathAbsLong)                        \
 UNREACHABLE_INTRINSIC(Arch, MathAbsFloat)                       \
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 0b04fff..81c0b50 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -344,14 +344,6 @@
   GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetVIXLAssembler());
 }
 
-static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
 static void GenNumberOfLeadingZeros(LocationSummary* locations,
                                     DataType::Type type,
                                     MacroAssembler* masm) {
@@ -529,113 +521,6 @@
   GenLowestOneBit(invoke, DataType::Type::kInt64, GetVIXLAssembler());
 }
 
-static void GenMinMaxFP(LocationSummary* locations,
-                        bool is_min,
-                        bool is_double,
-                        MacroAssembler* masm) {
-  Location op1 = locations->InAt(0);
-  Location op2 = locations->InAt(1);
-  Location out = locations->Out();
-
-  FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
-  FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
-  FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
-  if (is_min) {
-    __ Fmin(out_reg, op1_reg, op2_reg);
-  } else {
-    __ Fmax(out_reg, op1_reg, op2_reg);
-  }
-}
-
-static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetInAt(1, Location::RequiresFpuRegister());
-  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(
-      invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler());
-}
-
-static void GenMinMax(LocationSummary* locations,
-                      bool is_min,
-                      bool is_long,
-                      MacroAssembler* masm) {
-  Location op1 = locations->InAt(0);
-  Location op2 = locations->InAt(1);
-  Location out = locations->Out();
-
-  Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
-  Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
-  Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
-
-  __ Cmp(op1_reg, op2_reg);
-  __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler());
-}
-
 static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
   LocationSummary* locations =
       new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index e351fcc..e61a0b0 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -432,264 +432,6 @@
   GenNumberOfTrailingZeros(invoke, DataType::Type::kInt64, codegen_);
 }
 
-static void GenMinMaxFloat(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) {
-  ArmVIXLAssembler* assembler = codegen->GetAssembler();
-  Location op1_loc = invoke->GetLocations()->InAt(0);
-  Location op2_loc = invoke->GetLocations()->InAt(1);
-  Location out_loc = invoke->GetLocations()->Out();
-
-  // Optimization: don't generate any code if inputs are the same.
-  if (op1_loc.Equals(op2_loc)) {
-    DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in location builder.
-    return;
-  }
-
-  vixl32::SRegister op1 = SRegisterFrom(op1_loc);
-  vixl32::SRegister op2 = SRegisterFrom(op2_loc);
-  vixl32::SRegister out = OutputSRegister(invoke);
-  UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
-  const vixl32::Register temp1 = temps.Acquire();
-  vixl32::Register temp2 = RegisterFrom(invoke->GetLocations()->GetTemp(0));
-  vixl32::Label nan, done;
-  vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done);
-
-  DCHECK(op1.Is(out));
-
-  __ Vcmp(op1, op2);
-  __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
-  __ B(vs, &nan, /* far_target */ false);  // if un-ordered, go to NaN handling.
-
-  // op1 <> op2
-  vixl32::ConditionType cond = is_min ? gt : lt;
-  {
-    ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(),
-                                2 * kMaxInstructionSizeInBytes,
-                                CodeBufferCheckScope::kMaximumSize);
-    __ it(cond);
-    __ vmov(cond, F32, out, op2);
-  }
-  // for <>(not equal), we've done min/max calculation.
-  __ B(ne, final_label, /* far_target */ false);
-
-  // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
-  __ Vmov(temp1, op1);
-  __ Vmov(temp2, op2);
-  if (is_min) {
-    __ Orr(temp1, temp1, temp2);
-  } else {
-    __ And(temp1, temp1, temp2);
-  }
-  __ Vmov(out, temp1);
-  __ B(final_label);
-
-  // handle NaN input.
-  __ Bind(&nan);
-  __ Movt(temp1, High16Bits(kNanFloat));  // 0x7FC0xxxx is a NaN.
-  __ Vmov(out, temp1);
-
-  if (done.IsReferenced()) {
-    __ Bind(&done);
-  }
-}
-
-static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetInAt(1, Location::RequiresFpuRegister());
-  locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-  invoke->GetLocations()->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) {
-  GenMinMaxFloat(invoke, /* is_min */ true, codegen_);
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-  invoke->GetLocations()->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  GenMinMaxFloat(invoke, /* is_min */ false, codegen_);
-}
-
-static void GenMinMaxDouble(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) {
-  ArmVIXLAssembler* assembler = codegen->GetAssembler();
-  Location op1_loc = invoke->GetLocations()->InAt(0);
-  Location op2_loc = invoke->GetLocations()->InAt(1);
-  Location out_loc = invoke->GetLocations()->Out();
-
-  // Optimization: don't generate any code if inputs are the same.
-  if (op1_loc.Equals(op2_loc)) {
-    DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in.
-    return;
-  }
-
-  vixl32::DRegister op1 = DRegisterFrom(op1_loc);
-  vixl32::DRegister op2 = DRegisterFrom(op2_loc);
-  vixl32::DRegister out = OutputDRegister(invoke);
-  vixl32::Label handle_nan_eq, done;
-  vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done);
-
-  DCHECK(op1.Is(out));
-
-  __ Vcmp(op1, op2);
-  __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
-  __ B(vs, &handle_nan_eq, /* far_target */ false);  // if un-ordered, go to NaN handling.
-
-  // op1 <> op2
-  vixl32::ConditionType cond = is_min ? gt : lt;
-  {
-    ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(),
-                                2 * kMaxInstructionSizeInBytes,
-                                CodeBufferCheckScope::kMaximumSize);
-    __ it(cond);
-    __ vmov(cond, F64, out, op2);
-  }
-  // for <>(not equal), we've done min/max calculation.
-  __ B(ne, final_label, /* far_target */ false);
-
-  // handle op1 == op2, max(+0.0,-0.0).
-  if (!is_min) {
-    __ Vand(F64, out, op1, op2);
-    __ B(final_label);
-  }
-
-  // handle op1 == op2, min(+0.0,-0.0), NaN input.
-  __ Bind(&handle_nan_eq);
-  __ Vorr(F64, out, op1, op2);  // assemble op1/-0.0/NaN.
-
-  if (done.IsReferenced()) {
-    __ Bind(&done);
-  }
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  GenMinMaxDouble(invoke, /* is_min */ true , codegen_);
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  GenMinMaxDouble(invoke, /* is_min */ false, codegen_);
-}
-
-static void GenMinMaxLong(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) {
-  Location op1_loc = invoke->GetLocations()->InAt(0);
-  Location op2_loc = invoke->GetLocations()->InAt(1);
-  Location out_loc = invoke->GetLocations()->Out();
-
-  // Optimization: don't generate any code if inputs are the same.
-  if (op1_loc.Equals(op2_loc)) {
-    DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in location builder.
-    return;
-  }
-
-  vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
-  vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
-  vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
-  vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
-  vixl32::Register out_lo = LowRegisterFrom(out_loc);
-  vixl32::Register out_hi = HighRegisterFrom(out_loc);
-  UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
-  const vixl32::Register temp = temps.Acquire();
-
-  DCHECK(op1_lo.Is(out_lo));
-  DCHECK(op1_hi.Is(out_hi));
-
-  // Compare op1 >= op2, or op1 < op2.
-  __ Cmp(out_lo, op2_lo);
-  __ Sbcs(temp, out_hi, op2_hi);
-
-  // Now GE/LT condition code is correct for the long comparison.
-  {
-    vixl32::ConditionType cond = is_min ? ge : lt;
-    ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(),
-                                3 * kMaxInstructionSizeInBytes,
-                                CodeBufferCheckScope::kMaximumSize);
-    __ itt(cond);
-    __ mov(cond, out_lo, op2_lo);
-    __ mov(cond, out_hi, op2_hi);
-  }
-}
-
-static void CreateLongLongToLongLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMinLongLong(HInvoke* invoke) {
-  CreateLongLongToLongLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMinLongLong(HInvoke* invoke) {
-  GenMinMaxLong(invoke, /* is_min */ true, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxLongLong(HInvoke* invoke) {
-  CreateLongLongToLongLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxLongLong(HInvoke* invoke) {
-  GenMinMaxLong(invoke, /* is_min */ false, GetAssembler());
-}
-
-static void GenMinMax(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) {
-  vixl32::Register op1 = InputRegisterAt(invoke, 0);
-  vixl32::Register op2 = InputRegisterAt(invoke, 1);
-  vixl32::Register out = OutputRegister(invoke);
-
-  __ Cmp(op1, op2);
-
-  {
-    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
-                           3 * kMaxInstructionSizeInBytes,
-                           CodeBufferCheckScope::kMaximumSize);
-
-    __ ite(is_min ? lt : gt);
-    __ mov(is_min ? lt : gt, out, op1);
-    __ mov(is_min ? ge : le, out, op2);
-  }
-}
-
-static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMinIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMinIntInt(HInvoke* invoke) {
-  GenMinMax(invoke, /* is_min */ true, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) {
-  GenMinMax(invoke, /* is_min */ false, GetAssembler());
-}
-
 void IntrinsicLocationsBuilderARMVIXL::VisitMathSqrt(HInvoke* invoke) {
   CreateFPToFPLocations(allocator_, invoke);
 }
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 6d6ff75..bc1292b 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -58,6 +58,10 @@
   return codegen_->GetInstructionSetFeatures().Is32BitFloatingPoint();
 }
 
+inline bool IntrinsicCodeGeneratorMIPS::HasMsa() const {
+  return codegen_->GetInstructionSetFeatures().HasMsa();
+}
+
 #define __ codegen->GetAssembler()->
 
 static void MoveFromReturnRegister(Location trg,
@@ -612,6 +616,7 @@
 static void GenBitCount(LocationSummary* locations,
                         DataType::Type type,
                         bool isR6,
+                        bool hasMsa,
                         MipsAssembler* assembler) {
   Register out = locations->Out().AsRegister<Register>();
 
@@ -637,85 +642,102 @@
   // instructions compared to a loop-based algorithm which required 47
   // instructions.
 
-  if (type == DataType::Type::kInt32) {
-    Register in = locations->InAt(0).AsRegister<Register>();
-
-    __ Srl(TMP, in, 1);
-    __ LoadConst32(AT, 0x55555555);
-    __ And(TMP, TMP, AT);
-    __ Subu(TMP, in, TMP);
-    __ LoadConst32(AT, 0x33333333);
-    __ And(out, TMP, AT);
-    __ Srl(TMP, TMP, 2);
-    __ And(TMP, TMP, AT);
-    __ Addu(TMP, out, TMP);
-    __ Srl(out, TMP, 4);
-    __ Addu(out, out, TMP);
-    __ LoadConst32(AT, 0x0F0F0F0F);
-    __ And(out, out, AT);
-    __ LoadConst32(TMP, 0x01010101);
-    if (isR6) {
-      __ MulR6(out, out, TMP);
+  if (hasMsa) {
+    if (type == DataType::Type::kInt32) {
+      Register in = locations->InAt(0).AsRegister<Register>();
+      __ Mtc1(in, FTMP);
+      __ PcntW(static_cast<VectorRegister>(FTMP), static_cast<VectorRegister>(FTMP));
+      __ Mfc1(out, FTMP);
     } else {
-      __ MulR2(out, out, TMP);
+      DCHECK_EQ(type, DataType::Type::kInt64);
+      Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+      Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+      __ Mtc1(in_lo, FTMP);
+      __ Mthc1(in_hi, FTMP);
+      __ PcntD(static_cast<VectorRegister>(FTMP), static_cast<VectorRegister>(FTMP));
+      __ Mfc1(out, FTMP);
     }
-    __ Srl(out, out, 24);
   } else {
-    DCHECK_EQ(type, DataType::Type::kInt64);
-    Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
-    Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
-    Register tmp_hi = locations->GetTemp(0).AsRegister<Register>();
-    Register out_hi = locations->GetTemp(1).AsRegister<Register>();
-    Register tmp_lo = TMP;
-    Register out_lo = out;
+    if (type == DataType::Type::kInt32) {
+      Register in = locations->InAt(0).AsRegister<Register>();
 
-    __ Srl(tmp_lo, in_lo, 1);
-    __ Srl(tmp_hi, in_hi, 1);
-
-    __ LoadConst32(AT, 0x55555555);
-
-    __ And(tmp_lo, tmp_lo, AT);
-    __ Subu(tmp_lo, in_lo, tmp_lo);
-
-    __ And(tmp_hi, tmp_hi, AT);
-    __ Subu(tmp_hi, in_hi, tmp_hi);
-
-    __ LoadConst32(AT, 0x33333333);
-
-    __ And(out_lo, tmp_lo, AT);
-    __ Srl(tmp_lo, tmp_lo, 2);
-    __ And(tmp_lo, tmp_lo, AT);
-    __ Addu(tmp_lo, out_lo, tmp_lo);
-
-    __ And(out_hi, tmp_hi, AT);
-    __ Srl(tmp_hi, tmp_hi, 2);
-    __ And(tmp_hi, tmp_hi, AT);
-    __ Addu(tmp_hi, out_hi, tmp_hi);
-
-    // Here we deviate from the original algorithm a bit. We've reached
-    // the stage where the bitfields holding the subtotals are large
-    // enough to hold the combined subtotals for both the low word, and
-    // the high word. This means that we can add the subtotals for the
-    // the high, and low words into a single word, and compute the final
-    // result for both the high, and low words using fewer instructions.
-    __ LoadConst32(AT, 0x0F0F0F0F);
-
-    __ Addu(TMP, tmp_hi, tmp_lo);
-
-    __ Srl(out, TMP, 4);
-    __ And(out, out, AT);
-    __ And(TMP, TMP, AT);
-    __ Addu(out, out, TMP);
-
-    __ LoadConst32(AT, 0x01010101);
-
-    if (isR6) {
-      __ MulR6(out, out, AT);
+      __ Srl(TMP, in, 1);
+      __ LoadConst32(AT, 0x55555555);
+      __ And(TMP, TMP, AT);
+      __ Subu(TMP, in, TMP);
+      __ LoadConst32(AT, 0x33333333);
+      __ And(out, TMP, AT);
+      __ Srl(TMP, TMP, 2);
+      __ And(TMP, TMP, AT);
+      __ Addu(TMP, out, TMP);
+      __ Srl(out, TMP, 4);
+      __ Addu(out, out, TMP);
+      __ LoadConst32(AT, 0x0F0F0F0F);
+      __ And(out, out, AT);
+      __ LoadConst32(TMP, 0x01010101);
+      if (isR6) {
+        __ MulR6(out, out, TMP);
+      } else {
+        __ MulR2(out, out, TMP);
+      }
+      __ Srl(out, out, 24);
     } else {
-      __ MulR2(out, out, AT);
-    }
+      DCHECK_EQ(type, DataType::Type::kInt64);
+      Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+      Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+      Register tmp_hi = locations->GetTemp(0).AsRegister<Register>();
+      Register out_hi = locations->GetTemp(1).AsRegister<Register>();
+      Register tmp_lo = TMP;
+      Register out_lo = out;
 
-    __ Srl(out, out, 24);
+      __ Srl(tmp_lo, in_lo, 1);
+      __ Srl(tmp_hi, in_hi, 1);
+
+      __ LoadConst32(AT, 0x55555555);
+
+      __ And(tmp_lo, tmp_lo, AT);
+      __ Subu(tmp_lo, in_lo, tmp_lo);
+
+      __ And(tmp_hi, tmp_hi, AT);
+      __ Subu(tmp_hi, in_hi, tmp_hi);
+
+      __ LoadConst32(AT, 0x33333333);
+
+      __ And(out_lo, tmp_lo, AT);
+      __ Srl(tmp_lo, tmp_lo, 2);
+      __ And(tmp_lo, tmp_lo, AT);
+      __ Addu(tmp_lo, out_lo, tmp_lo);
+
+      __ And(out_hi, tmp_hi, AT);
+      __ Srl(tmp_hi, tmp_hi, 2);
+      __ And(tmp_hi, tmp_hi, AT);
+      __ Addu(tmp_hi, out_hi, tmp_hi);
+
+      // Here we deviate from the original algorithm a bit. We've reached
+      // the stage where the bitfields holding the subtotals are large
+      // enough to hold the combined subtotals for both the low word, and
+      // the high word. This means that we can add the subtotals for the
+      // the high, and low words into a single word, and compute the final
+      // result for both the high, and low words using fewer instructions.
+      __ LoadConst32(AT, 0x0F0F0F0F);
+
+      __ Addu(TMP, tmp_hi, tmp_lo);
+
+      __ Srl(out, TMP, 4);
+      __ And(out, out, AT);
+      __ And(TMP, TMP, AT);
+      __ Addu(out, out, TMP);
+
+      __ LoadConst32(AT, 0x01010101);
+
+      if (isR6) {
+        __ MulR6(out, out, AT);
+      } else {
+        __ MulR2(out, out, AT);
+      }
+
+      __ Srl(out, out, 24);
+    }
   }
 }
 
@@ -725,7 +747,7 @@
 }
 
 void IntrinsicCodeGeneratorMIPS::VisitIntegerBitCount(HInvoke* invoke) {
-  GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler());
+  GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), HasMsa(), GetAssembler());
 }
 
 // int java.lang.Long.bitCount(int)
@@ -739,459 +761,7 @@
 }
 
 void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) {
-  GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler());
-}
-
-static void GenMinMaxFP(LocationSummary* locations,
-                        bool is_min,
-                        DataType::Type type,
-                        bool is_R6,
-                        MipsAssembler* assembler) {
-  FRegister out = locations->Out().AsFpuRegister<FRegister>();
-  FRegister a = locations->InAt(0).AsFpuRegister<FRegister>();
-  FRegister b = locations->InAt(1).AsFpuRegister<FRegister>();
-
-  if (is_R6) {
-    MipsLabel noNaNs;
-    MipsLabel done;
-    FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
-
-    // When Java computes min/max it prefers a NaN to a number; the
-    // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
-    // the inputs is a NaN and the other is a valid number, the MIPS
-    // instruction will return the number; Java wants the NaN value
-    // returned. This is why there is extra logic preceding the use of
-    // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
-    // NaN, return the NaN, otherwise return the min/max.
-    if (type == DataType::Type::kFloat64) {
-      __ CmpUnD(FTMP, a, b);
-      __ Bc1eqz(FTMP, &noNaNs);
-
-      // One of the inputs is a NaN
-      __ CmpEqD(ftmp, a, a);
-      // If a == a then b is the NaN, otherwise a is the NaN.
-      __ SelD(ftmp, a, b);
-
-      if (ftmp != out) {
-        __ MovD(out, ftmp);
-      }
-
-      __ B(&done);
-
-      __ Bind(&noNaNs);
-
-      if (is_min) {
-        __ MinD(out, a, b);
-      } else {
-        __ MaxD(out, a, b);
-      }
-    } else {
-      DCHECK_EQ(type, DataType::Type::kFloat32);
-      __ CmpUnS(FTMP, a, b);
-      __ Bc1eqz(FTMP, &noNaNs);
-
-      // One of the inputs is a NaN
-      __ CmpEqS(ftmp, a, a);
-      // If a == a then b is the NaN, otherwise a is the NaN.
-      __ SelS(ftmp, a, b);
-
-      if (ftmp != out) {
-        __ MovS(out, ftmp);
-      }
-
-      __ B(&done);
-
-      __ Bind(&noNaNs);
-
-      if (is_min) {
-        __ MinS(out, a, b);
-      } else {
-        __ MaxS(out, a, b);
-      }
-    }
-
-    __ Bind(&done);
-  } else {
-    MipsLabel ordered;
-    MipsLabel compare;
-    MipsLabel select;
-    MipsLabel done;
-
-    if (type == DataType::Type::kFloat64) {
-      __ CunD(a, b);
-    } else {
-      DCHECK_EQ(type, DataType::Type::kFloat32);
-      __ CunS(a, b);
-    }
-    __ Bc1f(&ordered);
-
-    // a or b (or both) is a NaN. Return one, which is a NaN.
-    if (type == DataType::Type::kFloat64) {
-      __ CeqD(b, b);
-    } else {
-      __ CeqS(b, b);
-    }
-    __ B(&select);
-
-    __ Bind(&ordered);
-
-    // Neither is a NaN.
-    // a == b? (-0.0 compares equal with +0.0)
-    // If equal, handle zeroes, else compare further.
-    if (type == DataType::Type::kFloat64) {
-      __ CeqD(a, b);
-    } else {
-      __ CeqS(a, b);
-    }
-    __ Bc1f(&compare);
-
-    // a == b either bit for bit or one is -0.0 and the other is +0.0.
-    if (type == DataType::Type::kFloat64) {
-      __ MoveFromFpuHigh(TMP, a);
-      __ MoveFromFpuHigh(AT, b);
-    } else {
-      __ Mfc1(TMP, a);
-      __ Mfc1(AT, b);
-    }
-
-    if (is_min) {
-      // -0.0 prevails over +0.0.
-      __ Or(TMP, TMP, AT);
-    } else {
-      // +0.0 prevails over -0.0.
-      __ And(TMP, TMP, AT);
-    }
-
-    if (type == DataType::Type::kFloat64) {
-      __ Mfc1(AT, a);
-      __ Mtc1(AT, out);
-      __ MoveToFpuHigh(TMP, out);
-    } else {
-      __ Mtc1(TMP, out);
-    }
-    __ B(&done);
-
-    __ Bind(&compare);
-
-    if (type == DataType::Type::kFloat64) {
-      if (is_min) {
-        // return (a <= b) ? a : b;
-        __ ColeD(a, b);
-      } else {
-        // return (a >= b) ? a : b;
-        __ ColeD(b, a);  // b <= a
-      }
-    } else {
-      if (is_min) {
-        // return (a <= b) ? a : b;
-        __ ColeS(a, b);
-      } else {
-        // return (a >= b) ? a : b;
-        __ ColeS(b, a);  // b <= a
-      }
-    }
-
-    __ Bind(&select);
-
-    if (type == DataType::Type::kFloat64) {
-      __ MovtD(out, a);
-      __ MovfD(out, b);
-    } else {
-      __ MovtS(out, a);
-      __ MovfS(out, b);
-    }
-
-    __ Bind(&done);
-  }
-}
-
-static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetInAt(1, Location::RequiresFpuRegister());
-  locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap);
-}
-
-// double java.lang.Math.min(double, double)
-void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(),
-              /* is_min */ true,
-              DataType::Type::kFloat64,
-              IsR6(),
-              GetAssembler());
-}
-
-// float java.lang.Math.min(float, float)
-void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(),
-              /* is_min */ true,
-              DataType::Type::kFloat32,
-              IsR6(),
-              GetAssembler());
-}
-
-// double java.lang.Math.max(double, double)
-void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(),
-              /* is_min */ false,
-              DataType::Type::kFloat64,
-              IsR6(),
-              GetAssembler());
-}
-
-// float java.lang.Math.max(float, float)
-void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(),
-              /* is_min */ false,
-              DataType::Type::kFloat32,
-              IsR6(),
-              GetAssembler());
-}
-
-static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-static void GenMinMax(LocationSummary* locations,
-                      bool is_min,
-                      DataType::Type type,
-                      bool is_R6,
-                      MipsAssembler* assembler) {
-  if (is_R6) {
-    // Some architectures, such as ARM and MIPS (prior to r6), have a
-    // conditional move instruction which only changes the target
-    // (output) register if the condition is true (MIPS prior to r6 had
-    // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions
-    // always change the target (output) register.  If the condition is
-    // true the output register gets the contents of the "rs" register;
-    // otherwise, the output register is set to zero. One consequence
-    // of this is that to implement something like "rd = c==0 ? rs : rt"
-    // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions.
-    // After executing this pair of instructions one of the output
-    // registers from the pair will necessarily contain zero. Then the
-    // code ORs the output registers from the SELEQZ/SELNEZ instructions
-    // to get the final result.
-    //
-    // The initial test to see if the output register is same as the
-    // first input register is needed to make sure that value in the
-    // first input register isn't clobbered before we've finished
-    // computing the output value. The logic in the corresponding else
-    // clause performs the same task but makes sure the second input
-    // register isn't clobbered in the event that it's the same register
-    // as the output register; the else clause also handles the case
-    // where the output register is distinct from both the first, and the
-    // second input registers.
-    if (type == DataType::Type::kInt64) {
-      Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
-      Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
-      Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
-      Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
-      Register out_lo = locations->Out().AsRegisterPairLow<Register>();
-      Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
-
-      MipsLabel compare_done;
-
-      if (a_lo == b_lo) {
-        if (out_lo != a_lo) {
-          __ Move(out_lo, a_lo);
-          __ Move(out_hi, a_hi);
-        }
-      } else {
-        __ Slt(TMP, b_hi, a_hi);
-        __ Bne(b_hi, a_hi, &compare_done);
-
-        __ Sltu(TMP, b_lo, a_lo);
-
-        __ Bind(&compare_done);
-
-        if (is_min) {
-          __ Seleqz(AT, a_lo, TMP);
-          __ Selnez(out_lo, b_lo, TMP);  // Safe even if out_lo == a_lo/b_lo
-                                         // because at this point we're
-                                         // done using a_lo/b_lo.
-        } else {
-          __ Selnez(AT, a_lo, TMP);
-          __ Seleqz(out_lo, b_lo, TMP);  // ditto
-        }
-        __ Or(out_lo, out_lo, AT);
-        if (is_min) {
-          __ Seleqz(AT, a_hi, TMP);
-          __ Selnez(out_hi, b_hi, TMP);  // ditto but for out_hi & a_hi/b_hi
-        } else {
-          __ Selnez(AT, a_hi, TMP);
-          __ Seleqz(out_hi, b_hi, TMP);  // ditto but for out_hi & a_hi/b_hi
-        }
-        __ Or(out_hi, out_hi, AT);
-      }
-    } else {
-      DCHECK_EQ(type, DataType::Type::kInt32);
-      Register a = locations->InAt(0).AsRegister<Register>();
-      Register b = locations->InAt(1).AsRegister<Register>();
-      Register out = locations->Out().AsRegister<Register>();
-
-      if (a == b) {
-        if (out != a) {
-          __ Move(out, a);
-        }
-      } else {
-        __ Slt(AT, b, a);
-        if (is_min) {
-          __ Seleqz(TMP, a, AT);
-          __ Selnez(AT, b, AT);
-        } else {
-          __ Selnez(TMP, a, AT);
-          __ Seleqz(AT, b, AT);
-        }
-        __ Or(out, TMP, AT);
-      }
-    }
-  } else {
-    if (type == DataType::Type::kInt64) {
-      Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
-      Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
-      Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
-      Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
-      Register out_lo = locations->Out().AsRegisterPairLow<Register>();
-      Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
-
-      MipsLabel compare_done;
-
-      if (a_lo == b_lo) {
-        if (out_lo != a_lo) {
-          __ Move(out_lo, a_lo);
-          __ Move(out_hi, a_hi);
-        }
-      } else {
-        __ Slt(TMP, a_hi, b_hi);
-        __ Bne(a_hi, b_hi, &compare_done);
-
-        __ Sltu(TMP, a_lo, b_lo);
-
-        __ Bind(&compare_done);
-
-        if (is_min) {
-          if (out_lo != a_lo) {
-            __ Movn(out_hi, a_hi, TMP);
-            __ Movn(out_lo, a_lo, TMP);
-          }
-          if (out_lo != b_lo) {
-            __ Movz(out_hi, b_hi, TMP);
-            __ Movz(out_lo, b_lo, TMP);
-          }
-        } else {
-          if (out_lo != a_lo) {
-            __ Movz(out_hi, a_hi, TMP);
-            __ Movz(out_lo, a_lo, TMP);
-          }
-          if (out_lo != b_lo) {
-            __ Movn(out_hi, b_hi, TMP);
-            __ Movn(out_lo, b_lo, TMP);
-          }
-        }
-      }
-    } else {
-      DCHECK_EQ(type, DataType::Type::kInt32);
-      Register a = locations->InAt(0).AsRegister<Register>();
-      Register b = locations->InAt(1).AsRegister<Register>();
-      Register out = locations->Out().AsRegister<Register>();
-
-      if (a == b) {
-        if (out != a) {
-          __ Move(out, a);
-        }
-      } else {
-        __ Slt(AT, a, b);
-        if (is_min) {
-          if (out != a) {
-            __ Movn(out, a, AT);
-          }
-          if (out != b) {
-            __ Movz(out, b, AT);
-          }
-        } else {
-          if (out != a) {
-            __ Movz(out, a, AT);
-          }
-          if (out != b) {
-            __ Movn(out, b, AT);
-          }
-        }
-      }
-    }
-  }
-}
-
-// int java.lang.Math.min(int, int)
-void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(),
-            /* is_min */ true,
-            DataType::Type::kInt32,
-            IsR6(),
-            GetAssembler());
-}
-
-// long java.lang.Math.min(long, long)
-void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(),
-            /* is_min */ true,
-            DataType::Type::kInt64,
-            IsR6(),
-            GetAssembler());
-}
-
-// int java.lang.Math.max(int, int)
-void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(),
-            /* is_min */ false,
-            DataType::Type::kInt32,
-            IsR6(),
-            GetAssembler());
-}
-
-// long java.lang.Math.max(long, long)
-void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(),
-            /* is_min */ false,
-            DataType::Type::kInt64,
-            IsR6(),
-            GetAssembler());
+  GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), HasMsa(), GetAssembler());
 }
 
 // double java.lang.Math.sqrt(double)
diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h
index 13397f1..1c1ba40 100644
--- a/compiler/optimizing/intrinsics_mips.h
+++ b/compiler/optimizing/intrinsics_mips.h
@@ -71,6 +71,7 @@
   bool IsR2OrNewer() const;
   bool IsR6() const;
   bool Is32BitFPU() const;
+  bool HasMsa() const;
 
  private:
   MipsAssembler* GetAssembler();
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 5debd26..f429afd 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -46,6 +46,10 @@
   return codegen_->GetGraph()->GetAllocator();
 }
 
+inline bool IntrinsicCodeGeneratorMIPS64::HasMsa() const {
+  return codegen_->GetInstructionSetFeatures().HasMsa();
+}
+
 #define __ codegen->GetAssembler()->
 
 static void MoveFromReturnRegister(Location trg,
@@ -386,6 +390,7 @@
 
 static void GenBitCount(LocationSummary* locations,
                         const DataType::Type type,
+                        const bool hasMsa,
                         Mips64Assembler* assembler) {
   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
   GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>();
@@ -414,41 +419,52 @@
   // bits are set but the algorithm here attempts to minimize the total
   // number of instructions executed even when a large number of bits
   // are set.
-
-  if (type == DataType::Type::kInt32) {
-    __ Srl(TMP, in, 1);
-    __ LoadConst32(AT, 0x55555555);
-    __ And(TMP, TMP, AT);
-    __ Subu(TMP, in, TMP);
-    __ LoadConst32(AT, 0x33333333);
-    __ And(out, TMP, AT);
-    __ Srl(TMP, TMP, 2);
-    __ And(TMP, TMP, AT);
-    __ Addu(TMP, out, TMP);
-    __ Srl(out, TMP, 4);
-    __ Addu(out, out, TMP);
-    __ LoadConst32(AT, 0x0F0F0F0F);
-    __ And(out, out, AT);
-    __ LoadConst32(TMP, 0x01010101);
-    __ MulR6(out, out, TMP);
-    __ Srl(out, out, 24);
-  } else if (type == DataType::Type::kInt64) {
-    __ Dsrl(TMP, in, 1);
-    __ LoadConst64(AT, 0x5555555555555555L);
-    __ And(TMP, TMP, AT);
-    __ Dsubu(TMP, in, TMP);
-    __ LoadConst64(AT, 0x3333333333333333L);
-    __ And(out, TMP, AT);
-    __ Dsrl(TMP, TMP, 2);
-    __ And(TMP, TMP, AT);
-    __ Daddu(TMP, out, TMP);
-    __ Dsrl(out, TMP, 4);
-    __ Daddu(out, out, TMP);
-    __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL);
-    __ And(out, out, AT);
-    __ LoadConst64(TMP, 0x0101010101010101L);
-    __ Dmul(out, out, TMP);
-    __ Dsrl32(out, out, 24);
+  if (hasMsa) {
+    if (type == DataType::Type::kInt32) {
+      __ Mtc1(in, FTMP);
+      __ PcntW(static_cast<VectorRegister>(FTMP), static_cast<VectorRegister>(FTMP));
+      __ Mfc1(out, FTMP);
+    } else {
+      __ Dmtc1(in, FTMP);
+      __ PcntD(static_cast<VectorRegister>(FTMP), static_cast<VectorRegister>(FTMP));
+      __ Dmfc1(out, FTMP);
+    }
+  } else {
+    if (type == DataType::Type::kInt32) {
+      __ Srl(TMP, in, 1);
+      __ LoadConst32(AT, 0x55555555);
+      __ And(TMP, TMP, AT);
+      __ Subu(TMP, in, TMP);
+      __ LoadConst32(AT, 0x33333333);
+      __ And(out, TMP, AT);
+      __ Srl(TMP, TMP, 2);
+      __ And(TMP, TMP, AT);
+      __ Addu(TMP, out, TMP);
+      __ Srl(out, TMP, 4);
+      __ Addu(out, out, TMP);
+      __ LoadConst32(AT, 0x0F0F0F0F);
+      __ And(out, out, AT);
+      __ LoadConst32(TMP, 0x01010101);
+      __ MulR6(out, out, TMP);
+      __ Srl(out, out, 24);
+    } else {
+      __ Dsrl(TMP, in, 1);
+      __ LoadConst64(AT, 0x5555555555555555L);
+      __ And(TMP, TMP, AT);
+      __ Dsubu(TMP, in, TMP);
+      __ LoadConst64(AT, 0x3333333333333333L);
+      __ And(out, TMP, AT);
+      __ Dsrl(TMP, TMP, 2);
+      __ And(TMP, TMP, AT);
+      __ Daddu(TMP, out, TMP);
+      __ Dsrl(out, TMP, 4);
+      __ Daddu(out, out, TMP);
+      __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL);
+      __ And(out, out, AT);
+      __ LoadConst64(TMP, 0x0101010101010101L);
+      __ Dmul(out, out, TMP);
+      __ Dsrl32(out, out, 24);
+    }
   }
 }
 
@@ -458,7 +474,7 @@
 }
 
 void IntrinsicCodeGeneratorMIPS64::VisitIntegerBitCount(HInvoke* invoke) {
-  GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, GetAssembler());
+  GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, HasMsa(), GetAssembler());
 }
 
 // int java.lang.Long.bitCount(long)
@@ -467,222 +483,7 @@
 }
 
 void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) {
-  GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, GetAssembler());
-}
-
-static void GenMinMaxFP(LocationSummary* locations,
-                        bool is_min,
-                        DataType::Type type,
-                        Mips64Assembler* assembler) {
-  FpuRegister a = locations->InAt(0).AsFpuRegister<FpuRegister>();
-  FpuRegister b = locations->InAt(1).AsFpuRegister<FpuRegister>();
-  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
-
-  Mips64Label noNaNs;
-  Mips64Label done;
-  FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
-
-  // When Java computes min/max it prefers a NaN to a number; the
-  // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
-  // the inputs is a NaN and the other is a valid number, the MIPS
-  // instruction will return the number; Java wants the NaN value
-  // returned. This is why there is extra logic preceding the use of
-  // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
-  // NaN, return the NaN, otherwise return the min/max.
-  if (type == DataType::Type::kFloat64) {
-    __ CmpUnD(FTMP, a, b);
-    __ Bc1eqz(FTMP, &noNaNs);
-
-    // One of the inputs is a NaN
-    __ CmpEqD(ftmp, a, a);
-    // If a == a then b is the NaN, otherwise a is the NaN.
-    __ SelD(ftmp, a, b);
-
-    if (ftmp != out) {
-      __ MovD(out, ftmp);
-    }
-
-    __ Bc(&done);
-
-    __ Bind(&noNaNs);
-
-    if (is_min) {
-      __ MinD(out, a, b);
-    } else {
-      __ MaxD(out, a, b);
-    }
-  } else {
-    DCHECK_EQ(type, DataType::Type::kFloat32);
-    __ CmpUnS(FTMP, a, b);
-    __ Bc1eqz(FTMP, &noNaNs);
-
-    // One of the inputs is a NaN
-    __ CmpEqS(ftmp, a, a);
-    // If a == a then b is the NaN, otherwise a is the NaN.
-    __ SelS(ftmp, a, b);
-
-    if (ftmp != out) {
-      __ MovS(out, ftmp);
-    }
-
-    __ Bc(&done);
-
-    __ Bind(&noNaNs);
-
-    if (is_min) {
-      __ MinS(out, a, b);
-    } else {
-      __ MaxS(out, a, b);
-    }
-  }
-
-  __ Bind(&done);
-}
-
-static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetInAt(1, Location::RequiresFpuRegister());
-  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-}
-
-// double java.lang.Math.min(double, double)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, DataType::Type::kFloat64, GetAssembler());
-}
-
-// float java.lang.Math.min(float, float)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, DataType::Type::kFloat32, GetAssembler());
-}
-
-// double java.lang.Math.max(double, double)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, DataType::Type::kFloat64, GetAssembler());
-}
-
-// float java.lang.Math.max(float, float)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, DataType::Type::kFloat32, GetAssembler());
-}
-
-static void GenMinMax(LocationSummary* locations,
-                      bool is_min,
-                      Mips64Assembler* assembler) {
-  GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
-  GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
-  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
-
-  if (lhs == rhs) {
-    if (out != lhs) {
-      __ Move(out, lhs);
-    }
-  } else {
-    // Some architectures, such as ARM and MIPS (prior to r6), have a
-    // conditional move instruction which only changes the target
-    // (output) register if the condition is true (MIPS prior to r6 had
-    // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
-    // change the target (output) register.  If the condition is true the
-    // output register gets the contents of the "rs" register; otherwise,
-    // the output register is set to zero. One consequence of this is
-    // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
-    // needs to use a pair of SELEQZ/SELNEZ instructions.  After
-    // executing this pair of instructions one of the output registers
-    // from the pair will necessarily contain zero. Then the code ORs the
-    // output registers from the SELEQZ/SELNEZ instructions to get the
-    // final result.
-    //
-    // The initial test to see if the output register is same as the
-    // first input register is needed to make sure that value in the
-    // first input register isn't clobbered before we've finished
-    // computing the output value. The logic in the corresponding else
-    // clause performs the same task but makes sure the second input
-    // register isn't clobbered in the event that it's the same register
-    // as the output register; the else clause also handles the case
-    // where the output register is distinct from both the first, and the
-    // second input registers.
-    if (out == lhs) {
-      __ Slt(AT, rhs, lhs);
-      if (is_min) {
-        __ Seleqz(out, lhs, AT);
-        __ Selnez(AT, rhs, AT);
-      } else {
-        __ Selnez(out, lhs, AT);
-        __ Seleqz(AT, rhs, AT);
-      }
-    } else {
-      __ Slt(AT, lhs, rhs);
-      if (is_min) {
-        __ Seleqz(out, rhs, AT);
-        __ Selnez(AT, lhs, AT);
-      } else {
-        __ Selnez(out, rhs, AT);
-        __ Seleqz(AT, lhs, AT);
-      }
-    }
-    __ Or(out, out, AT);
-  }
-}
-
-static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-// int java.lang.Math.min(int, int)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMinIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMinIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
-}
-
-// long java.lang.Math.min(long, long)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMinLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMinLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
-}
-
-// int java.lang.Math.max(int, int)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMaxIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMaxIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
-}
-
-// long java.lang.Math.max(long, long)
-void IntrinsicLocationsBuilderMIPS64::VisitMathMaxLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitMathMaxLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
+  GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, HasMsa(), GetAssembler());
 }
 
 // double java.lang.Math.sqrt(double)
diff --git a/compiler/optimizing/intrinsics_mips64.h b/compiler/optimizing/intrinsics_mips64.h
index 6f40d90..748b0b0 100644
--- a/compiler/optimizing/intrinsics_mips64.h
+++ b/compiler/optimizing/intrinsics_mips64.h
@@ -68,6 +68,8 @@
 #undef INTRINSICS_LIST
 #undef OPTIMIZING_INTRINSICS
 
+  bool HasMsa() const;
+
  private:
   Mips64Assembler* GetAssembler();
 
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 0edc061..c4f322b 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -40,11 +40,6 @@
 
 namespace x86 {
 
-static constexpr int kDoubleNaNHigh = 0x7FF80000;
-static constexpr int kDoubleNaNLow = 0x00000000;
-static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000);
-static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000);
-
 IntrinsicLocationsBuilderX86::IntrinsicLocationsBuilderX86(CodeGeneratorX86* codegen)
   : allocator_(codegen->GetGraph()->GetAllocator()),
     codegen_(codegen) {
@@ -333,278 +328,6 @@
   GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler());
 }
 
-static void GenMinMaxFP(HInvoke* invoke,
-                        bool is_min,
-                        bool is_double,
-                        X86Assembler* assembler,
-                        CodeGeneratorX86* codegen) {
-  LocationSummary* locations = invoke->GetLocations();
-  Location op1_loc = locations->InAt(0);
-  Location op2_loc = locations->InAt(1);
-  Location out_loc = locations->Out();
-  XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
-
-  // Shortcut for same input locations.
-  if (op1_loc.Equals(op2_loc)) {
-    DCHECK(out_loc.Equals(op1_loc));
-    return;
-  }
-
-  //  (out := op1)
-  //  out <=? op2
-  //  if Nan jmp Nan_label
-  //  if out is min jmp done
-  //  if op2 is min jmp op2_label
-  //  handle -0/+0
-  //  jmp done
-  // Nan_label:
-  //  out := NaN
-  // op2_label:
-  //  out := op2
-  // done:
-  //
-  // This removes one jmp, but needs to copy one input (op1) to out.
-  //
-  // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath?
-
-  XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
-
-  NearLabel nan, done, op2_label;
-  if (is_double) {
-    __ ucomisd(out, op2);
-  } else {
-    __ ucomiss(out, op2);
-  }
-
-  __ j(Condition::kParityEven, &nan);
-
-  __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
-  __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
-
-  // Handle 0.0/-0.0.
-  if (is_min) {
-    if (is_double) {
-      __ orpd(out, op2);
-    } else {
-      __ orps(out, op2);
-    }
-  } else {
-    if (is_double) {
-      __ andpd(out, op2);
-    } else {
-      __ andps(out, op2);
-    }
-  }
-  __ jmp(&done);
-
-  // NaN handling.
-  __ Bind(&nan);
-  // Do we have a constant area pointer?
-  if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) {
-    HX86ComputeBaseMethodAddress* method_address =
-        invoke->InputAt(2)->AsX86ComputeBaseMethodAddress();
-    DCHECK(locations->InAt(2).IsRegister());
-    Register constant_area = locations->InAt(2).AsRegister<Register>();
-    if (is_double) {
-      __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, method_address, constant_area));
-    } else {
-      __ movss(out, codegen->LiteralInt32Address(kFloatNaN, method_address, constant_area));
-    }
-  } else {
-    if (is_double) {
-      __ pushl(Immediate(kDoubleNaNHigh));
-      __ pushl(Immediate(kDoubleNaNLow));
-      __ movsd(out, Address(ESP, 0));
-      __ addl(ESP, Immediate(8));
-    } else {
-      __ pushl(Immediate(kFloatNaN));
-      __ movss(out, Address(ESP, 0));
-      __ addl(ESP, Immediate(4));
-    }
-  }
-  __ jmp(&done);
-
-  // out := op2;
-  __ Bind(&op2_label);
-  if (is_double) {
-    __ movsd(out, op2);
-  } else {
-    __ movss(out, op2);
-  }
-
-  // Done.
-  __ Bind(&done);
-}
-
-static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetInAt(1, Location::RequiresFpuRegister());
-  // The following is sub-optimal, but all we can do for now. It would be fine to also accept
-  // the second input to be the output (we can simply swap inputs).
-  locations->SetOut(Location::SameAsFirstInput());
-  HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect();
-  DCHECK(static_or_direct != nullptr);
-  if (static_or_direct->HasSpecialInput() &&
-      invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
-    locations->SetInAt(2, Location::RequiresRegister());
-  }
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke,
-              /* is_min */ true,
-              /* is_double */ true,
-              GetAssembler(),
-              codegen_);
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(invoke,
-              /* is_min */ true,
-              /* is_double */ false,
-              GetAssembler(),
-              codegen_);
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(invoke,
-              /* is_min */ false,
-              /* is_double */ true,
-              GetAssembler(),
-              codegen_);
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFPLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(invoke,
-              /* is_min */ false,
-              /* is_double */ false,
-              GetAssembler(),
-              codegen_);
-}
-
-static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long,
-                      X86Assembler* assembler) {
-  Location op1_loc = locations->InAt(0);
-  Location op2_loc = locations->InAt(1);
-
-  // Shortcut for same input locations.
-  if (op1_loc.Equals(op2_loc)) {
-    // Can return immediately, as op1_loc == out_loc.
-    // Note: if we ever support separate registers, e.g., output into memory, we need to check for
-    //       a copy here.
-    DCHECK(locations->Out().Equals(op1_loc));
-    return;
-  }
-
-  if (is_long) {
-    // Need to perform a subtract to get the sign right.
-    // op1 is already in the same location as the output.
-    Location output = locations->Out();
-    Register output_lo = output.AsRegisterPairLow<Register>();
-    Register output_hi = output.AsRegisterPairHigh<Register>();
-
-    Register op2_lo = op2_loc.AsRegisterPairLow<Register>();
-    Register op2_hi = op2_loc.AsRegisterPairHigh<Register>();
-
-    // Spare register to compute the subtraction to set condition code.
-    Register temp = locations->GetTemp(0).AsRegister<Register>();
-
-    // Subtract off op2_low.
-    __ movl(temp, output_lo);
-    __ subl(temp, op2_lo);
-
-    // Now use the same tempo and the borrow to finish the subtraction of op2_hi.
-    __ movl(temp, output_hi);
-    __ sbbl(temp, op2_hi);
-
-    // Now the condition code is correct.
-    Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess;
-    __ cmovl(cond, output_lo, op2_lo);
-    __ cmovl(cond, output_hi, op2_hi);
-  } else {
-    Register out = locations->Out().AsRegister<Register>();
-    Register op2 = op2_loc.AsRegister<Register>();
-
-    //  (out := op1)
-    //  out <=? op2
-    //  if out is min jmp done
-    //  out := op2
-    // done:
-
-    __ cmpl(out, op2);
-    Condition cond = is_min ? Condition::kGreater : Condition::kLess;
-    __ cmovl(cond, out, op2);
-  }
-}
-
-static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::SameAsFirstInput());
-}
-
-static void CreateLongLongToLongLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::SameAsFirstInput());
-  // Register to use to perform a long subtract to set cc.
-  locations->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMinIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMinIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMinLongLong(HInvoke* invoke) {
-  CreateLongLongToLongLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMinLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMaxIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMaxIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderX86::VisitMathMaxLongLong(HInvoke* invoke) {
-  CreateLongLongToLongLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86::VisitMathMaxLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetAssembler());
-}
-
 static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
   LocationSummary* locations =
       new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 9d378d6..437bc3d 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -236,208 +236,6 @@
   GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler());
 }
 
-static void GenMinMaxFP(LocationSummary* locations,
-                        bool is_min,
-                        bool is_double,
-                        X86_64Assembler* assembler,
-                        CodeGeneratorX86_64* codegen) {
-  Location op1_loc = locations->InAt(0);
-  Location op2_loc = locations->InAt(1);
-  Location out_loc = locations->Out();
-  XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
-
-  // Shortcut for same input locations.
-  if (op1_loc.Equals(op2_loc)) {
-    DCHECK(out_loc.Equals(op1_loc));
-    return;
-  }
-
-  //  (out := op1)
-  //  out <=? op2
-  //  if Nan jmp Nan_label
-  //  if out is min jmp done
-  //  if op2 is min jmp op2_label
-  //  handle -0/+0
-  //  jmp done
-  // Nan_label:
-  //  out := NaN
-  // op2_label:
-  //  out := op2
-  // done:
-  //
-  // This removes one jmp, but needs to copy one input (op1) to out.
-  //
-  // TODO: This is straight from Quick. Make NaN an out-of-line slowpath?
-
-  XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
-
-  NearLabel nan, done, op2_label;
-  if (is_double) {
-    __ ucomisd(out, op2);
-  } else {
-    __ ucomiss(out, op2);
-  }
-
-  __ j(Condition::kParityEven, &nan);
-
-  __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
-  __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
-
-  // Handle 0.0/-0.0.
-  if (is_min) {
-    if (is_double) {
-      __ orpd(out, op2);
-    } else {
-      __ orps(out, op2);
-    }
-  } else {
-    if (is_double) {
-      __ andpd(out, op2);
-    } else {
-      __ andps(out, op2);
-    }
-  }
-  __ jmp(&done);
-
-  // NaN handling.
-  __ Bind(&nan);
-  if (is_double) {
-    __ movsd(out, codegen->LiteralInt64Address(INT64_C(0x7FF8000000000000)));
-  } else {
-    __ movss(out, codegen->LiteralInt32Address(INT32_C(0x7FC00000)));
-  }
-  __ jmp(&done);
-
-  // out := op2;
-  __ Bind(&op2_label);
-  if (is_double) {
-    __ movsd(out, op2);
-  } else {
-    __ movss(out, op2);
-  }
-
-  // Done.
-  __ Bind(&done);
-}
-
-static void CreateFPFPToFP(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetInAt(1, Location::RequiresFpuRegister());
-  // The following is sub-optimal, but all we can do for now. It would be fine to also accept
-  // the second input to be the output (we can simply swap inputs).
-  locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFP(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(
-      invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetAssembler(), codegen_);
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFP(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(
-      invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetAssembler(), codegen_);
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  CreateFPFPToFP(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
-  GenMinMaxFP(
-      invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetAssembler(), codegen_);
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  CreateFPFPToFP(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
-  GenMinMaxFP(
-      invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetAssembler(), codegen_);
-}
-
-static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long,
-                      X86_64Assembler* assembler) {
-  Location op1_loc = locations->InAt(0);
-  Location op2_loc = locations->InAt(1);
-
-  // Shortcut for same input locations.
-  if (op1_loc.Equals(op2_loc)) {
-    // Can return immediately, as op1_loc == out_loc.
-    // Note: if we ever support separate registers, e.g., output into memory, we need to check for
-    //       a copy here.
-    DCHECK(locations->Out().Equals(op1_loc));
-    return;
-  }
-
-  CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-  CpuRegister op2 = op2_loc.AsRegister<CpuRegister>();
-
-  //  (out := op1)
-  //  out <=? op2
-  //  if out is min jmp done
-  //  out := op2
-  // done:
-
-  if (is_long) {
-    __ cmpq(out, op2);
-  } else {
-    __ cmpl(out, op2);
-  }
-
-  __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, is_long);
-}
-
-static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
-  LocationSummary* locations =
-      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMinIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMinIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMinLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMinLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
-  CreateIntIntToIntLocations(allocator_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetAssembler());
-}
-
 static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
   LocationSummary* locations =
       new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 8994963..d3b081e 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -334,29 +334,14 @@
 // Detect reductions of the following forms,
 //   x = x_phi + ..
 //   x = x_phi - ..
-//   x = max(x_phi, ..)
 //   x = min(x_phi, ..)
+//   x = max(x_phi, ..)
 static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) {
-  if (reduction->IsAdd()) {
+  if (reduction->IsAdd() || reduction->IsMin() || reduction->IsMax()) {
     return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) ||
            (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi);
   } else if (reduction->IsSub()) {
     return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi);
-  } else if (reduction->IsInvokeStaticOrDirect()) {
-    switch (reduction->AsInvokeStaticOrDirect()->GetIntrinsic()) {
-      case Intrinsics::kMathMinIntInt:
-      case Intrinsics::kMathMinLongLong:
-      case Intrinsics::kMathMinFloatFloat:
-      case Intrinsics::kMathMinDoubleDouble:
-      case Intrinsics::kMathMaxIntInt:
-      case Intrinsics::kMathMaxLongLong:
-      case Intrinsics::kMathMaxFloatFloat:
-      case Intrinsics::kMathMaxDoubleDouble:
-        return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) ||
-               (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi);
-      default:
-        return false;
-    }
   }
   return false;
 }
@@ -1297,80 +1282,59 @@
         return true;
       }
     }
-  } else if (instruction->IsInvokeStaticOrDirect()) {
-    // Accept particular intrinsics.
-    HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect();
-    switch (invoke->GetIntrinsic()) {
-      case Intrinsics::kMathAbsInt:
-      case Intrinsics::kMathAbsLong:
-      case Intrinsics::kMathAbsFloat:
-      case Intrinsics::kMathAbsDouble: {
-        // Deal with vector restrictions.
-        HInstruction* opa = instruction->InputAt(0);
-        HInstruction* r = opa;
-        bool is_unsigned = false;
-        if (HasVectorRestrictions(restrictions, kNoAbs)) {
-          return false;
-        } else if (HasVectorRestrictions(restrictions, kNoHiBits) &&
-                   (!IsNarrowerOperand(opa, type, &r, &is_unsigned) || is_unsigned)) {
-          return false;  // reject, unless operand is sign-extension narrower
-        }
-        // Accept ABS(x) for vectorizable operand.
-        DCHECK(r != nullptr);
-        if (generate_code && vector_mode_ != kVector) {  // de-idiom
-          r = opa;
-        }
-        if (VectorizeUse(node, r, generate_code, type, restrictions)) {
-          if (generate_code) {
-            GenerateVecOp(instruction,
-                          vector_map_->Get(r),
-                          nullptr,
-                          HVecOperation::ToProperType(type, is_unsigned));
-          }
-          return true;
-        }
-        return false;
+  } else if (instruction->IsAbs()) {
+    // Deal with vector restrictions.
+    HInstruction* opa = instruction->InputAt(0);
+    HInstruction* r = opa;
+    bool is_unsigned = false;
+    if (HasVectorRestrictions(restrictions, kNoAbs)) {
+      return false;
+    } else if (HasVectorRestrictions(restrictions, kNoHiBits) &&
+               (!IsNarrowerOperand(opa, type, &r, &is_unsigned) || is_unsigned)) {
+      return false;  // reject, unless operand is sign-extension narrower
+    }
+    // Accept ABS(x) for vectorizable operand.
+    DCHECK(r != nullptr);
+    if (generate_code && vector_mode_ != kVector) {  // de-idiom
+      r = opa;
+    }
+    if (VectorizeUse(node, r, generate_code, type, restrictions)) {
+      if (generate_code) {
+        GenerateVecOp(instruction,
+                      vector_map_->Get(r),
+                      nullptr,
+                      HVecOperation::ToProperType(type, is_unsigned));
       }
-      case Intrinsics::kMathMinIntInt:
-      case Intrinsics::kMathMinLongLong:
-      case Intrinsics::kMathMinFloatFloat:
-      case Intrinsics::kMathMinDoubleDouble:
-      case Intrinsics::kMathMaxIntInt:
-      case Intrinsics::kMathMaxLongLong:
-      case Intrinsics::kMathMaxFloatFloat:
-      case Intrinsics::kMathMaxDoubleDouble: {
-        // Deal with vector restrictions.
-        HInstruction* opa = instruction->InputAt(0);
-        HInstruction* opb = instruction->InputAt(1);
-        HInstruction* r = opa;
-        HInstruction* s = opb;
-        bool is_unsigned = false;
-        if (HasVectorRestrictions(restrictions, kNoMinMax)) {
-          return false;
-        } else if (HasVectorRestrictions(restrictions, kNoHiBits) &&
-                   !IsNarrowerOperands(opa, opb, type, &r, &s, &is_unsigned)) {
-          return false;  // reject, unless all operands are same-extension narrower
-        }
-        // Accept MIN/MAX(x, y) for vectorizable operands.
-        DCHECK(r != nullptr);
-        DCHECK(s != nullptr);
-        if (generate_code && vector_mode_ != kVector) {  // de-idiom
-          r = opa;
-          s = opb;
-        }
-        if (VectorizeUse(node, r, generate_code, type, restrictions) &&
-            VectorizeUse(node, s, generate_code, type, restrictions)) {
-          if (generate_code) {
-            GenerateVecOp(
-                instruction, vector_map_->Get(r), vector_map_->Get(s), type, is_unsigned);
-          }
-          return true;
-        }
-        return false;
+      return true;
+    }
+  } else if (instruction->IsMin() || instruction->IsMax()) {
+    // Deal with vector restrictions.
+    HInstruction* opa = instruction->InputAt(0);
+    HInstruction* opb = instruction->InputAt(1);
+    HInstruction* r = opa;
+    HInstruction* s = opb;
+    bool is_unsigned = false;
+    if (HasVectorRestrictions(restrictions, kNoMinMax)) {
+      return false;
+    } else if (HasVectorRestrictions(restrictions, kNoHiBits) &&
+               !IsNarrowerOperands(opa, opb, type, &r, &s, &is_unsigned)) {
+      return false;  // reject, unless all operands are same-extension narrower
+    }
+    // Accept MIN/MAX(x, y) for vectorizable operands.
+    DCHECK(r != nullptr);
+    DCHECK(s != nullptr);
+    if (generate_code && vector_mode_ != kVector) {  // de-idiom
+      r = opa;
+      s = opb;
+    }
+    if (VectorizeUse(node, r, generate_code, type, restrictions) &&
+        VectorizeUse(node, s, generate_code, type, restrictions)) {
+      if (generate_code) {
+        GenerateVecOp(
+            instruction, vector_map_->Get(r), vector_map_->Get(s), type, is_unsigned);
       }
-      default:
-        return false;
-    }  // switch
+      return true;
+    }
   }
   return false;
 }
@@ -1811,83 +1775,29 @@
       GENERATE_VEC(
         new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_, dex_pc),
         new (global_allocator_) HUShr(org_type, opa, opb, dex_pc));
-    case HInstruction::kInvokeStaticOrDirect: {
-      HInvokeStaticOrDirect* invoke = org->AsInvokeStaticOrDirect();
-      if (vector_mode_ == kVector) {
-        switch (invoke->GetIntrinsic()) {
-          case Intrinsics::kMathAbsInt:
-          case Intrinsics::kMathAbsLong:
-          case Intrinsics::kMathAbsFloat:
-          case Intrinsics::kMathAbsDouble:
-            DCHECK(opb == nullptr);
-            vector = new (global_allocator_)
-                HVecAbs(global_allocator_, opa, type, vector_length_, dex_pc);
-            break;
-          case Intrinsics::kMathMinIntInt:
-          case Intrinsics::kMathMinLongLong:
-          case Intrinsics::kMathMinFloatFloat:
-          case Intrinsics::kMathMinDoubleDouble: {
-            vector = new (global_allocator_)
-                HVecMin(global_allocator_,
-                        opa,
-                        opb,
-                        HVecOperation::ToProperType(type, is_unsigned),
-                        vector_length_,
-                        dex_pc);
-            break;
-          }
-          case Intrinsics::kMathMaxIntInt:
-          case Intrinsics::kMathMaxLongLong:
-          case Intrinsics::kMathMaxFloatFloat:
-          case Intrinsics::kMathMaxDoubleDouble: {
-            vector = new (global_allocator_)
-                HVecMax(global_allocator_,
-                        opa,
-                        opb,
-                        HVecOperation::ToProperType(type, is_unsigned),
-                        vector_length_,
-                        dex_pc);
-            break;
-          }
-          default:
-            LOG(FATAL) << "Unsupported SIMD intrinsic " << org->GetId();
-            UNREACHABLE();
-        }  // switch invoke
-      } else {
-        // In scalar code, simply clone the method invoke, and replace its operands with the
-        // corresponding new scalar instructions in the loop. The instruction will get an
-        // environment while being inserted from the instruction map in original program order.
-        DCHECK(vector_mode_ == kSequential);
-        size_t num_args = invoke->GetNumberOfArguments();
-        HInvokeStaticOrDirect* new_invoke = new (global_allocator_) HInvokeStaticOrDirect(
-            global_allocator_,
-            num_args,
-            invoke->GetType(),
-            invoke->GetDexPc(),
-            invoke->GetDexMethodIndex(),
-            invoke->GetResolvedMethod(),
-            invoke->GetDispatchInfo(),
-            invoke->GetInvokeType(),
-            invoke->GetTargetMethod(),
-            invoke->GetClinitCheckRequirement());
-        HInputsRef inputs = invoke->GetInputs();
-        size_t num_inputs = inputs.size();
-        DCHECK_LE(num_args, num_inputs);
-        DCHECK_EQ(num_inputs, new_invoke->GetInputs().size());  // both invokes agree
-        for (size_t index = 0; index < num_inputs; ++index) {
-          HInstruction* new_input = index < num_args
-              ? vector_map_->Get(inputs[index])
-              : inputs[index];  // beyond arguments: just pass through
-          new_invoke->SetArgumentAt(index, new_input);
-        }
-        new_invoke->SetIntrinsic(invoke->GetIntrinsic(),
-                                 kNeedsEnvironmentOrCache,
-                                 kNoSideEffects,
-                                 kNoThrow);
-        vector = new_invoke;
-      }
-      break;
-    }
+    case HInstruction::kMin:
+      GENERATE_VEC(
+        new (global_allocator_) HVecMin(global_allocator_,
+                                        opa,
+                                        opb,
+                                        HVecOperation::ToProperType(type, is_unsigned),
+                                        vector_length_,
+                                        dex_pc),
+        new (global_allocator_) HMin(org_type, opa, opb, dex_pc));
+    case HInstruction::kMax:
+      GENERATE_VEC(
+        new (global_allocator_) HVecMax(global_allocator_,
+                                        opa,
+                                        opb,
+                                        HVecOperation::ToProperType(type, is_unsigned),
+                                        vector_length_,
+                                        dex_pc),
+        new (global_allocator_) HMax(org_type, opa, opb, dex_pc));
+    case HInstruction::kAbs:
+      DCHECK(opb == nullptr);
+      GENERATE_VEC(
+        new (global_allocator_) HVecAbs(global_allocator_, opa, type, vector_length_, dex_pc),
+        new (global_allocator_) HAbs(org_type, opa, dex_pc));
     default:
       break;
   }  // switch
@@ -1998,9 +1908,7 @@
   HInstruction* v = instruction->InputAt(1);
   HInstruction* a = nullptr;
   HInstruction* b = nullptr;
-  if (v->IsInvokeStaticOrDirect() &&
-       (v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsInt ||
-        v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsLong)) {
+  if (v->GetType() == reduction_type && v->IsAbs()) {
     HInstruction* x = v->InputAt(0);
     if (x->GetType() == reduction_type) {
       int64_t c = 0;
@@ -2054,14 +1962,13 @@
       VectorizeUse(node, r, generate_code, sub_type, restrictions) &&
       VectorizeUse(node, s, generate_code, sub_type, restrictions)) {
     if (generate_code) {
-      reduction_type = HVecOperation::ToProperType(reduction_type, is_unsigned);
       if (vector_mode_ == kVector) {
         vector_map_->Put(instruction, new (global_allocator_) HVecSADAccumulate(
             global_allocator_,
             vector_map_->Get(q),
             vector_map_->Get(r),
             vector_map_->Get(s),
-            reduction_type,
+            HVecOperation::ToProperType(reduction_type, is_unsigned),
             GetOtherVL(reduction_type, sub_type, vector_length_),
             kNoDexPc));
         MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom);
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index db83689..c21bd65 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -227,11 +227,14 @@
   graph_->ClearDominanceInformation();
   graph_->BuildDominatorTree();
 
+  // BuildDominatorTree inserts a block beetween loop header and entry block.
+  EXPECT_EQ(header->GetPredecessors()[0]->GetSinglePredecessor(), entry_block_);
+
   // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs
   // are still mapped correctly to the block predecessors.
   for (size_t i = 0, e = phi->InputCount(); i < e; i++) {
     HInstruction* input = phi->InputAt(i);
-    ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i]));
+    EXPECT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i]));
   }
 }
 
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index f6ba19f..d3212cb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2891,6 +2891,8 @@
       return os << "BootImageLinkTimePcRelative";
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       return os << "DirectAddress";
+    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo:
+      return os << "BootImageRelRo";
     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry:
       return os << "BssEntry";
     case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall:
@@ -2925,7 +2927,7 @@
   }
   switch (GetLoadKind()) {
     case LoadKind::kBootImageAddress:
-    case LoadKind::kBootImageClassTable:
+    case LoadKind::kBootImageRelRo:
     case LoadKind::kJitTableAddress: {
       ScopedObjectAccess soa(Thread::Current());
       return GetClass().Get() == other_load_class->GetClass().Get();
@@ -2944,8 +2946,8 @@
       return os << "BootImageLinkTimePcRelative";
     case HLoadClass::LoadKind::kBootImageAddress:
       return os << "BootImageAddress";
-    case HLoadClass::LoadKind::kBootImageClassTable:
-      return os << "BootImageClassTable";
+    case HLoadClass::LoadKind::kBootImageRelRo:
+      return os << "BootImageRelRo";
     case HLoadClass::LoadKind::kBssEntry:
       return os << "BssEntry";
     case HLoadClass::LoadKind::kJitTableAddress:
@@ -2968,7 +2970,7 @@
   }
   switch (GetLoadKind()) {
     case LoadKind::kBootImageAddress:
-    case LoadKind::kBootImageInternTable:
+    case LoadKind::kBootImageRelRo:
     case LoadKind::kJitTableAddress: {
       ScopedObjectAccess soa(Thread::Current());
       return GetString().Get() == other_load_string->GetString().Get();
@@ -2984,8 +2986,8 @@
       return os << "BootImageLinkTimePcRelative";
     case HLoadString::LoadKind::kBootImageAddress:
       return os << "BootImageAddress";
-    case HLoadString::LoadKind::kBootImageInternTable:
-      return os << "BootImageInternTable";
+    case HLoadString::LoadKind::kBootImageRelRo:
+      return os << "BootImageRelRo";
     case HLoadString::LoadKind::kBssEntry:
       return os << "BssEntry";
     case HLoadString::LoadKind::kJitTableAddress:
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 99d80d7..a8364e0 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
 #include "base/arena_object.h"
 #include "base/array_ref.h"
 #include "base/iteration_range.h"
+#include "base/quasi_atomic.h"
 #include "base/stl_util.h"
 #include "base/transform_array_ref.h"
 #include "data_type.h"
@@ -1383,7 +1384,9 @@
   M(LoadException, Instruction)                                         \
   M(LoadString, Instruction)                                            \
   M(LongConstant, Constant)                                             \
+  M(Max, Instruction)                                                   \
   M(MemoryBarrier, Instruction)                                         \
+  M(Min, BinaryOperation)                                               \
   M(MonitorOperation, Instruction)                                      \
   M(Mul, BinaryOperation)                                               \
   M(NativeDebugInfo, Instruction)                                       \
@@ -4428,6 +4431,10 @@
     // Used for app->boot calls with non-relocatable image and for JIT-compiled calls.
     kDirectAddress,
 
+    // Load from an entry in the .data.bimg.rel.ro using a PC-relative load.
+    // Used for app->boot calls with relocatable image.
+    kBootImageRelRo,
+
     // Load from an entry in the .bss section using a PC-relative load.
     // Used for classes outside boot image when .bss is accessible with a PC-relative load.
     kBssEntry,
@@ -4560,6 +4567,7 @@
   bool HasMethodAddress() const { return GetMethodLoadKind() == MethodLoadKind::kDirectAddress; }
   bool HasPcRelativeMethodLoadKind() const {
     return GetMethodLoadKind() == MethodLoadKind::kBootImageLinkTimePcRelative ||
+           GetMethodLoadKind() == MethodLoadKind::kBootImageRelRo ||
            GetMethodLoadKind() == MethodLoadKind::kBssEntry;
   }
   bool HasCurrentMethodInput() const {
@@ -5016,6 +5024,76 @@
   DEFAULT_COPY_CONSTRUCTOR(Rem);
 };
 
+class HMin FINAL : public HBinaryOperation {
+ public:
+  HMin(DataType::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc)
+      : HBinaryOperation(kMin, result_type, left, right, SideEffects::None(), dex_pc) {}
+
+  bool IsCommutative() const OVERRIDE { return true; }
+
+  // Evaluation for integral values.
+  template <typename T> static T ComputeIntegral(T x, T y) {
+    return (x <= y) ? x : y;
+  }
+
+  HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+    return GetBlock()->GetGraph()->GetIntConstant(
+        ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
+  }
+  HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
+    return GetBlock()->GetGraph()->GetLongConstant(
+        ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
+  }
+  // TODO: Evaluation for floating-point values.
+  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+                      HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; }
+  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+                      HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; }
+
+  DECLARE_INSTRUCTION(Min);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Min);
+};
+
+class HMax FINAL : public HBinaryOperation {
+ public:
+  HMax(DataType::Type result_type,
+       HInstruction* left,
+       HInstruction* right,
+       uint32_t dex_pc)
+      : HBinaryOperation(kMax, result_type, left, right, SideEffects::None(), dex_pc) {}
+
+  bool IsCommutative() const OVERRIDE { return true; }
+
+  // Evaluation for integral values.
+  template <typename T> static T ComputeIntegral(T x, T y) {
+    return (x >= y) ? x : y;
+  }
+
+  HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+    return GetBlock()->GetGraph()->GetIntConstant(
+        ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
+  }
+  HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
+    return GetBlock()->GetGraph()->GetLongConstant(
+        ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
+  }
+  // TODO: Evaluation for floating-point values.
+  HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+                      HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; }
+  HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+                      HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; }
+
+  DECLARE_INSTRUCTION(Max);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Max);
+};
+
 class HAbs FINAL : public HUnaryOperation {
  public:
   HAbs(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
@@ -6066,12 +6144,12 @@
     kBootImageLinkTimePcRelative,
 
     // Use a known boot image Class* address, embedded in the code by the codegen.
-    // Used for boot image classes referenced by apps in AOT- and JIT-compiled code.
+    // Used for boot image classes referenced by apps in JIT- and AOT-compiled code (non-PIC).
     kBootImageAddress,
 
-    // Use a PC-relative load from a boot image ClassTable mmapped into the .bss
-    // of the oat file.
-    kBootImageClassTable,
+    // Load from an entry in the .data.bimg.rel.ro using a PC-relative load.
+    // Used for boot image classes referenced by apps in AOT-compiled code (PIC).
+    kBootImageRelRo,
 
     // Load from an entry in the .bss section using a PC-relative load.
     // Used for classes outside boot image when .bss is accessible with a PC-relative load.
@@ -6119,6 +6197,12 @@
     return GetPackedField<LoadKindField>();
   }
 
+  bool HasPcRelativeLoadKind() const {
+    return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
+           GetLoadKind() == LoadKind::kBootImageRelRo ||
+           GetLoadKind() == LoadKind::kBssEntry;
+  }
+
   bool CanBeMoved() const OVERRIDE { return true; }
 
   bool InstructionDataEquals(const HInstruction* other) const;
@@ -6223,7 +6307,6 @@
   static bool HasTypeReference(LoadKind load_kind) {
     return load_kind == LoadKind::kReferrersClass ||
         load_kind == LoadKind::kBootImageLinkTimePcRelative ||
-        load_kind == LoadKind::kBootImageClassTable ||
         load_kind == LoadKind::kBssEntry ||
         load_kind == LoadKind::kRuntimeCall;
   }
@@ -6269,7 +6352,7 @@
   // including literal pool loads, which are PC-relative too.
   DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
          GetLoadKind() == LoadKind::kBootImageAddress ||
-         GetLoadKind() == LoadKind::kBootImageClassTable ||
+         GetLoadKind() == LoadKind::kBootImageRelRo ||
          GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind();
   DCHECK(special_input_.GetInstruction() == nullptr);
   special_input_ = HUserRecord<HInstruction*>(special_input);
@@ -6285,12 +6368,12 @@
     kBootImageLinkTimePcRelative,
 
     // Use a known boot image String* address, embedded in the code by the codegen.
-    // Used for boot image strings referenced by apps in AOT- and JIT-compiled code.
+    // Used for boot image strings referenced by apps in JIT- and AOT-compiled code (non-PIC).
     kBootImageAddress,
 
-    // Use a PC-relative load from a boot image InternTable mmapped into the .bss
-    // of the oat file.
-    kBootImageInternTable,
+    // Load from an entry in the .data.bimg.rel.ro using a PC-relative load.
+    // Used for boot image strings referenced by apps in AOT-compiled code (PIC).
+    kBootImageRelRo,
 
     // Load from an entry in the .bss section using a PC-relative load.
     // Used for strings outside boot image when .bss is accessible with a PC-relative load.
@@ -6325,6 +6408,12 @@
     return GetPackedField<LoadKindField>();
   }
 
+  bool HasPcRelativeLoadKind() const {
+    return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
+           GetLoadKind() == LoadKind::kBootImageRelRo ||
+           GetLoadKind() == LoadKind::kBssEntry;
+  }
+
   const DexFile& GetDexFile() const {
     return dex_file_;
   }
@@ -6353,7 +6442,7 @@
     LoadKind load_kind = GetLoadKind();
     if (load_kind == LoadKind::kBootImageLinkTimePcRelative ||
         load_kind == LoadKind::kBootImageAddress ||
-        load_kind == LoadKind::kBootImageInternTable ||
+        load_kind == LoadKind::kBootImageRelRo ||
         load_kind == LoadKind::kJitTableAddress) {
       return false;
     }
@@ -6431,7 +6520,7 @@
   // including literal pool loads, which are PC-relative too.
   DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
          GetLoadKind() == LoadKind::kBootImageAddress ||
-         GetLoadKind() == LoadKind::kBootImageInternTable ||
+         GetLoadKind() == LoadKind::kBootImageRelRo ||
          GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind();
   // HLoadString::GetInputRecords() returns an empty array at this point,
   // so use the GetInputRecords() from the base class to set the input record.
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 0023265..00194ff 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -22,9 +22,9 @@
 #include <string>
 #include <type_traits>
 
-#include "atomic.h"
+#include "base/atomic.h"
+#include "base/globals.h"
 #include "base/logging.h"  // For VLOG_IS_ON.
-#include "globals.h"
 
 namespace art {
 
diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc
index 9d53585..0102254 100644
--- a/compiler/optimizing/pc_relative_fixups_mips.cc
+++ b/compiler/optimizing/pc_relative_fixups_mips.cc
@@ -75,7 +75,7 @@
     switch (load_kind) {
       case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
       case HLoadClass::LoadKind::kBootImageAddress:
-      case HLoadClass::LoadKind::kBootImageClassTable:
+      case HLoadClass::LoadKind::kBootImageRelRo:
       case HLoadClass::LoadKind::kBssEntry:
         // Add a base register for PC-relative literals on R2.
         InitializePCRelativeBasePointer();
@@ -91,7 +91,7 @@
     switch (load_kind) {
       case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
       case HLoadString::LoadKind::kBootImageAddress:
-      case HLoadString::LoadKind::kBootImageInternTable:
+      case HLoadString::LoadKind::kBootImageRelRo:
       case HLoadString::LoadKind::kBssEntry:
         // Add a base register for PC-relative literals on R2.
         InitializePCRelativeBasePointer();
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index a3ca631..647336b 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -81,20 +81,14 @@
   }
 
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
-    HLoadClass::LoadKind load_kind = load_class->GetLoadKind();
-    if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
-        load_kind == HLoadClass::LoadKind::kBootImageClassTable ||
-        load_kind == HLoadClass::LoadKind::kBssEntry) {
+    if (load_class->HasPcRelativeLoadKind()) {
       HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_class);
       load_class->AddSpecialInput(method_address);
     }
   }
 
   void VisitLoadString(HLoadString* load_string) OVERRIDE {
-    HLoadString::LoadKind load_kind = load_string->GetLoadKind();
-    if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
-        load_kind == HLoadString::LoadKind::kBootImageInternTable ||
-        load_kind == HLoadString::LoadKind::kBssEntry) {
+    if (load_string->HasPcRelativeLoadKind()) {
       HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_string);
       load_string->AddSpecialInput(method_address);
     }
@@ -234,12 +228,13 @@
     switch (invoke->GetIntrinsic()) {
       case Intrinsics::kMathAbsDouble:
       case Intrinsics::kMathAbsFloat:
-        LOG(FATAL) << "Unreachable abs";
-        UNREACHABLE();
       case Intrinsics::kMathMaxDoubleDouble:
       case Intrinsics::kMathMaxFloatFloat:
       case Intrinsics::kMathMinDoubleDouble:
       case Intrinsics::kMathMinFloatFloat:
+        LOG(FATAL) << "Unreachable min/max/abs: intrinsics should have been lowered "
+                      "to IR nodes by instruction simplifier";
+        UNREACHABLE();
       case Intrinsics::kMathRoundFloat:
         if (!base_added) {
           DCHECK(invoke_static_or_direct != nullptr);
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index cdbe483..bca538f 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -679,6 +679,8 @@
            instruction->IsCompare() ||
            instruction->IsCondition() ||
            instruction->IsDiv() ||
+           instruction->IsMin() ||
+           instruction->IsMax() ||
            instruction->IsMul() ||
            instruction->IsOr() ||
            instruction->IsRem() ||
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 1e49411..7dffb2a 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -125,8 +125,12 @@
              BootImageAOTCanEmbedMethod(callee, compiler_driver)) {
     method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative;
     code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
+  } else if (IsInBootImage(callee)) {
+    // Use PC-relative access to the .data.bimg.rel.ro methods array.
+    method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo;
+    code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
   } else {
-    // Use PC-relative access to the .bss methods arrays.
+    // Use PC-relative access to the .bss methods array.
     method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBssEntry;
     code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
   }
@@ -207,7 +211,7 @@
       } else if (is_in_boot_image) {
         // AOT app compilation, boot image class.
         if (codegen->GetCompilerOptions().GetCompilePic()) {
-          desired_load_kind = HLoadClass::LoadKind::kBootImageClassTable;
+          desired_load_kind = HLoadClass::LoadKind::kBootImageRelRo;
         } else {
           desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
         }
@@ -288,7 +292,7 @@
       string = class_linker->LookupString(string_index, dex_cache.Get());
       if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
         if (codegen->GetCompilerOptions().GetCompilePic()) {
-          desired_load_kind = HLoadString::LoadKind::kBootImageInternTable;
+          desired_load_kind = HLoadString::LoadKind::kBootImageRelRo;
         } else {
           desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
         }
diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc
index a7c23be..04942f9 100644
--- a/compiler/optimizing/superblock_cloner.cc
+++ b/compiler/optimizing/superblock_cloner.cc
@@ -28,6 +28,11 @@
 using HBasicBlockSet = SuperblockCloner::HBasicBlockSet;
 using HEdgeSet = SuperblockCloner::HEdgeSet;
 
+// When doing peeling we can choose whether to keep original loop (made of original basic blocks)
+// and form a peeled iteration of the copy blocks (preserve the header) or transfer original loop
+// blocks to the peeled iteration and create new loop from the copy blocks. Similar for unrolling.
+static const bool kPeelUnrollPreserveHeader = true;
+
 void HEdge::Dump(std::ostream& stream) const {
   stream << "(" << from_ << "->" << to_ << ")";
 }
@@ -70,20 +75,18 @@
   return true;
 }
 
-// Returns a common predecessor of loop1 and loop2 in the loop tree or nullptr if it is the whole
-// graph.
-static HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) {
-  if (loop1 != nullptr || loop2 != nullptr) {
-    return nullptr;
+// Returns whether two Edge sets are equal (ArenaHashSet doesn't have "Equal" method).
+static bool EdgeHashSetsEqual(const HEdgeSet* set1, const HEdgeSet* set2) {
+  if (set1->Size() != set2->Size()) {
+    return false;
   }
 
-  if (loop1->IsIn(*loop2)) {
-    return loop2;
-  } else if (loop2->IsIn(*loop1)) {
-    return loop1;
+  for (auto e : *set1) {
+    if (set2->Find(e) == set2->end()) {
+      return false;
+    }
   }
-  HBasicBlock* block = CommonDominator::ForPair(loop1->GetHeader(), loop2->GetHeader());
-  return block->GetLoopInformation();
+  return true;
 }
 
 // Calls HGraph::OrderLoopHeaderPredecessors for each loop in the graph.
@@ -95,6 +98,21 @@
   }
 }
 
+// Performs DFS on the subgraph (specified by 'bb_set') starting from the specified block; while
+// traversing the function removes basic blocks from the bb_set (instead of traditional DFS
+// 'marking'). So what is left in the 'bb_set' after the traversal is not reachable from the start
+// block.
+static void TraverseSubgraphForConnectivity(HBasicBlock* block, HBasicBlockSet* bb_set) {
+  DCHECK(bb_set->IsBitSet(block->GetBlockId()));
+  bb_set->ClearBit(block->GetBlockId());
+
+  for (HBasicBlock* succ : block->GetSuccessors()) {
+    if (bb_set->IsBitSet(succ->GetBlockId())) {
+      TraverseSubgraphForConnectivity(succ, bb_set);
+    }
+  }
+}
+
 //
 // Helpers for CloneBasicBlock.
 //
@@ -268,7 +286,6 @@
 }
 
 void SuperblockCloner::RecalculateBackEdgesInfo(ArenaBitVector* outer_loop_bb_set) {
-  // TODO: DCHECK that after the transformation the graph is connected.
   HBasicBlock* block_entry = nullptr;
 
   if (outer_loop_ == nullptr) {
@@ -424,6 +441,11 @@
       outer_loop_ = nullptr;
       break;
     }
+    if (outer_loop_ == nullptr) {
+      // We should not use the initial outer_loop_ value 'nullptr' when finding the most outer
+      // common loop.
+      outer_loop_ = loop_exit_loop_info;
+    }
     outer_loop_ = FindCommonLoop(outer_loop_, loop_exit_loop_info);
   }
 
@@ -507,6 +529,34 @@
 // Debug and logging methods.
 //
 
+// Debug function to dump graph' BasicBlocks info.
+void DumpBB(HGraph* graph) {
+  for (HBasicBlock* bb : graph->GetBlocks()) {
+    if (bb == nullptr) {
+      continue;
+    }
+    std::cout << bb->GetBlockId();
+    std::cout << " <- ";
+    for (HBasicBlock* pred : bb->GetPredecessors()) {
+      std::cout << pred->GetBlockId() << " ";
+    }
+    std::cout << " -> ";
+    for (HBasicBlock* succ : bb->GetSuccessors()) {
+      std::cout << succ->GetBlockId()  << " ";
+    }
+
+    if (bb->GetDominator()) {
+      std::cout << " dom " << bb->GetDominator()->GetBlockId();
+    }
+
+    if (bb->GetLoopInformation()) {
+      std::cout <<  "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId();
+    }
+
+    std::cout << std::endl;
+  }
+}
+
 void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) {
   DCHECK(!orig_instr->IsPhi());
   HInstruction* copy_instr = GetInstrCopy(orig_instr);
@@ -542,6 +592,82 @@
   }
 }
 
+bool SuperblockCloner::CheckRemappingInfoIsValid() {
+  for (HEdge edge : *remap_orig_internal_) {
+    if (!IsEdgeValid(edge, graph_) ||
+        !IsInOrigBBSet(edge.GetFrom()) ||
+        !IsInOrigBBSet(edge.GetTo())) {
+      return false;
+    }
+  }
+
+  for (auto edge : *remap_copy_internal_) {
+    if (!IsEdgeValid(edge, graph_) ||
+        !IsInOrigBBSet(edge.GetFrom()) ||
+        !IsInOrigBBSet(edge.GetTo())) {
+      return false;
+    }
+  }
+
+  for (auto edge : *remap_incoming_) {
+    if (!IsEdgeValid(edge, graph_) ||
+        IsInOrigBBSet(edge.GetFrom()) ||
+        !IsInOrigBBSet(edge.GetTo())) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void SuperblockCloner::VerifyGraph() {
+  for (auto it : *hir_map_) {
+    HInstruction* orig_instr = it.first;
+    HInstruction* copy_instr = it.second;
+    if (!orig_instr->IsPhi() && !orig_instr->IsSuspendCheck()) {
+      DCHECK(it.first->GetBlock() != nullptr);
+    }
+    if (!copy_instr->IsPhi() && !copy_instr->IsSuspendCheck()) {
+      DCHECK(it.second->GetBlock() != nullptr);
+    }
+  }
+
+  GraphChecker checker(graph_);
+  checker.Run();
+  if (!checker.IsValid()) {
+    for (const std::string& error : checker.GetErrors()) {
+      std::cout << error << std::endl;
+    }
+    LOG(FATAL) << "GraphChecker failed: superblock cloner\n";
+  }
+}
+
+void DumpBBSet(const ArenaBitVector* set) {
+  for (uint32_t idx : set->Indexes()) {
+    std::cout << idx << "\n";
+  }
+}
+
+void SuperblockCloner::DumpInputSets() {
+  std::cout << graph_->PrettyMethod() << "\n";
+  std::cout << "orig_bb_set:\n";
+  for (uint32_t idx : orig_bb_set_.Indexes()) {
+    std::cout << idx << "\n";
+  }
+  std::cout << "remap_orig_internal:\n";
+  for (HEdge e : *remap_orig_internal_) {
+    std::cout << e << "\n";
+  }
+  std::cout << "remap_copy_internal:\n";
+  for (auto e : *remap_copy_internal_) {
+    std::cout << e << "\n";
+  }
+  std::cout << "remap_incoming:\n";
+  for (auto e : *remap_incoming_) {
+    std::cout << e << "\n";
+  }
+}
+
 //
 // Public methods.
 //
@@ -569,6 +695,7 @@
   remap_orig_internal_ = remap_orig_internal;
   remap_copy_internal_ = remap_copy_internal;
   remap_incoming_ = remap_incoming;
+  DCHECK(CheckRemappingInfoIsValid());
 }
 
 bool SuperblockCloner::IsSubgraphClonable() const {
@@ -602,6 +729,63 @@
   return true;
 }
 
+bool SuperblockCloner::IsFastCase() const {
+  // Check that loop unrolling/loop peeling is being conducted.
+  // Check that all the basic blocks belong to the same loop.
+  bool flag = false;
+  HLoopInformation* common_loop_info = nullptr;
+  for (uint32_t idx : orig_bb_set_.Indexes()) {
+    HBasicBlock* block = GetBlockById(idx);
+    HLoopInformation* block_loop_info = block->GetLoopInformation();
+    if (!flag) {
+      common_loop_info = block_loop_info;
+    } else {
+      if (block_loop_info != common_loop_info) {
+        return false;
+      }
+    }
+  }
+
+  // Check that orig_bb_set_ corresponds to loop peeling/unrolling.
+  if (common_loop_info == nullptr || !orig_bb_set_.SameBitsSet(&common_loop_info->GetBlocks())) {
+    return false;
+  }
+
+  bool peeling_or_unrolling = false;
+  HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+
+
+  // Check whether remapping info corresponds to loop unrolling.
+  CollectRemappingInfoForPeelUnroll(/* to_unroll*/ true,
+                                    common_loop_info,
+                                    &remap_orig_internal,
+                                    &remap_copy_internal,
+                                    &remap_incoming);
+
+  peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) &&
+                          EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) &&
+                          EdgeHashSetsEqual(&remap_incoming, remap_incoming_);
+
+  remap_orig_internal.Clear();
+  remap_copy_internal.Clear();
+  remap_incoming.Clear();
+
+  // Check whether remapping info corresponds to loop peeling.
+  CollectRemappingInfoForPeelUnroll(/* to_unroll*/ false,
+                                    common_loop_info,
+                                    &remap_orig_internal,
+                                    &remap_copy_internal,
+                                    &remap_incoming);
+
+  peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) &&
+                          EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) &&
+                          EdgeHashSetsEqual(&remap_incoming, remap_incoming_);
+
+  return peeling_or_unrolling;
+}
+
 void SuperblockCloner::Run() {
   DCHECK(bb_map_ != nullptr);
   DCHECK(hir_map_ != nullptr);
@@ -609,6 +793,11 @@
          remap_copy_internal_ != nullptr &&
          remap_incoming_ != nullptr);
   DCHECK(IsSubgraphClonable());
+  DCHECK(IsFastCase());
+
+  if (kSuperblockClonerLogging) {
+    DumpInputSets();
+  }
 
   // Find an area in the graph for which control flow information should be adjusted.
   FindAndSetLocalAreaForAdjustments();
@@ -618,6 +807,19 @@
   // Connect the blocks together/remap successors and fix phis which are directly affected my the
   // remapping.
   RemapEdgesSuccessors();
+
+  // Check that the subgraph is connected.
+  if (kIsDebugBuild) {
+    HBasicBlockSet work_set(arena_, orig_bb_set_.GetSizeOf(), true, kArenaAllocSuperblockCloner);
+
+    // Add original and copy blocks of the subgraph to the work set.
+    for (auto iter : *bb_map_) {
+      work_set.SetBit(iter.first->GetBlockId());   // Original block.
+      work_set.SetBit(iter.second->GetBlockId());  // Copy block.
+    }
+    CHECK(IsSubgraphConnected(&work_set, graph_));
+  }
+
   // Recalculate dominance and backedge information which is required by the next stage.
   AdjustControlFlowInfo();
   // Fix data flow of the graph.
@@ -650,6 +852,10 @@
       }
     }
   }
+
+  if (kSuperblockClonerVerify) {
+    VerifyGraph();
+  }
 }
 
 HBasicBlock* SuperblockCloner::CloneBasicBlock(const HBasicBlock* orig_block) {
@@ -701,4 +907,125 @@
   }
 }
 
+//
+// Stand-alone methods.
+//
+
+void CollectRemappingInfoForPeelUnroll(bool to_unroll,
+                                       HLoopInformation* loop_info,
+                                       HEdgeSet* remap_orig_internal,
+                                       HEdgeSet* remap_copy_internal,
+                                       HEdgeSet* remap_incoming) {
+  DCHECK(loop_info != nullptr);
+  HBasicBlock* loop_header = loop_info->GetHeader();
+  // Set up remap_orig_internal edges set - set is empty.
+  // Set up remap_copy_internal edges set.
+  for (HBasicBlock* back_edge_block : loop_info->GetBackEdges()) {
+    HEdge e = HEdge(back_edge_block, loop_header);
+    if (to_unroll) {
+      remap_orig_internal->Insert(e);
+      remap_copy_internal->Insert(e);
+    } else {
+      if (kPeelUnrollPreserveHeader) {
+        remap_copy_internal->Insert(e);
+      } else {
+        remap_orig_internal->Insert(e);
+      }
+    }
+  }
+
+  // Set up remap_incoming edges set.
+  if (to_unroll != kPeelUnrollPreserveHeader) {
+    remap_incoming->Insert(HEdge(loop_info->GetPreHeader(), loop_header));
+  }
+}
+
+bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph) {
+  ArenaVector<HBasicBlock*> entry_blocks(
+      graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+
+  // Find subgraph entry blocks.
+  for (uint32_t orig_block_id : work_set->Indexes()) {
+    HBasicBlock* block = graph->GetBlocks()[orig_block_id];
+    for (HBasicBlock* pred : block->GetPredecessors()) {
+      if (!work_set->IsBitSet(pred->GetBlockId())) {
+        entry_blocks.push_back(block);
+        break;
+      }
+    }
+  }
+
+  for (HBasicBlock* entry_block : entry_blocks) {
+    if (work_set->IsBitSet(entry_block->GetBlockId())) {
+      TraverseSubgraphForConnectivity(entry_block, work_set);
+    }
+  }
+
+  // Return whether there are unvisited - unreachable - blocks.
+  return work_set->NumSetBits() == 0;
+}
+
+HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) {
+  if (loop1 == nullptr || loop2 == nullptr) {
+    return nullptr;
+  }
+
+  if (loop1->IsIn(*loop2)) {
+    return loop2;
+  }
+
+  HLoopInformation* current = loop1;
+  while (current != nullptr && !loop2->IsIn(*current)) {
+    current = current->GetPreHeader()->GetLoopInformation();
+  }
+
+  return current;
+}
+
+bool PeelUnrollHelper::IsLoopClonable(HLoopInformation* loop_info) {
+  PeelUnrollHelper helper(loop_info, nullptr, nullptr);
+  return helper.IsLoopClonable();
+}
+
+HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) {
+  // For now do peeling only for natural loops.
+  DCHECK(!loop_info_->IsIrreducible());
+
+  HBasicBlock* loop_header = loop_info_->GetHeader();
+  HGraph* graph = loop_header->GetGraph();
+  ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool());
+
+  HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HEdgeSet remap_copy_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HEdgeSet remap_incoming(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+
+  CollectRemappingInfoForPeelUnroll(to_unroll,
+                                    loop_info_,
+                                    &remap_orig_internal,
+                                    &remap_copy_internal,
+                                    &remap_incoming);
+
+  cloner_.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming);
+  cloner_.Run();
+  cloner_.CleanUp();
+
+  return kPeelUnrollPreserveHeader ? loop_header : cloner_.GetBlockCopy(loop_header);
+}
+
+PeelUnrollSimpleHelper::PeelUnrollSimpleHelper(HLoopInformation* info)
+  : bb_map_(std::less<HBasicBlock*>(),
+            info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)),
+    hir_map_(std::less<HInstruction*>(),
+             info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)),
+    helper_(info, &bb_map_, &hir_map_) {}
+
 }  // namespace art
+
+namespace std {
+
+ostream& operator<<(ostream& os, const art::HEdge& e) {
+  e.Dump(os);
+  return os;
+}
+
+}  // namespace std
diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h
index 23de692..19c9dd4 100644
--- a/compiler/optimizing/superblock_cloner.h
+++ b/compiler/optimizing/superblock_cloner.h
@@ -152,6 +152,15 @@
   // TODO: Start from small range of graph patterns then extend it.
   bool IsSubgraphClonable() const;
 
+  // Returns whether selected subgraph satisfies the criteria for fast data flow resolution
+  // when iterative DF algorithm is not required and dominators/instructions inputs can be
+  // trivially adjusted.
+  //
+  // TODO: formally describe the criteria.
+  //
+  // Loop peeling and unrolling satisfy the criteria.
+  bool IsFastCase() const;
+
   // Runs the copy algorithm according to the description.
   void Run();
 
@@ -202,11 +211,17 @@
     return IsInOrigBBSet(block->GetBlockId());
   }
 
+  // Returns the area (the most outer loop) in the graph for which control flow (back edges, loops,
+  // dominators) needs to be adjusted.
+  HLoopInformation* GetRegionToBeAdjusted() const {
+    return outer_loop_;
+  }
+
  private:
   // Fills the 'exits' vector with the subgraph exits.
   void SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits);
 
-  // Finds and records information about the area in the graph for which control-flow (back edges,
+  // Finds and records information about the area in the graph for which control flow (back edges,
   // loops, dominators) needs to be adjusted.
   void FindAndSetLocalAreaForAdjustments();
 
@@ -217,7 +232,7 @@
   // phis' nor instructions' inputs values are resolved.
   void RemapEdgesSuccessors();
 
-  // Adjusts control-flow (back edges, loops, dominators) for the local area defined by
+  // Adjusts control flow (back edges, loops, dominators) for the local area defined by
   // FindAndSetLocalAreaForAdjustments.
   void AdjustControlFlowInfo();
 
@@ -272,6 +287,9 @@
   // Debug and logging methods.
   //
   void CheckInstructionInputsRemapping(HInstruction* orig_instr);
+  bool CheckRemappingInfoIsValid();
+  void VerifyGraph();
+  void DumpInputSets();
 
   HBasicBlock* GetBlockById(uint32_t block_id) const {
     DCHECK(block_id < graph_->GetBlocks().size());
@@ -295,15 +313,94 @@
   HBasicBlockMap* bb_map_;
   // Correspondence map for instructions: (original HInstruction, copy HInstruction).
   HInstructionMap* hir_map_;
-  // Area in the graph for which control-flow (back edges, loops, dominators) needs to be adjusted.
+  // Area in the graph for which control flow (back edges, loops, dominators) needs to be adjusted.
   HLoopInformation* outer_loop_;
   HBasicBlockSet outer_loop_bb_set_;
 
   ART_FRIEND_TEST(SuperblockClonerTest, AdjustControlFlowInfo);
+  ART_FRIEND_TEST(SuperblockClonerTest, IsGraphConnected);
 
   DISALLOW_COPY_AND_ASSIGN(SuperblockCloner);
 };
 
+// Helper class to perform loop peeling/unrolling.
+//
+// This helper should be used when correspondence map between original and copied
+// basic blocks/instructions are demanded.
+class PeelUnrollHelper : public ValueObject {
+ public:
+  explicit PeelUnrollHelper(HLoopInformation* info,
+                            SuperblockCloner::HBasicBlockMap* bb_map,
+                            SuperblockCloner::HInstructionMap* hir_map) :
+      loop_info_(info),
+      cloner_(info->GetHeader()->GetGraph(), &info->GetBlocks(), bb_map, hir_map) {
+    // For now do peeling/unrolling only for natural loops.
+    DCHECK(!info->IsIrreducible());
+  }
+
+  // Returns whether the loop can be peeled/unrolled (static function).
+  static bool IsLoopClonable(HLoopInformation* loop_info);
+
+  // Returns whether the loop can be peeled/unrolled.
+  bool IsLoopClonable() const { return cloner_.IsSubgraphClonable(); }
+
+  HBasicBlock* DoPeeling() { return DoPeelUnrollImpl(/* to_unroll */ false); }
+  HBasicBlock* DoUnrolling() { return DoPeelUnrollImpl(/* to_unroll */ true); }
+  HLoopInformation* GetRegionToBeAdjusted() const { return cloner_.GetRegionToBeAdjusted(); }
+
+ protected:
+  // Applies loop peeling/unrolling for the loop specified by 'loop_info'.
+  //
+  // Depending on 'do_unroll' either unrolls loop by 2 or peels one iteration from it.
+  HBasicBlock* DoPeelUnrollImpl(bool to_unroll);
+
+ private:
+  HLoopInformation* loop_info_;
+  SuperblockCloner cloner_;
+
+  DISALLOW_COPY_AND_ASSIGN(PeelUnrollHelper);
+};
+
+// Helper class to perform loop peeling/unrolling.
+//
+// This helper should be used when there is no need to get correspondence information between
+// original and copied basic blocks/instructions.
+class PeelUnrollSimpleHelper : public ValueObject {
+ public:
+  explicit PeelUnrollSimpleHelper(HLoopInformation* info);
+  bool IsLoopClonable() const { return helper_.IsLoopClonable(); }
+  HBasicBlock* DoPeeling() { return helper_.DoPeeling(); }
+  HBasicBlock* DoUnrolling() { return helper_.DoUnrolling(); }
+  HLoopInformation* GetRegionToBeAdjusted() const { return helper_.GetRegionToBeAdjusted(); }
+
+ private:
+  SuperblockCloner::HBasicBlockMap bb_map_;
+  SuperblockCloner::HInstructionMap hir_map_;
+  PeelUnrollHelper helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(PeelUnrollSimpleHelper);
+};
+
+// Collects edge remapping info for loop peeling/unrolling for the loop specified by loop info.
+void CollectRemappingInfoForPeelUnroll(bool to_unroll,
+                                       HLoopInformation* loop_info,
+                                       SuperblockCloner::HEdgeSet* remap_orig_internal,
+                                       SuperblockCloner::HEdgeSet* remap_copy_internal,
+                                       SuperblockCloner::HEdgeSet* remap_incoming);
+
+// Returns whether blocks from 'work_set' are reachable from the rest of the graph.
+//
+// Returns whether such a set 'outer_entries' of basic blocks exists that:
+// - each block from 'outer_entries' is not from 'work_set'.
+// - each block from 'work_set' is reachable from at least one block from 'outer_entries'.
+//
+// After the function returns work_set contains only blocks from the original 'work_set'
+// which are unreachable from the rest of the graph.
+bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph);
+
+// Returns a common predecessor of loop1 and loop2 in the loop tree or nullptr if it is the whole
+// graph.
+HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2);
 }  // namespace art
 
 namespace std {
@@ -312,11 +409,12 @@
 struct hash<art::HEdge> {
   size_t operator()(art::HEdge const& x) const noexcept  {
     // Use Cantor pairing function as the hash function.
-    uint32_t a = x.GetFrom();
-    uint32_t b = x.GetTo();
+    size_t a = x.GetFrom();
+    size_t b = x.GetTo();
     return (a + b) * (a + b + 1) / 2 + b;
   }
 };
+ostream& operator<<(ostream& os, const art::HEdge& e);
 
 }  // namespace std
 
diff --git a/compiler/optimizing/superblock_cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc
index f1b7bff..df2e517 100644
--- a/compiler/optimizing/superblock_cloner_test.cc
+++ b/compiler/optimizing/superblock_cloner_test.cc
@@ -25,54 +25,67 @@
 
 using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
 using HInstructionMap = SuperblockCloner::HInstructionMap;
+using HBasicBlockSet = SuperblockCloner::HBasicBlockSet;
+using HEdgeSet = SuperblockCloner::HEdgeSet;
 
 // This class provides methods and helpers for testing various cloning and copying routines:
 // individual instruction cloning and cloning of the more coarse-grain structures.
 class SuperblockClonerTest : public OptimizingUnitTest {
  public:
-  SuperblockClonerTest()
-      : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {}
+  SuperblockClonerTest() : graph_(CreateGraph()),
+                           entry_block_(nullptr),
+                           return_block_(nullptr),
+                           exit_block_(nullptr),
+                           parameter_(nullptr) {}
 
-  void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p,
-                                  /* out */ HBasicBlock** body_p) {
+  void InitGraph() {
     entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
     graph_->AddBlock(entry_block_);
     graph_->SetEntryBlock(entry_block_);
 
-    HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
-    HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_);
-
-    graph_->AddBlock(loop_preheader);
-    graph_->AddBlock(loop_header);
-    graph_->AddBlock(loop_body);
-    graph_->AddBlock(loop_exit);
+    return_block_ = new (GetAllocator()) HBasicBlock(graph_);
+    graph_->AddBlock(return_block_);
 
     exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
     graph_->AddBlock(exit_block_);
     graph_->SetExitBlock(exit_block_);
 
-    entry_block_->AddSuccessor(loop_preheader);
-    loop_preheader->AddSuccessor(loop_header);
-    // Loop exit first to have a proper exit condition/target for HIf.
-    loop_header->AddSuccessor(loop_exit);
-    loop_header->AddSuccessor(loop_body);
-    loop_body->AddSuccessor(loop_header);
-    loop_exit->AddSuccessor(exit_block_);
-
-    *header_p = loop_header;
-    *body_p = loop_body;
+    entry_block_->AddSuccessor(return_block_);
+    return_block_->AddSuccessor(exit_block_);
 
     parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
                                                       dex::TypeIndex(0),
                                                       0,
                                                       DataType::Type::kInt32);
     entry_block_->AddInstruction(parameter_);
-    loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid());
+    return_block_->AddInstruction(new (GetAllocator()) HReturnVoid());
     exit_block_->AddInstruction(new (GetAllocator()) HExit());
   }
 
+  void CreateBasicLoopControlFlow(HBasicBlock* position,
+                                  HBasicBlock* successor,
+                                  /* out */ HBasicBlock** header_p,
+                                  /* out */ HBasicBlock** body_p) {
+    HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
+    HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
+    HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
+
+    graph_->AddBlock(loop_preheader);
+    graph_->AddBlock(loop_header);
+    graph_->AddBlock(loop_body);
+
+    position->ReplaceSuccessor(successor, loop_preheader);
+
+    loop_preheader->AddSuccessor(loop_header);
+    // Loop exit first to have a proper exit condition/target for HIf.
+    loop_header->AddSuccessor(successor);
+    loop_header->AddSuccessor(loop_body);
+    loop_body->AddSuccessor(loop_header);
+
+    *header_p = loop_header;
+    *body_p = loop_body;
+  }
+
   void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) {
     uint32_t dex_pc = 0;
 
@@ -84,11 +97,12 @@
     // Header block.
     HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
     HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
+    HInstruction* loop_check = new (GetAllocator()) HGreaterThanOrEqual(phi, const_128);
 
     loop_header->AddPhi(phi);
     loop_header->AddInstruction(suspend_check);
-    loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128));
-    loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_));
+    loop_header->AddInstruction(loop_check);
+    loop_header->AddInstruction(new (GetAllocator()) HIf(loop_check));
 
     // Loop body block.
     HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc);
@@ -97,8 +111,8 @@
     HInstruction* array_get =
         new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc);
     HInstruction* add =  new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1);
-    HInstruction* array_set =
-        new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
+    HInstruction* array_set = new (GetAllocator()) HArraySet(
+        null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
     HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1);
 
     loop_body->AddInstruction(null_check);
@@ -153,6 +167,7 @@
   HGraph* graph_;
 
   HBasicBlock* entry_block_;
+  HBasicBlock* return_block_;
   HBasicBlock* exit_block_;
 
   HInstruction* parameter_;
@@ -162,10 +177,11 @@
   HBasicBlock* header = nullptr;
   HBasicBlock* loop_body = nullptr;
 
-  CreateBasicLoopControlFlow(&header, &loop_body);
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
-  ASSERT_TRUE(CheckGraph());
+  EXPECT_TRUE(CheckGraph());
 
   HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
   CloneAndReplaceInstructionVisitor visitor(graph_);
@@ -193,7 +209,8 @@
   HBasicBlock* loop_body = nullptr;
   ArenaAllocator* arena = graph_->GetAllocator();
 
-  CreateBasicLoopControlFlow(&header, &loop_body);
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
   ASSERT_TRUE(CheckGraph());
@@ -272,7 +289,8 @@
   HBasicBlock* loop_body = nullptr;
   ArenaAllocator* arena = graph_->GetAllocator();
 
-  CreateBasicLoopControlFlow(&header, &loop_body);
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
   CreateBasicLoopDataFlow(header, loop_body);
   graph_->BuildDominatorTree();
   ASSERT_TRUE(CheckGraph());
@@ -303,4 +321,487 @@
   EXPECT_TRUE(loop_info->IsBackEdge(*loop_body));
 }
 
+// Tests IsSubgraphConnected function for negative case.
+TEST_F(SuperblockClonerTest, IsGraphConnected) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+  ArenaAllocator* arena = graph_->GetAllocator();
+
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* unreachable_block = new (GetAllocator()) HBasicBlock(graph_);
+  graph_->AddBlock(unreachable_block);
+
+  HBasicBlockSet bb_set(
+      arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
+  bb_set.SetBit(header->GetBlockId());
+  bb_set.SetBit(loop_body->GetBlockId());
+  bb_set.SetBit(unreachable_block->GetBlockId());
+
+  EXPECT_FALSE(IsSubgraphConnected(&bb_set, graph_));
+  EXPECT_EQ(bb_set.NumSetBits(), 1u);
+  EXPECT_TRUE(bb_set.IsBitSet(unreachable_block->GetBlockId()));
+}
+
+// Tests SuperblockCloner for loop peeling case.
+//
+// Control Flow of the example (ignoring critical edges splitting).
+//
+//       Before                    After
+//
+//         |B|                      |B|
+//          |                        |
+//          v                        v
+//         |1|                      |1|
+//          |                        |
+//          v                        v
+//         |2|<-\              (6) |2A|
+//         / \  /                   / \
+//        v   v/                   /   v
+//       |4|  |3|                 /   |3A| (7)
+//        |                      /     /
+//        v                     |     v
+//       |E|                     \   |2|<-\
+//                                \ / \   /
+//                                 v   v /
+//                                |4|  |3|
+//                                 |
+//                                 v
+//                                |E|
+TEST_F(SuperblockClonerTest, LoopPeeling) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  HBasicBlockMap bb_map(
+      std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HInstructionMap hir_map(
+      std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+
+  HLoopInformation* loop_info = header->GetLoopInformation();
+  PeelUnrollHelper helper(loop_info, &bb_map, &hir_map);
+  EXPECT_TRUE(helper.IsLoopClonable());
+  HBasicBlock* new_header = helper.DoPeeling();
+  HLoopInformation* new_loop_info = new_header->GetLoopInformation();
+
+  EXPECT_TRUE(CheckGraph());
+
+  // Check loop body successors.
+  EXPECT_EQ(loop_body->GetSingleSuccessor(), header);
+  EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header);
+
+  // Check loop structure.
+  EXPECT_EQ(header, new_header);
+  EXPECT_EQ(new_loop_info->GetHeader(), header);
+  EXPECT_EQ(new_loop_info->GetBackEdges().size(), 1u);
+  EXPECT_EQ(new_loop_info->GetBackEdges()[0], loop_body);
+}
+
+// Tests SuperblockCloner for loop unrolling case.
+//
+// Control Flow of the example (ignoring critical edges splitting).
+//
+//       Before                    After
+//
+//         |B|                      |B|
+//          |                        |
+//          v                        v
+//         |1|                      |1|
+//          |                        |
+//          v                        v
+//         |2|<-\               (6) |2A|<-\
+//         / \  /                   / \    \
+//        v   v/                   /   v    \
+//       |4|  |3|                 /(7)|3A|   |
+//        |                      /     /    /
+//        v                     |     v    /
+//       |E|                     \   |2|  /
+//                                \ / \  /
+//                                 v   v/
+//                                |4| |3|
+//                                 |
+//                                 v
+//                                |E|
+TEST_F(SuperblockClonerTest, LoopUnrolling) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  HBasicBlockMap bb_map(
+      std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HInstructionMap hir_map(
+      std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+
+  HLoopInformation* loop_info = header->GetLoopInformation();
+  PeelUnrollHelper helper(loop_info, &bb_map, &hir_map);
+  EXPECT_TRUE(helper.IsLoopClonable());
+  HBasicBlock* new_header = helper.DoUnrolling();
+
+  EXPECT_TRUE(CheckGraph());
+
+  // Check loop body successors.
+  EXPECT_EQ(loop_body->GetSingleSuccessor(), bb_map.Get(header));
+  EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header);
+
+  // Check loop structure.
+  EXPECT_EQ(header, new_header);
+  EXPECT_EQ(loop_info, new_header->GetLoopInformation());
+  EXPECT_EQ(loop_info->GetHeader(), new_header);
+  EXPECT_EQ(loop_info->GetBackEdges().size(), 1u);
+  EXPECT_EQ(loop_info->GetBackEdges()[0], bb_map.Get(loop_body));
+}
+
+// Checks that loop unrolling works fine for a loop with multiple back edges. Tests that after
+// the transformation the loop has a single preheader.
+TEST_F(SuperblockClonerTest, LoopPeelingMultipleBackEdges) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+
+  // Transform a basic loop to have multiple back edges.
+  HBasicBlock* latch = header->GetSuccessors()[1];
+  HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_);
+  HBasicBlock* temp1 = new (GetAllocator()) HBasicBlock(graph_);
+  graph_->AddBlock(if_block);
+  graph_->AddBlock(temp1);
+  header->ReplaceSuccessor(latch, if_block);
+  if_block->AddSuccessor(latch);
+  if_block->AddSuccessor(temp1);
+  temp1->AddSuccessor(header);
+
+  if_block->AddInstruction(new (GetAllocator()) HIf(parameter_));
+
+  HInstructionIterator it(header->GetPhis());
+  DCHECK(!it.Done());
+  HPhi* loop_phi = it.Current()->AsPhi();
+  HInstruction* temp_add = new (GetAllocator()) HAdd(DataType::Type::kInt32,
+                                                     loop_phi,
+                                                     graph_->GetIntConstant(2));
+  temp1->AddInstruction(temp_add);
+  temp1->AddInstruction(new (GetAllocator()) HGoto());
+  loop_phi->AddInput(temp_add);
+
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  HLoopInformation* loop_info = header->GetLoopInformation();
+  PeelUnrollSimpleHelper helper(loop_info);
+  HBasicBlock* new_header = helper.DoPeeling();
+  EXPECT_EQ(header, new_header);
+
+  EXPECT_TRUE(CheckGraph());
+  EXPECT_EQ(header->GetPredecessors().size(), 3u);
+}
+
+static void CheckLoopStructureForLoopPeelingNested(HBasicBlock* loop1_header,
+                                                   HBasicBlock* loop2_header,
+                                                   HBasicBlock* loop3_header) {
+  EXPECT_EQ(loop1_header->GetLoopInformation()->GetHeader(), loop1_header);
+  EXPECT_EQ(loop2_header->GetLoopInformation()->GetHeader(), loop2_header);
+  EXPECT_EQ(loop3_header->GetLoopInformation()->GetHeader(), loop3_header);
+  EXPECT_EQ(loop1_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr);
+  EXPECT_EQ(loop2_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr);
+  EXPECT_EQ(loop3_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation()->GetHeader(),
+            loop2_header);
+}
+
+TEST_F(SuperblockClonerTest, LoopPeelingNested) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraph();
+
+  // Create the following nested structure of loops
+  //   Headers:  1    2 3
+  //             [ ], [ [ ] ]
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop1_header = header;
+
+  CreateBasicLoopControlFlow(header, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop2_header = header;
+
+  CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop3_header = header;
+
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  HLoopInformation* loop2_info_before = loop2_header->GetLoopInformation();
+  HLoopInformation* loop3_info_before = loop3_header->GetLoopInformation();
+
+  // Check nested loops structure.
+  CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
+  PeelUnrollSimpleHelper helper(loop1_header->GetLoopInformation());
+  helper.DoPeeling();
+  // Check that nested loops structure has not changed after the transformation.
+  CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
+
+  // Test that the loop info is preserved.
+  EXPECT_EQ(loop2_info_before, loop2_header->GetLoopInformation());
+  EXPECT_EQ(loop3_info_before, loop3_header->GetLoopInformation());
+
+  EXPECT_EQ(loop3_info_before->GetPreHeader()->GetLoopInformation(), loop2_info_before);
+  EXPECT_EQ(loop2_info_before->GetPreHeader()->GetLoopInformation(), nullptr);
+
+  EXPECT_EQ(helper.GetRegionToBeAdjusted(), nullptr);
+
+  EXPECT_TRUE(CheckGraph());
+}
+
+// Checks that the loop population is correctly propagated after an inner loop is peeled.
+TEST_F(SuperblockClonerTest, OuterLoopPopulationAfterInnerPeeled) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraph();
+
+  // Create the following nested structure of loops
+  //   Headers:  1 2 3        4
+  //             [ [ [ ] ] ], [ ]
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop1_header = header;
+
+  CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop2_header = header;
+
+  CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop3_header = header;
+
+  CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop4_header = header;
+
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation());
+  helper.DoPeeling();
+  HLoopInformation* loop1 = loop1_header->GetLoopInformation();
+  HLoopInformation* loop2 = loop2_header->GetLoopInformation();
+  HLoopInformation* loop3 = loop3_header->GetLoopInformation();
+  HLoopInformation* loop4 = loop4_header->GetLoopInformation();
+
+  EXPECT_TRUE(loop1->Contains(*loop2_header));
+  EXPECT_TRUE(loop1->Contains(*loop3_header));
+  EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader()));
+
+  // Check that loop4 info has not been touched after local run of AnalyzeLoops.
+  EXPECT_EQ(loop4, loop4_header->GetLoopInformation());
+
+  EXPECT_TRUE(loop1->IsIn(*loop1));
+  EXPECT_TRUE(loop2->IsIn(*loop1));
+  EXPECT_TRUE(loop3->IsIn(*loop1));
+  EXPECT_TRUE(loop3->IsIn(*loop2));
+  EXPECT_TRUE(!loop4->IsIn(*loop1));
+
+  EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), nullptr);
+
+  EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop2);
+
+  EXPECT_TRUE(CheckGraph());
+}
+
+// Checks the case when inner loop have an exit not to its immediate outer_loop but some other loop
+// in the hierarchy. Loop population information must be valid after loop peeling.
+TEST_F(SuperblockClonerTest, NestedCaseExitToOutermost) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraph();
+
+  // Create the following nested structure of loops then peel loop3.
+  //   Headers:  1 2 3
+  //             [ [ [ ] ] ]
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop1_header = header;
+  HBasicBlock* loop_body1 = loop_body;
+
+  CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+
+  CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop3_header = header;
+  HBasicBlock* loop_body3 = loop_body;
+
+  // Change the loop3 - insert an exit which leads to loop1.
+  HBasicBlock* loop3_extra_if_block = new (GetAllocator()) HBasicBlock(graph_);
+  graph_->AddBlock(loop3_extra_if_block);
+  loop3_extra_if_block->AddInstruction(new (GetAllocator()) HIf(parameter_));
+
+  loop3_header->ReplaceSuccessor(loop_body3, loop3_extra_if_block);
+  loop3_extra_if_block->AddSuccessor(loop_body1);  // Long exit.
+  loop3_extra_if_block->AddSuccessor(loop_body3);
+
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  HBasicBlock* loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0];
+  EXPECT_TRUE(loop1_header->GetLoopInformation()->Contains(*loop3_long_exit));
+
+  PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation());
+  helper.DoPeeling();
+
+  HLoopInformation* loop1 = loop1_header->GetLoopInformation();
+  // Check that after the transformation the local area for CF adjustments has been chosen
+  // correctly and loop population has been updated.
+  loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0];
+  EXPECT_TRUE(loop1->Contains(*loop3_long_exit));
+
+  EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop1);
+
+  EXPECT_TRUE(loop1->Contains(*loop3_header));
+  EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader()));
+
+  EXPECT_TRUE(CheckGraph());
+}
+
+TEST_F(SuperblockClonerTest, FastCaseCheck) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+  ArenaAllocator* arena = graph_->GetAllocator();
+
+  InitGraph();
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  graph_->BuildDominatorTree();
+
+  HLoopInformation* loop_info = header->GetLoopInformation();
+
+  ArenaBitVector orig_bb_set(
+      arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
+  orig_bb_set.Union(&loop_info->GetBlocks());
+
+  HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+  HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
+
+  CollectRemappingInfoForPeelUnroll(true,
+                                    loop_info,
+                                    &remap_orig_internal,
+                                    &remap_copy_internal,
+                                    &remap_incoming);
+
+  // Insert some extra nodes and edges.
+  HBasicBlock* preheader = loop_info->GetPreHeader();
+  orig_bb_set.SetBit(preheader->GetBlockId());
+
+  // Adjust incoming edges.
+  remap_incoming.Clear();
+  remap_incoming.Insert(HEdge(preheader->GetSinglePredecessor(), preheader));
+
+  HBasicBlockMap bb_map(std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner));
+  HInstructionMap hir_map(std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner));
+
+  SuperblockCloner cloner(graph_,
+                          &orig_bb_set,
+                          &bb_map,
+                          &hir_map);
+  cloner.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming);
+
+  EXPECT_FALSE(cloner.IsFastCase());
+}
+
+// Helper for FindCommonLoop which also check that FindCommonLoop is symmetric.
+static HLoopInformation* FindCommonLoopCheck(HLoopInformation* loop1, HLoopInformation* loop2) {
+  HLoopInformation* common_loop12 = FindCommonLoop(loop1, loop2);
+  HLoopInformation* common_loop21 = FindCommonLoop(loop2, loop1);
+  EXPECT_EQ(common_loop21, common_loop12);
+  return common_loop12;
+}
+
+// Tests FindCommonLoop function on a loop nest.
+TEST_F(SuperblockClonerTest, FindCommonLoop) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  InitGraph();
+
+  // Create the following nested structure of loops
+  //   Headers:  1 2 3      4      5
+  //             [ [ [ ] ], [ ] ], [ ]
+  CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop1_header = header;
+
+  CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop2_header = header;
+
+  CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop3_header = header;
+
+  CreateBasicLoopControlFlow(loop2_header, loop2_header->GetSuccessors()[0], &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop4_header = header;
+
+  CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  HBasicBlock* loop5_header = header;
+
+  graph_->BuildDominatorTree();
+  EXPECT_TRUE(CheckGraph());
+
+  HLoopInformation* loop1 = loop1_header->GetLoopInformation();
+  HLoopInformation* loop2 = loop2_header->GetLoopInformation();
+  HLoopInformation* loop3 = loop3_header->GetLoopInformation();
+  HLoopInformation* loop4 = loop4_header->GetLoopInformation();
+  HLoopInformation* loop5 = loop5_header->GetLoopInformation();
+
+  EXPECT_TRUE(loop1->IsIn(*loop1));
+  EXPECT_TRUE(loop2->IsIn(*loop1));
+  EXPECT_TRUE(loop3->IsIn(*loop1));
+  EXPECT_TRUE(loop3->IsIn(*loop2));
+  EXPECT_TRUE(loop4->IsIn(*loop1));
+
+  EXPECT_FALSE(loop5->IsIn(*loop1));
+  EXPECT_FALSE(loop4->IsIn(*loop2));
+  EXPECT_FALSE(loop4->IsIn(*loop3));
+
+  EXPECT_EQ(loop1->GetPreHeader()->GetLoopInformation(), nullptr);
+  EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), loop1);
+
+  EXPECT_EQ(FindCommonLoopCheck(nullptr, nullptr), nullptr);
+  EXPECT_EQ(FindCommonLoopCheck(loop2, nullptr), nullptr);
+
+  EXPECT_EQ(FindCommonLoopCheck(loop1, loop1), loop1);
+  EXPECT_EQ(FindCommonLoopCheck(loop1, loop2), loop1);
+  EXPECT_EQ(FindCommonLoopCheck(loop1, loop3), loop1);
+  EXPECT_EQ(FindCommonLoopCheck(loop1, loop4), loop1);
+  EXPECT_EQ(FindCommonLoopCheck(loop1, loop5), nullptr);
+
+  EXPECT_EQ(FindCommonLoopCheck(loop2, loop3), loop2);
+  EXPECT_EQ(FindCommonLoopCheck(loop2, loop4), loop1);
+  EXPECT_EQ(FindCommonLoopCheck(loop2, loop5), nullptr);
+
+  EXPECT_EQ(FindCommonLoopCheck(loop3, loop4), loop1);
+  EXPECT_EQ(FindCommonLoopCheck(loop3, loop5), nullptr);
+
+  EXPECT_EQ(FindCommonLoopCheck(loop4, loop5), nullptr);
+
+  EXPECT_EQ(FindCommonLoopCheck(loop5, loop5), loop5);
+}
+
 }  // namespace art
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index 1482210..778a015 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -25,9 +25,9 @@
 
 #include "android-base/strings.h"
 
+#include "base/utils.h"
 #include "common_runtime_test.h"  // For ScratchFile
 #include "exec_utils.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/utils/atomic_dex_ref_map.h b/compiler/utils/atomic_dex_ref_map.h
index cabd79e..fc24379 100644
--- a/compiler/utils/atomic_dex_ref_map.h
+++ b/compiler/utils/atomic_dex_ref_map.h
@@ -17,7 +17,7 @@
 #ifndef ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_
 #define ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/dchecked_vector.h"
 #include "base/safe_map.h"
 #include "dex/dex_file_reference.h"
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 2218ef9..b2ad490 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -2793,6 +2793,26 @@
   DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt);
 }
 
+void MipsAssembler::PcntB(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  DsFsmInstr(EmitMsa2R(0xc1, 0x0, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws);
+}
+
+void MipsAssembler::PcntH(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  DsFsmInstr(EmitMsa2R(0xc1, 0x1, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws);
+}
+
+void MipsAssembler::PcntW(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  DsFsmInstr(EmitMsa2R(0xc1, 0x2, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws);
+}
+
+void MipsAssembler::PcntD(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  DsFsmInstr(EmitMsa2R(0xc1, 0x3, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws);
+}
+
 void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst,
                                                 FRegister src,
                                                 bool is_double) {
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 7de8e2e..c6ce62b 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -756,6 +756,11 @@
   void Hadd_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
   void Hadd_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
 
+  void PcntB(VectorRegister wd, VectorRegister ws);
+  void PcntH(VectorRegister wd, VectorRegister ws);
+  void PcntW(VectorRegister wd, VectorRegister ws);
+  void PcntD(VectorRegister wd, VectorRegister ws);
+
   // Helper for replicating floating point value in all destination elements.
   void ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double);
 
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index 937ee25..691c33f 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -2277,6 +2277,22 @@
   DriverStr(RepeatVR(&mips::MipsAssembler::FillW, "fill.w ${reg1}, ${reg2}"), "fill.w");
 }
 
+TEST_F(AssemblerMIPS32r6Test, PcntB) {
+  DriverStr(RepeatVV(&mips::MipsAssembler::PcntB, "pcnt.b ${reg1}, ${reg2}"), "pcnt.b");
+}
+
+TEST_F(AssemblerMIPS32r6Test, PcntH) {
+  DriverStr(RepeatVV(&mips::MipsAssembler::PcntH, "pcnt.h ${reg1}, ${reg2}"), "pcnt.h");
+}
+
+TEST_F(AssemblerMIPS32r6Test, PcntW) {
+  DriverStr(RepeatVV(&mips::MipsAssembler::PcntW, "pcnt.w ${reg1}, ${reg2}"), "pcnt.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, PcntD) {
+  DriverStr(RepeatVV(&mips::MipsAssembler::PcntD, "pcnt.d ${reg1}, ${reg2}"), "pcnt.d");
+}
+
 TEST_F(AssemblerMIPS32r6Test, LdiB) {
   DriverStr(RepeatVIb(&mips::MipsAssembler::LdiB, -8, "ldi.b ${reg}, {imm}"), "ldi.b");
 }
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index e1b0e75..5a817fa 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -2279,6 +2279,26 @@
   EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15);
 }
 
+void Mips64Assembler::PcntB(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  EmitMsa2R(0xc1, 0x0, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::PcntH(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  EmitMsa2R(0xc1, 0x1, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::PcntW(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  EmitMsa2R(0xc1, 0x2, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::PcntD(VectorRegister wd, VectorRegister ws) {
+  CHECK(HasMsa());
+  EmitMsa2R(0xc1, 0x3, ws, wd, 0x1e);
+}
+
 void Mips64Assembler::ReplicateFPToVectorRegister(VectorRegister dst,
                                                   FpuRegister src,
                                                   bool is_double) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 7a61f39..542dbaf 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -863,6 +863,11 @@
   void Hadd_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
   void Hadd_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
 
+  void PcntB(VectorRegister wd, VectorRegister ws);
+  void PcntH(VectorRegister wd, VectorRegister ws);
+  void PcntW(VectorRegister wd, VectorRegister ws);
+  void PcntD(VectorRegister wd, VectorRegister ws);
+
   // Helper for replicating floating point value in all destination elements.
   void ReplicateFPToVectorRegister(VectorRegister dst, FpuRegister src, bool is_double);
 
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index b0e1d91..fb5f12b 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -3529,6 +3529,22 @@
   DriverStr(RepeatVR(&mips64::Mips64Assembler::FillD, "fill.d ${reg1}, ${reg2}"), "fill.d");
 }
 
+TEST_F(AssemblerMIPS64Test, PcntB) {
+  DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntB, "pcnt.b ${reg1}, ${reg2}"), "pcnt.b");
+}
+
+TEST_F(AssemblerMIPS64Test, PcntH) {
+  DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntH, "pcnt.h ${reg1}, ${reg2}"), "pcnt.h");
+}
+
+TEST_F(AssemblerMIPS64Test, PcntW) {
+  DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntW, "pcnt.w ${reg1}, ${reg2}"), "pcnt.w");
+}
+
+TEST_F(AssemblerMIPS64Test, PcntD) {
+  DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntD, "pcnt.d ${reg1}, ${reg2}"), "pcnt.d");
+}
+
 TEST_F(AssemblerMIPS64Test, LdiB) {
   DriverStr(RepeatVIb(&mips64::Mips64Assembler::LdiB, -8, "ldi.b ${reg}, {imm}"), "ldi.b");
 }
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
index f4bca59..1650080 100644
--- a/compiler/utils/swap_space_test.cc
+++ b/compiler/utils/swap_space_test.cc
@@ -24,9 +24,9 @@
 
 #include "gtest/gtest.h"
 
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/compiler/utils/test_dex_file_builder_test.cc b/compiler/utils/test_dex_file_builder_test.cc
index 736a17e..788afd8 100644
--- a/compiler/utils/test_dex_file_builder_test.cc
+++ b/compiler/utils/test_dex_file_builder_test.cc
@@ -16,9 +16,9 @@
 
 #include "test_dex_file_builder.h"
 
+#include "base/utils.h"
 #include "dex/dex_file-inl.h"
 #include "gtest/gtest.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index ea160c8..42c2541 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -913,6 +913,78 @@
 }
 
 
+void X86Assembler::paddusb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xDC);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::paddsb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xEC);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::paddusw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xDD);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::paddsw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xED);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubusb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xD8);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubsb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xE8);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubusw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xD9);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubsw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xE9);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
 void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF3);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index a085677..22eaedc 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -449,6 +449,15 @@
   void paddq(XmmRegister dst, XmmRegister src);
   void psubq(XmmRegister dst, XmmRegister src);
 
+  void paddusb(XmmRegister dst, XmmRegister src);
+  void paddsb(XmmRegister dst, XmmRegister src);
+  void paddusw(XmmRegister dst, XmmRegister src);
+  void paddsw(XmmRegister dst, XmmRegister src);
+  void psubusb(XmmRegister dst, XmmRegister src);
+  void psubsb(XmmRegister dst, XmmRegister src);
+  void psubusw(XmmRegister dst, XmmRegister src);
+  void psubsw(XmmRegister dst, XmmRegister src);
+
   void cvtsi2ss(XmmRegister dst, Register src);
   void cvtsi2sd(XmmRegister dst, Register src);
 
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 2fd1b27..8f72db7 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -600,6 +600,38 @@
   DriverStr(RepeatFF(&x86::X86Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq");
 }
 
+TEST_F(AssemblerX86Test, PAddUSB) {
+  DriverStr(RepeatFF(&x86::X86Assembler::paddusb, "paddusb %{reg2}, %{reg1}"), "paddusb");
+}
+
+TEST_F(AssemblerX86Test, PAddSB) {
+  DriverStr(RepeatFF(&x86::X86Assembler::paddsb, "paddsb %{reg2}, %{reg1}"), "paddsb");
+}
+
+TEST_F(AssemblerX86Test, PAddUSW) {
+  DriverStr(RepeatFF(&x86::X86Assembler::paddusw, "paddusw %{reg2}, %{reg1}"), "paddusw");
+}
+
+TEST_F(AssemblerX86Test, PAddSW) {
+  DriverStr(RepeatFF(&x86::X86Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw");
+}
+
+TEST_F(AssemblerX86Test, PSubUSB) {
+  DriverStr(RepeatFF(&x86::X86Assembler::psubusb, "psubusb %{reg2}, %{reg1}"), "psubusb");
+}
+
+TEST_F(AssemblerX86Test, PSubSB) {
+  DriverStr(RepeatFF(&x86::X86Assembler::psubsb, "psubsb %{reg2}, %{reg1}"), "psubsb");
+}
+
+TEST_F(AssemblerX86Test, PSubUSW) {
+  DriverStr(RepeatFF(&x86::X86Assembler::psubusw, "psubusw %{reg2}, %{reg1}"), "psubusw");
+}
+
+TEST_F(AssemblerX86Test, PSubSW) {
+  DriverStr(RepeatFF(&x86::X86Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw");
+}
+
 TEST_F(AssemblerX86Test, XorPD) {
   DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
 }
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index ff5a357..c6e16e7 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1011,6 +1011,86 @@
 }
 
 
+void X86_64Assembler::paddusb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xDC);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::paddsb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xEC);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::paddusw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xDD);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::paddsw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xED);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubusb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xD8);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubsb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xE8);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubusw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xD9);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubsw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xE9);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
 void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) {
   cvtsi2ss(dst, src, false);
 }
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 7a5fdb5..ab761fb 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -485,6 +485,15 @@
   void paddq(XmmRegister dst, XmmRegister src);
   void psubq(XmmRegister dst, XmmRegister src);
 
+  void paddusb(XmmRegister dst, XmmRegister src);
+  void paddsb(XmmRegister dst, XmmRegister src);
+  void paddusw(XmmRegister dst, XmmRegister src);
+  void paddsw(XmmRegister dst, XmmRegister src);
+  void psubusb(XmmRegister dst, XmmRegister src);
+  void psubsb(XmmRegister dst, XmmRegister src);
+  void psubusw(XmmRegister dst, XmmRegister src);
+  void psubsw(XmmRegister dst, XmmRegister src);
+
   void cvtsi2ss(XmmRegister dst, CpuRegister src);  // Note: this is the r/m32 version.
   void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit);
   void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 6b1e53c..104e215 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1282,6 +1282,38 @@
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq");
 }
 
+TEST_F(AssemblerX86_64Test, Paddusb) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddusb, "paddusb %{reg2}, %{reg1}"), "paddusb");
+}
+
+TEST_F(AssemblerX86_64Test, Paddsb) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddsb, "paddsb %{reg2}, %{reg1}"), "paddsb");
+}
+
+TEST_F(AssemblerX86_64Test, Paddusw) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddusw, "paddusw %{reg2}, %{reg1}"), "paddusw");
+}
+
+TEST_F(AssemblerX86_64Test, Paddsw) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddsw, "paddsw %{reg2}, %{reg1}"), "paddsw");
+}
+
+TEST_F(AssemblerX86_64Test, Psubusb) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubusb, "psubusb %{reg2}, %{reg1}"), "psubusb");
+}
+
+TEST_F(AssemblerX86_64Test, Psubsb) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubsb, "psubsb %{reg2}, %{reg1}"), "psubsb");
+}
+
+TEST_F(AssemblerX86_64Test, Psubusw) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubusw, "psubusw %{reg2}, %{reg1}"), "psubusw");
+}
+
+TEST_F(AssemblerX86_64Test, Psubsw) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw");
+}
+
 TEST_F(AssemblerX86_64Test, Cvtsi2ss) {
   DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss");
 }
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index b67898d..b158231 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -139,7 +139,14 @@
             "-Wno-frame-larger-than=",
             "-DART_PGO_INSTRUMENTATION",
         ],
-    }
+    },
+    target: {
+        android: {
+            lto: {
+                 thin: true,
+            },
+        },
+    },
 }
 
 art_cc_binary {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 926575e..6eeec4e 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -46,12 +46,15 @@
 #include "base/file_utils.h"
 #include "base/leb128.h"
 #include "base/macros.h"
+#include "base/mutex.h"
+#include "base/os.h"
 #include "base/scoped_flock.h"
 #include "base/stl_util.h"
 #include "base/stringpiece.h"
 #include "base/time_utils.h"
 #include "base/timing_logger.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "class_loader_context.h"
 #include "cmdline_parser.h"
@@ -89,11 +92,9 @@
 #include "mirror/object_array-inl.h"
 #include "oat_file.h"
 #include "oat_file_assistant.h"
-#include "os.h"
 #include "runtime.h"
 #include "runtime_options.h"
 #include "scoped_thread_state_change-inl.h"
-#include "utils.h"
 #include "vdex_file.h"
 #include "verifier/verifier_deps.h"
 #include "well_known_classes.h"
@@ -1164,6 +1165,7 @@
     original_argc = argc;
     original_argv = argv;
 
+    Locks::Init();
     InitLogging(argv, Runtime::Abort);
 
     compiler_options_.reset(new CompilerOptions());
@@ -2067,11 +2069,9 @@
         std::unique_ptr<linker::OatWriter>& oat_writer = oat_writers_[i];
 
         oat_writer->PrepareLayout(&patcher);
-
-        size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
-        size_t text_size = oat_writer->GetOatSize() - rodata_size;
-        elf_writer->PrepareDynamicSection(rodata_size,
-                                          text_size,
+        elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(),
+                                          oat_writer->GetCodeSize(),
+                                          oat_writer->GetDataBimgRelRoSize(),
                                           oat_writer->GetBssSize(),
                                           oat_writer->GetBssMethodsOffset(),
                                           oat_writer->GetBssRootsOffset(),
@@ -2121,6 +2121,16 @@
         }
         elf_writer->EndText(text);
 
+        if (oat_writer->GetDataBimgRelRoSize() != 0u) {
+          linker::OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo();
+          if (!oat_writer->WriteDataBimgRelRo(data_bimg_rel_ro)) {
+            LOG(ERROR) << "Failed to write .data.bimg.rel.ro section to the ELF file "
+                << oat_file->GetPath();
+            return false;
+          }
+          elf_writer->EndDataBimgRelRo(data_bimg_rel_ro);
+        }
+
         if (!oat_writer->WriteHeader(elf_writer->GetStream(),
                                      image_file_location_oat_checksum_,
                                      image_file_location_oat_data_begin_,
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 05592f1..d895282 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -29,13 +29,13 @@
 #include "base/file_utils.h"
 #include "base/macros.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_loader.h"
 #include "jit/profile_compilation_info.h"
 #include "method_reference.h"
 #include "runtime.h"
-#include "utils.h"
 
 namespace art {
 
@@ -129,12 +129,15 @@
     std::string art_file = scratch.GetFilename() + ".art";
     std::string oat_file = scratch.GetFilename() + ".oat";
     std::string vdex_file = scratch.GetFilename() + ".vdex";
-    ret.art_size = GetFileSizeBytes(art_file);
-    ret.oat_size = GetFileSizeBytes(oat_file);
-    ret.vdex_size = GetFileSizeBytes(vdex_file);
-    CHECK_GT(ret.art_size, 0u) << art_file;
-    CHECK_GT(ret.oat_size, 0u) << oat_file;
-    CHECK_GT(ret.vdex_size, 0u) << vdex_file;
+    int64_t art_size = OS::GetFileSizeBytes(art_file.c_str());
+    int64_t oat_size = OS::GetFileSizeBytes(oat_file.c_str());
+    int64_t vdex_size = OS::GetFileSizeBytes(vdex_file.c_str());
+    CHECK_GT(art_size, 0u) << art_file;
+    CHECK_GT(oat_size, 0u) << oat_file;
+    CHECK_GT(vdex_size, 0u) << vdex_file;
+    ret.art_size = art_size;
+    ret.oat_size = oat_size;
+    ret.vdex_size = vdex_size;
     scratch.Close();
     // Clear image files since we compile the image multiple times and don't want to leave any
     // artifacts behind.
diff --git a/dex2oat/dex2oat_options.h b/dex2oat/dex2oat_options.h
index ccc85c8..cc124c1 100644
--- a/dex2oat/dex2oat_options.h
+++ b/dex2oat/dex2oat_options.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include "arch/instruction_set.h"
 #include "base/variant_map.h"
 #include "cmdline_types.h"  // TODO: don't need to include this file here
 #include "compiler.h"
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 6b75595..96dd319 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -29,6 +29,7 @@
 
 #include "base/macros.h"
 #include "base/mutex-inl.h"
+#include "base/utils.h"
 #include "bytecode_utils.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/base64_test_util.h"
@@ -40,7 +41,6 @@
 #include "jit/profile_compilation_info.h"
 #include "oat.h"
 #include "oat_file.h"
-#include "utils.h"
 #include "vdex_file.h"
 #include "ziparchive/zip_writer.h"
 
diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h
index 7c47740..cd8cf4c 100644
--- a/dex2oat/linker/elf_writer.h
+++ b/dex2oat/linker/elf_writer.h
@@ -25,8 +25,8 @@
 #include "base/array_ref.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "base/os.h"
 #include "debug/debug_info.h"
-#include "os.h"
 
 namespace art {
 
@@ -63,6 +63,7 @@
   // This method must be called before calling GetLoadedSize().
   virtual void PrepareDynamicSection(size_t rodata_size,
                                      size_t text_size,
+                                     size_t data_bimg_rel_ro_size,
                                      size_t bss_size,
                                      size_t bss_methods_offset,
                                      size_t bss_roots_offset,
@@ -72,6 +73,8 @@
   virtual void EndRoData(OutputStream* rodata) = 0;
   virtual OutputStream* StartText() = 0;
   virtual void EndText(OutputStream* text) = 0;
+  virtual OutputStream* StartDataBimgRelRo() = 0;
+  virtual void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) = 0;
   virtual void WriteDynamicSection() = 0;
   virtual void WriteDebugInfo(const debug::DebugInfo& debug_info) = 0;
   virtual bool End() = 0;
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index d2e863c..4ab2012 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -24,6 +24,7 @@
 
 #include "base/casts.h"
 #include "base/leb128.h"
+#include "base/utils.h"
 #include "compiled_method.h"
 #include "debug/elf_debug_writer.h"
 #include "debug/method_debug_info.h"
@@ -36,7 +37,6 @@
 #include "linker/file_output_stream.h"
 #include "thread-current-inl.h"
 #include "thread_pool.h"
-#include "utils.h"
 
 namespace art {
 namespace linker {
@@ -105,6 +105,7 @@
   void Start() OVERRIDE;
   void PrepareDynamicSection(size_t rodata_size,
                              size_t text_size,
+                             size_t data_bimg_rel_ro_size,
                              size_t bss_size,
                              size_t bss_methods_offset,
                              size_t bss_roots_offset,
@@ -114,6 +115,8 @@
   void EndRoData(OutputStream* rodata) OVERRIDE;
   OutputStream* StartText() OVERRIDE;
   void EndText(OutputStream* text) OVERRIDE;
+  OutputStream* StartDataBimgRelRo() OVERRIDE;
+  void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) OVERRIDE;
   void WriteDynamicSection() OVERRIDE;
   void WriteDebugInfo(const debug::DebugInfo& debug_info) OVERRIDE;
   bool End() OVERRIDE;
@@ -131,6 +134,7 @@
   File* const elf_file_;
   size_t rodata_size_;
   size_t text_size_;
+  size_t data_bimg_rel_ro_size_;
   size_t bss_size_;
   size_t dex_section_size_;
   std::unique_ptr<BufferedOutputStream> output_stream_;
@@ -171,6 +175,7 @@
       elf_file_(elf_file),
       rodata_size_(0u),
       text_size_(0u),
+      data_bimg_rel_ro_size_(0u),
       bss_size_(0u),
       dex_section_size_(0u),
       output_stream_(
@@ -192,6 +197,7 @@
 template <typename ElfTypes>
 void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size,
                                                      size_t text_size,
+                                                     size_t data_bimg_rel_ro_size,
                                                      size_t bss_size,
                                                      size_t bss_methods_offset,
                                                      size_t bss_roots_offset,
@@ -200,6 +206,8 @@
   rodata_size_ = rodata_size;
   DCHECK_EQ(text_size_, 0u);
   text_size_ = text_size;
+  DCHECK_EQ(data_bimg_rel_ro_size_, 0u);
+  data_bimg_rel_ro_size_ = data_bimg_rel_ro_size;
   DCHECK_EQ(bss_size_, 0u);
   bss_size_ = bss_size;
   DCHECK_EQ(dex_section_size_, 0u);
@@ -207,6 +215,7 @@
   builder_->PrepareDynamicSection(elf_file_->GetPath(),
                                   rodata_size_,
                                   text_size_,
+                                  data_bimg_rel_ro_size_,
                                   bss_size_,
                                   bss_methods_offset,
                                   bss_roots_offset,
@@ -240,6 +249,19 @@
 }
 
 template <typename ElfTypes>
+OutputStream* ElfWriterQuick<ElfTypes>::StartDataBimgRelRo() {
+  auto* data_bimg_rel_ro = builder_->GetDataBimgRelRo();
+  data_bimg_rel_ro->Start();
+  return data_bimg_rel_ro;
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) {
+  CHECK_EQ(builder_->GetDataBimgRelRo(), data_bimg_rel_ro);
+  builder_->GetDataBimgRelRo()->End();
+}
+
+template <typename ElfTypes>
 void ElfWriterQuick<ElfTypes>::WriteDynamicSection() {
   if (builder_->GetIsa() == InstructionSet::kMips ||
       builder_->GetIsa() == InstructionSet::kMips64) {
diff --git a/dex2oat/linker/elf_writer_quick.h b/dex2oat/linker/elf_writer_quick.h
index e20957c..274d18b 100644
--- a/dex2oat/linker/elf_writer_quick.h
+++ b/dex2oat/linker/elf_writer_quick.h
@@ -20,8 +20,8 @@
 #include <memory>
 
 #include "arch/instruction_set.h"
+#include "base/os.h"
 #include "elf_writer.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
index 8427e7b..b2be003 100644
--- a/dex2oat/linker/elf_writer_test.cc
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -18,13 +18,13 @@
 
 #include "base/file_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "common_compiler_test.h"
 #include "elf_file.h"
 #include "elf_file_impl.h"
 #include "elf_writer_quick.h"
 #include "linker/elf_builder.h"
 #include "oat.h"
-#include "utils.h"
 
 namespace art {
 namespace linker {
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index c6ce951..7449191 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -28,6 +28,7 @@
 #include "art_method-inl.h"
 #include "base/file_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "common_compiler_test.h"
 #include "compiler_callbacks.h"
@@ -46,7 +47,6 @@
 #include "oat_writer.h"
 #include "scoped_thread_state_change-inl.h"
 #include "signal_catcher.h"
-#include "utils.h"
 
 namespace art {
 namespace linker {
@@ -313,10 +313,9 @@
         oat_writer->WriteChecksumsAndVdexHeader(vdex_out.get());
 
         oat_writer->PrepareLayout(&patcher);
-        size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
-        size_t text_size = oat_writer->GetOatSize() - rodata_size;
-        elf_writer->PrepareDynamicSection(rodata_size,
-                                          text_size,
+        elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(),
+                                          oat_writer->GetCodeSize(),
+                                          oat_writer->GetDataBimgRelRoSize(),
                                           oat_writer->GetBssSize(),
                                           oat_writer->GetBssMethodsOffset(),
                                           oat_writer->GetBssRootsOffset(),
@@ -336,6 +335,13 @@
         ASSERT_TRUE(text_ok);
         elf_writer->EndText(text);
 
+        if (oat_writer->GetDataBimgRelRoSize() != 0u) {
+          OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo();
+          bool data_bimg_rel_ro_ok = oat_writer->WriteDataBimgRelRo(data_bimg_rel_ro);
+          ASSERT_TRUE(data_bimg_rel_ro_ok);
+          elf_writer->EndDataBimgRelRo(data_bimg_rel_ro);
+        }
+
         bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u);
         ASSERT_TRUE(header_ok);
 
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index a2ba816..6530ead 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -692,7 +692,7 @@
   for (ImageInfo& image_info : image_infos_) {
     ImageSection unused_sections[ImageHeader::kSectionCount];
     const size_t length = RoundUp(
-        image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize);
+        image_info.CreateImageSections(unused_sections), kPageSize);
 
     std::string error_msg;
     image_info.image_.reset(MemMap::MapAnonymous("image writer image",
@@ -1842,7 +1842,7 @@
     image_info.image_offset_ = image_offset;
     ImageSection unused_sections[ImageHeader::kSectionCount];
     image_info.image_size_ =
-        RoundUp(image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize);
+        RoundUp(image_info.CreateImageSections(unused_sections), kPageSize);
     // There should be no gaps until the next image.
     image_offset += image_info.image_size_;
   }
@@ -1873,8 +1873,7 @@
   }
 }
 
-size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections,
-                                                   bool app_image) 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
@@ -1912,13 +1911,8 @@
   ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays];
   *dex_cache_arrays_section = ImageSection(GetBinSlotOffset(Bin::kDexCacheArray),
                                            GetBinSlotSize(Bin::kDexCacheArray));
-  // For boot image, round up to the page boundary to separate the interned strings and
-  // class table from the modifiable data. We shall mprotect() these pages read-only when
-  // we load the boot image. This is more than sufficient for the string table alignment,
-  // namely sizeof(uint64_t). See HashSet::WriteToMemory.
-  static_assert(IsAligned<sizeof(uint64_t)>(kPageSize), "String table alignment check.");
-  size_t cur_pos =
-      RoundUp(dex_cache_arrays_section->End(), app_image ? sizeof(uint64_t) : kPageSize);
+  // Round up to the alignment the string table expects. See HashSet::WriteToMemory.
+  size_t cur_pos = RoundUp(dex_cache_arrays_section->End(), sizeof(uint64_t));
   // Calculate the size of the interned strings.
   ImageSection* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings];
   *interned_strings_section = ImageSection(cur_pos, intern_table_bytes_);
@@ -1941,7 +1935,7 @@
 
   // Create the image sections.
   ImageSection sections[ImageHeader::kSectionCount];
-  const size_t image_end = image_info.CreateImageSections(sections, compile_app_image_);
+  const size_t image_end = image_info.CreateImageSections(sections);
 
   // Finally bitmap section.
   const size_t bitmap_bytes = image_info.image_bitmap_->Size();
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 856edfb..c67835b 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -33,7 +33,9 @@
 #include "base/enums.h"
 #include "base/length_prefixed_array.h"
 #include "base/macros.h"
+#include "base/os.h"
 #include "base/safe_map.h"
+#include "base/utils.h"
 #include "class_table.h"
 #include "driver/compiler_driver.h"
 #include "image.h"
@@ -43,8 +45,6 @@
 #include "mirror/dex_cache.h"
 #include "oat_file.h"
 #include "obj_ptr.h"
-#include "os.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
@@ -267,7 +267,7 @@
 
     // Create the image sections into the out sections variable, returns the size of the image
     // excluding the bitmap.
-    size_t CreateImageSections(ImageSection* out_sections, bool app_image) const;
+    size_t CreateImageSections(ImageSection* out_sections) const;
 
     size_t GetStubOffset(StubType stub_type) const {
       DCHECK_LT(static_cast<size_t>(stub_type), kNumberOfStubTypes);
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 2feb14a..31f1f1e 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -27,6 +27,7 @@
 #include "base/enums.h"
 #include "base/file_magic.h"
 #include "base/logging.h"  // For VLOG
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
@@ -60,7 +61,6 @@
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "oat_quick_method_header.h"
-#include "os.h"
 #include "quicken_info.h"
 #include "scoped_thread_state_change-inl.h"
 #include "type_lookup_table.h"
@@ -375,16 +375,19 @@
     vdex_dex_shared_data_offset_(0u),
     vdex_verifier_deps_offset_(0u),
     vdex_quickening_info_offset_(0u),
+    code_size_(0u),
     oat_size_(0u),
+    data_bimg_rel_ro_start_(0u),
+    data_bimg_rel_ro_size_(0u),
     bss_start_(0u),
     bss_size_(0u),
     bss_methods_offset_(0u),
     bss_roots_offset_(0u),
+    data_bimg_rel_ro_entries_(),
     bss_method_entry_references_(),
     bss_method_entries_(),
     bss_type_entries_(),
     bss_string_entries_(),
-    map_boot_image_tables_to_bss_(false),
     oat_data_offset_(0u),
     oat_header_(nullptr),
     size_vdex_header_(0),
@@ -409,6 +412,8 @@
     size_method_header_(0),
     size_code_(0),
     size_code_alignment_(0),
+    size_data_bimg_rel_ro_(0),
+    size_data_bimg_rel_ro_alignment_(0),
     size_relative_call_thunks_(0),
     size_misc_thunks_(0),
     size_vmap_table_(0),
@@ -737,8 +742,13 @@
   {
     TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_);
     offset = InitOatCodeDexFiles(offset);
+    code_size_ = offset - GetOatHeader().GetExecutableOffset();
   }
-  oat_size_ = offset;
+  {
+    TimingLogger::ScopedTiming split("InitDataBimgRelRoLayout", timings_);
+    offset = InitDataBimgRelRoLayout(offset);
+  }
+  oat_size_ = offset;  // .bss does not count towards oat_size_.
   bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u;
 
   CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
@@ -845,7 +855,10 @@
         MethodReference(dex_file_, it.GetMemberIndex()));
     if (HasCompiledCode(compiled_method)) {
       for (const LinkerPatch& patch : compiled_method->GetPatches()) {
-        if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) {
+        if (patch.GetType() == LinkerPatch::Type::kDataBimgRelRo) {
+          writer_->data_bimg_rel_ro_entries_.Overwrite(patch.BootImageOffset(),
+                                                       /* placeholder */ 0u);
+        } else if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) {
           MethodReference target_method = patch.TargetMethod();
           AddBssReference(target_method,
                           target_method.dex_file->NumMethodIds(),
@@ -863,9 +876,6 @@
                           target_string.dex_file->NumStringIds(),
                           &writer_->bss_string_entry_references_);
           writer_->bss_string_entries_.Overwrite(target_string, /* placeholder */ 0u);
-        } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable ||
-                   patch.GetType() == LinkerPatch::Type::kTypeClassTable) {
-          writer_->map_boot_image_tables_to_bss_ = true;
         }
       }
     } else {
@@ -1776,6 +1786,16 @@
         for (const LinkerPatch& patch : compiled_method->GetPatches()) {
           uint32_t literal_offset = patch.LiteralOffset();
           switch (patch.GetType()) {
+            case LinkerPatch::Type::kDataBimgRelRo: {
+              uint32_t target_offset =
+                  writer_->data_bimg_rel_ro_start_ +
+                  writer_->data_bimg_rel_ro_entries_.Get(patch.BootImageOffset());
+              writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+                                                                   patch,
+                                                                   offset_ + literal_offset,
+                                                                   target_offset);
+              break;
+            }
             case LinkerPatch::Type::kMethodBssEntry: {
               uint32_t target_offset =
                   writer_->bss_start_ + writer_->bss_method_entries_.Get(patch.TargetMethod());
@@ -1802,14 +1822,6 @@
                                                                    target_offset);
               break;
             }
-            case LinkerPatch::Type::kStringInternTable: {
-              uint32_t target_offset = GetInternTableEntryOffset(patch);
-              writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
-                                                                   patch,
-                                                                   offset_ + literal_offset,
-                                                                   target_offset);
-              break;
-            }
             case LinkerPatch::Type::kStringBssEntry: {
               StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex());
               uint32_t target_offset =
@@ -1828,14 +1840,6 @@
                                                                    target_offset);
               break;
             }
-            case LinkerPatch::Type::kTypeClassTable: {
-              uint32_t target_offset = GetClassTableEntryOffset(patch);
-              writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
-                                                                   patch,
-                                                                   offset_ + literal_offset,
-                                                                   target_offset);
-              break;
-            }
             case LinkerPatch::Type::kTypeBssEntry: {
               TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
               uint32_t target_offset = writer_->bss_start_ + writer_->bss_type_entries_.Get(ref);
@@ -2037,42 +2041,6 @@
     data[2] = (address >> 16) & 0xffu;
     data[3] = (address >> 24) & 0xffu;
   }
-
-  // Calculate the offset of the InternTable slot (GcRoot<String>) when mmapped to the .bss.
-  uint32_t GetInternTableEntryOffset(const LinkerPatch& patch)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(!writer_->HasBootImage());
-    const uint8_t* string_root = writer_->LookupBootImageInternTableSlot(
-        *patch.TargetStringDexFile(), patch.TargetStringIndex());
-    DCHECK(string_root != nullptr);
-    return GetBootImageTableEntryOffset(string_root);
-  }
-
-  // Calculate the offset of the ClassTable::TableSlot when mmapped to the .bss.
-  uint32_t GetClassTableEntryOffset(const LinkerPatch& patch)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(!writer_->HasBootImage());
-    const uint8_t* table_slot =
-        writer_->LookupBootImageClassTableSlot(*patch.TargetTypeDexFile(), patch.TargetTypeIndex());
-    DCHECK(table_slot != nullptr);
-    return GetBootImageTableEntryOffset(table_slot);
-  }
-
-  uint32_t GetBootImageTableEntryOffset(const uint8_t* raw_root) {
-    uint32_t base_offset = writer_->bss_start_;
-    for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-      const uint8_t* const_tables_begin =
-          space->Begin() + space->GetImageHeader().GetBootImageConstantTablesOffset();
-      size_t offset = static_cast<size_t>(raw_root - const_tables_begin);
-      if (offset < space->GetImageHeader().GetBootImageConstantTablesSize()) {
-        DCHECK_LE(base_offset + offset, writer_->bss_start_ + writer_->bss_methods_offset_);
-        return base_offset + offset;
-      }
-      base_offset += space->GetImageHeader().GetBootImageConstantTablesSize();
-    }
-    LOG(FATAL) << "Didn't find boot image string in boot image intern tables!";
-    UNREACHABLE();
-  }
 };
 
 class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor {
@@ -2510,6 +2478,25 @@
   return offset;
 }
 
+size_t OatWriter::InitDataBimgRelRoLayout(size_t offset) {
+  DCHECK_EQ(data_bimg_rel_ro_size_, 0u);
+  if (data_bimg_rel_ro_entries_.empty()) {
+    // Nothing to put to the .data.bimg.rel.ro section.
+    return offset;
+  }
+
+  data_bimg_rel_ro_start_ = RoundUp(offset, kPageSize);
+
+  for (auto& entry : data_bimg_rel_ro_entries_) {
+    size_t& entry_offset = entry.second;
+    entry_offset = data_bimg_rel_ro_size_;
+    data_bimg_rel_ro_size_ += sizeof(uint32_t);
+  }
+
+  offset = data_bimg_rel_ro_start_ + data_bimg_rel_ro_size_;
+  return offset;
+}
+
 void OatWriter::InitBssLayout(InstructionSet instruction_set) {
   {
     InitBssLayoutMethodVisitor visitor(this);
@@ -2519,25 +2506,16 @@
 
   DCHECK_EQ(bss_size_, 0u);
   if (HasBootImage()) {
-    DCHECK(!map_boot_image_tables_to_bss_);
     DCHECK(bss_string_entries_.empty());
   }
-  if (!map_boot_image_tables_to_bss_ &&
-      bss_method_entries_.empty() &&
+  if (bss_method_entries_.empty() &&
       bss_type_entries_.empty() &&
       bss_string_entries_.empty()) {
     // Nothing to put to the .bss section.
     return;
   }
 
-  // Allocate space for boot image tables in the .bss section.
   PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set);
-  if (map_boot_image_tables_to_bss_) {
-    for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-      bss_size_ += space->GetImageHeader().GetBootImageConstantTablesSize();
-    }
-  }
-
   bss_methods_offset_ = bss_size_;
 
   // Prepare offsets for .bss ArtMethod entries.
@@ -2905,6 +2883,49 @@
     return false;
   }
 
+  if (data_bimg_rel_ro_size_ != 0u) {
+    write_state_ = WriteState::kWriteDataBimgRelRo;
+  } else {
+    if (!CheckOatSize(out, file_offset, relative_offset)) {
+      return false;
+    }
+    write_state_ = WriteState::kWriteHeader;
+  }
+  return true;
+}
+
+bool OatWriter::WriteDataBimgRelRo(OutputStream* out) {
+  CHECK(write_state_ == WriteState::kWriteDataBimgRelRo);
+
+  // Wrap out to update checksum with each write.
+  ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+  out = &checksum_updating_out;
+
+  const size_t file_offset = oat_data_offset_;
+  size_t relative_offset = data_bimg_rel_ro_start_;
+
+  // Record the padding before the .data.bimg.rel.ro section.
+  // Do not write anything, this zero-filled part was skipped (Seek()) when starting the section.
+  size_t code_end = GetOatHeader().GetExecutableOffset() + code_size_;
+  DCHECK_EQ(RoundUp(code_end, kPageSize), relative_offset);
+  size_t padding_size = relative_offset - code_end;
+  DCHECK_EQ(size_data_bimg_rel_ro_alignment_, 0u);
+  size_data_bimg_rel_ro_alignment_ = padding_size;
+
+  relative_offset = WriteDataBimgRelRo(out, file_offset, relative_offset);
+  if (relative_offset == 0) {
+    LOG(ERROR) << "Failed to write boot image relocations to " << out->GetLocation();
+    return false;
+  }
+
+  if (!CheckOatSize(out, file_offset, relative_offset)) {
+    return false;
+  }
+  write_state_ = WriteState::kWriteHeader;
+  return true;
+}
+
+bool OatWriter::CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset) {
   const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent);
   if (oat_end_file_offset == static_cast<off_t>(-1)) {
     LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation();
@@ -2939,6 +2960,8 @@
     DO_STAT(size_method_header_);
     DO_STAT(size_code_);
     DO_STAT(size_code_alignment_);
+    DO_STAT(size_data_bimg_rel_ro_);
+    DO_STAT(size_data_bimg_rel_ro_alignment_);
     DO_STAT(size_relative_call_thunks_);
     DO_STAT(size_misc_thunks_);
     DO_STAT(size_vmap_table_);
@@ -3316,6 +3339,32 @@
   return relative_offset;
 }
 
+size_t OatWriter::WriteDataBimgRelRo(OutputStream* out,
+                                     size_t file_offset,
+                                     size_t relative_offset) {
+  if (data_bimg_rel_ro_entries_.empty()) {
+    return relative_offset;
+  }
+
+  // Write the entire .data.bimg.rel.ro with a single WriteFully().
+  std::vector<uint32_t> data;
+  data.reserve(data_bimg_rel_ro_entries_.size());
+  for (const auto& entry : data_bimg_rel_ro_entries_) {
+    uint32_t boot_image_offset = entry.first;
+    data.push_back(boot_image_offset);
+  }
+  DCHECK_EQ(data.size(), data_bimg_rel_ro_entries_.size());
+  DCHECK_OFFSET();
+  if (!out->WriteFully(data.data(), data.size() * sizeof(data[0]))) {
+    PLOG(ERROR) << "Failed to write .data.bimg.rel.ro in " << out->GetLocation();
+    return 0u;
+  }
+  DCHECK_EQ(size_data_bimg_rel_ro_, 0u);
+  size_data_bimg_rel_ro_ = data.size() * sizeof(data[0]);
+  relative_offset += size_data_bimg_rel_ro_;
+  return relative_offset;
+}
+
 bool OatWriter::RecordOatDataOffset(OutputStream* out) {
   // Get the elf file offset of the oat file.
   const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
@@ -4356,42 +4405,6 @@
   return true;
 }
 
-const uint8_t* OatWriter::LookupBootImageInternTableSlot(const DexFile& dex_file,
-                                                         dex::StringIndex string_idx)
-    NO_THREAD_SAFETY_ANALYSIS {  // Single-threaded OatWriter can avoid locking.
-  uint32_t utf16_length;
-  const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
-  DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data));
-  InternTable::Utf8String string(utf16_length,
-                                 utf8_data,
-                                 ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length));
-  const InternTable* intern_table = Runtime::Current()->GetClassLinker()->intern_table_;
-  for (const InternTable::Table::UnorderedSet& table : intern_table->strong_interns_.tables_) {
-    auto it = table.Find(string);
-    if (it != table.end()) {
-      return reinterpret_cast<const uint8_t*>(std::addressof(*it));
-    }
-  }
-  LOG(FATAL) << "Did not find boot image string " << utf8_data;
-  UNREACHABLE();
-}
-
-const uint8_t* OatWriter::LookupBootImageClassTableSlot(const DexFile& dex_file,
-                                                        dex::TypeIndex type_idx)
-    NO_THREAD_SAFETY_ANALYSIS {  // Single-threaded OatWriter can avoid locking.
-  const char* descriptor = dex_file.StringByTypeIdx(type_idx);
-  ClassTable::DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor));
-  ClassTable* table = Runtime::Current()->GetClassLinker()->boot_class_table_.get();
-  for (const ClassTable::ClassSet& class_set : table->classes_) {
-    auto it = class_set.Find(pair);
-    if (it != class_set.end()) {
-      return reinterpret_cast<const uint8_t*>(std::addressof(*it));
-    }
-  }
-  LOG(FATAL) << "Did not find boot image class " << descriptor;
-  UNREACHABLE();
-}
-
 debug::DebugInfo OatWriter::GetDebugInfo() const {
   debug::DebugInfo debug_info{};
   debug_info.compiled_methods = ArrayRef<const debug::MethodDebugInfo>(method_info_);
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index d67e4de..120ea56 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -24,6 +24,7 @@
 
 #include "base/array_ref.h"
 #include "base/dchecked_vector.h"
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "compiler.h"
 #include "dex/compact_dex_level.h"
@@ -33,7 +34,6 @@
 #include "method_reference.h"
 #include "mirror/class.h"
 #include "oat.h"
-#include "os.h"
 #include "string_reference.h"
 #include "type_reference.h"
 
@@ -137,6 +137,7 @@
   //   - PrepareLayout(),
   //   - WriteRodata(),
   //   - WriteCode(),
+  //   - WriteDataBimgRelRo() iff GetDataBimgRelRoSize() != 0,
   //   - WriteHeader().
 
   // Add dex file source(s) from a file, either a plain dex file or
@@ -197,6 +198,10 @@
   bool WriteRodata(OutputStream* out);
   // Write the code to the .text section.
   bool WriteCode(OutputStream* out);
+  // Write the boot image relocation data to the .data.bimg.rel.ro section.
+  bool WriteDataBimgRelRo(OutputStream* out);
+  // Check the size of the written oat file.
+  bool CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset);
   // Write the oat header. This finalizes the oat file.
   bool WriteHeader(OutputStream* out,
                    uint32_t image_file_location_oat_checksum,
@@ -218,10 +223,18 @@
     return *oat_header_;
   }
 
+  size_t GetCodeSize() const {
+    return code_size_;
+  }
+
   size_t GetOatSize() const {
     return oat_size_;
   }
 
+  size_t GetDataBimgRelRoSize() const {
+    return data_bimg_rel_ro_size_;
+  }
+
   size_t GetBssSize() const {
     return bss_size_;
   }
@@ -323,6 +336,7 @@
   size_t InitOatDexFiles(size_t offset);
   size_t InitOatCode(size_t offset);
   size_t InitOatCodeDexFiles(size_t offset);
+  size_t InitDataBimgRelRoLayout(size_t offset);
   void InitBssLayout(InstructionSet instruction_set);
 
   size_t WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset);
@@ -332,6 +346,7 @@
   size_t WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset);
   size_t WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset);
   size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset);
+  size_t WriteDataBimgRelRo(OutputStream* out, size_t file_offset, size_t relative_offset);
 
   bool RecordOatDataOffset(OutputStream* out);
   bool WriteTypeLookupTables(OutputStream* oat_rodata,
@@ -349,17 +364,12 @@
     return dex_files_ != nullptr && extract_dex_files_into_vdex_;
   }
 
-  // Find the address of the GcRoot<String> in the InternTable for a boot image string.
-  const uint8_t* LookupBootImageInternTableSlot(const DexFile& dex_file,
-                                                dex::StringIndex string_idx);
-  // Find the address of the ClassTable::TableSlot for a boot image class.
-  const uint8_t* LookupBootImageClassTableSlot(const DexFile& dex_file, dex::TypeIndex type_idx);
-
   enum class WriteState {
     kAddingDexFileSources,
     kPrepareLayout,
     kWriteRoData,
     kWriteText,
+    kWriteDataBimgRelRo,
     kWriteHeader,
     kDone
   };
@@ -401,9 +411,18 @@
   // Offset of section holding quickening info inside Vdex.
   size_t vdex_quickening_info_offset_;
 
+  // Size of the .text segment.
+  size_t code_size_;
+
   // Size required for Oat data structures.
   size_t oat_size_;
 
+  // The start of the required .data.bimg.rel.ro section.
+  size_t data_bimg_rel_ro_start_;
+
+  // The size of the required .data.bimg.rel.ro section holding the boot image relocations.
+  size_t data_bimg_rel_ro_size_;
+
   // The start of the required .bss section.
   size_t bss_start_;
 
@@ -416,6 +435,10 @@
   // The offset of the GC roots in .bss section.
   size_t bss_roots_offset_;
 
+  // Map for allocating .data.bimg.rel.ro entries. Indexed by the boot image offset of the
+  // relocation. The value is the assigned offset within the .data.bimg.rel.ro section.
+  SafeMap<uint32_t, size_t> data_bimg_rel_ro_entries_;
+
   // Map for recording references to ArtMethod entries in .bss.
   SafeMap<const DexFile*, BitVector> bss_method_entry_references_;
 
@@ -440,10 +463,6 @@
   // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
   SafeMap<StringReference, size_t, StringReferenceValueComparator> bss_string_entries_;
 
-  // Whether boot image tables should be mapped to the .bss. This is needed for compiled
-  // code that reads from these tables with PC-relative instructions.
-  bool map_boot_image_tables_to_bss_;
-
   // Offset of the oat data from the start of the mmapped region of the elf file.
   size_t oat_data_offset_;
 
@@ -484,6 +503,8 @@
   uint32_t size_method_header_;
   uint32_t size_code_;
   uint32_t size_code_alignment_;
+  uint32_t size_data_bimg_rel_ro_;
+  uint32_t size_data_bimg_rel_ro_alignment_;
   uint32_t size_relative_call_thunks_;
   uint32_t size_misc_thunks_;
   uint32_t size_vmap_table_;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 00b9abe..06d264e 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -216,10 +216,9 @@
                                     instruction_set_features_.get());
     oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files);
     oat_writer.PrepareLayout(&patcher);
-    size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset();
-    size_t text_size = oat_writer.GetOatSize() - rodata_size;
-    elf_writer->PrepareDynamicSection(rodata_size,
-                                      text_size,
+    elf_writer->PrepareDynamicSection(oat_writer.GetOatHeader().GetExecutableOffset(),
+                                      oat_writer.GetCodeSize(),
+                                      oat_writer.GetDataBimgRelRoSize(),
                                       oat_writer.GetBssSize(),
                                       oat_writer.GetBssMethodsOffset(),
                                       oat_writer.GetBssRootsOffset(),
@@ -248,6 +247,14 @@
     }
     elf_writer->EndText(text);
 
+    if (oat_writer.GetDataBimgRelRoSize() != 0u) {
+      OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo();
+      if (!oat_writer.WriteDataBimgRelRo(data_bimg_rel_ro)) {
+        return false;
+      }
+      elf_writer->EndDataBimgRelRo(data_bimg_rel_ro);
+    }
+
     if (!oat_writer.WriteHeader(elf_writer->GetStream(), 42U, 4096U, 0)) {
       return false;
     }
@@ -407,7 +414,7 @@
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
   }
 
-  ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
+  ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
   bool success = WriteElf(tmp_vdex.GetFile(),
@@ -544,10 +551,14 @@
   compiler_driver_->SetDexFilesForOatFile(dex_files);
   compiler_driver_->CompileAll(class_loader, dex_files, &timings);
 
-  ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
+  ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
-  bool success = WriteElf(tmp_vdex.GetFile(), tmp_oat.GetFile(), dex_files, key_value_store, false);
+  bool success = WriteElf(tmp_vdex.GetFile(),
+                          tmp_oat.GetFile(),
+                          dex_files,
+                          key_value_store,
+                          /* verify */ false);
   ASSERT_TRUE(success);
 
   std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
@@ -606,13 +617,13 @@
   ASSERT_TRUE(success);
   input_filenames.push_back(dex_file2.GetFilename().c_str());
 
-  ScratchFile oat_file, vdex_file(oat_file, ".vdex");
+  ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
   std::unique_ptr<ProfileCompilationInfo>
       profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
-  success = WriteElf(vdex_file.GetFile(),
-                     oat_file.GetFile(),
+  success = WriteElf(tmp_vdex.GetFile(),
+                     tmp_oat.GetFile(),
                      input_filenames,
                      key_value_store,
                      verify,
@@ -627,19 +638,19 @@
   ASSERT_TRUE(success);
 
   std::string error_msg;
-  std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
-                                                         oat_file.GetFilename(),
+  std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(tmp_oat.GetFilename(),
+                                                         tmp_oat.GetFilename(),
                                                          nullptr,
                                                          nullptr,
                                                          false,
                                                          low_4gb,
                                                          nullptr,
                                                          &error_msg));
+  ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
   if (low_4gb) {
     uintptr_t begin = reinterpret_cast<uintptr_t>(opened_oat_file->Begin());
     EXPECT_EQ(begin, static_cast<uint32_t>(begin));
   }
-  ASSERT_TRUE(opened_oat_file != nullptr);
   ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
   std::unique_ptr<const DexFile> opened_dex_file1 =
       opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
@@ -670,7 +681,7 @@
     ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u);
   }
 
-  int64_t actual_vdex_size = vdex_file.GetFile()->GetLength();
+  int64_t actual_vdex_size = tmp_vdex.GetFile()->GetLength();
   ASSERT_GE(actual_vdex_size, 0);
   ASSERT_EQ((uint64_t) actual_vdex_size, vdex_header.GetComputedFileSize());
 }
@@ -742,9 +753,13 @@
     // Test using the AddDexFileSource() interface with the zip file.
     std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() };
 
-    ScratchFile oat_file, vdex_file(oat_file, ".vdex");
-    success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames,
-                       key_value_store, verify, /*profile_compilation_info*/nullptr);
+    ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
+    success = WriteElf(tmp_vdex.GetFile(),
+                       tmp_oat.GetFile(),
+                       input_filenames,
+                       key_value_store,
+                       verify,
+                       /* profile_compilation_info */ nullptr);
 
     if (verify) {
       ASSERT_FALSE(success);
@@ -752,15 +767,15 @@
       ASSERT_TRUE(success);
 
       std::string error_msg;
-      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
-                                                             oat_file.GetFilename(),
+      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(tmp_oat.GetFilename(),
+                                                             tmp_oat.GetFilename(),
                                                              nullptr,
                                                              nullptr,
                                                              false,
                                                              /*low_4gb*/false,
                                                              nullptr,
                                                              &error_msg));
-      ASSERT_TRUE(opened_oat_file != nullptr);
+      ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
       ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
       std::unique_ptr<const DexFile> opened_dex_file1 =
           opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
@@ -788,9 +803,9 @@
     File zip_fd(dup(zip_file.GetFd()), /* check_usage */ false);
     ASSERT_NE(-1, zip_fd.Fd());
 
-    ScratchFile oat_file, vdex_file(oat_file, ".vdex");
-    success = WriteElf(vdex_file.GetFile(),
-                       oat_file.GetFile(),
+    ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
+    success = WriteElf(tmp_vdex.GetFile(),
+                       tmp_oat.GetFile(),
                        std::move(zip_fd),
                        zip_file.GetFilename().c_str(),
                        key_value_store,
@@ -801,15 +816,15 @@
       ASSERT_TRUE(success);
 
       std::string error_msg;
-      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
-                                                             oat_file.GetFilename(),
+      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(tmp_oat.GetFilename(),
+                                                             tmp_oat.GetFilename(),
                                                              nullptr,
                                                              nullptr,
                                                              false,
                                                              /*low_4gb*/false,
                                                              nullptr,
                                                              &error_msg));
-      ASSERT_TRUE(opened_oat_file != nullptr);
+      ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
       ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
       std::unique_ptr<const DexFile> opened_dex_file1 =
           opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
@@ -854,8 +869,12 @@
   std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() };
   ScratchFile oat_file, vdex_file(oat_file, ".vdex");
   std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(new ProfileCompilationInfo());
-  success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames,
-                     key_value_store, /*verify*/false, profile_compilation_info.get());
+  success = WriteElf(vdex_file.GetFile(),
+                     oat_file.GetFile(),
+                     input_filenames,
+                     key_value_store,
+                     /* verify */ false,
+                     profile_compilation_info.get());
   ASSERT_FALSE(success);
 }
 
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index eca0844..c63d6c3 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -46,7 +46,6 @@
     device_supported: false,
     static_libs: [
         "libdexfile",
-        "libbase",
     ] + art_static_dependencies,
     target: {
         darwin: {
diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc
index 559dc8e..63e0e3f 100644
--- a/dexdump/dexdump_test.cc
+++ b/dexdump/dexdump_test.cc
@@ -22,10 +22,10 @@
 #include <unistd.h>
 
 #include "arch/instruction_set.h"
+#include "base/os.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "exec_utils.h"
-#include "os.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index bea61d0..facda11 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -44,7 +44,14 @@
         instrumentation: true,
         profile_file: "art/dex2oat.profdata",
         benchmarks: ["dex2oat"],
-    }
+    },
+    target: {
+        android: {
+            lto: {
+                 thin: true,
+            },
+        },
+    },
 }
 
 art_cc_library {
@@ -106,6 +113,7 @@
     shared_libs: [
         "libart",
         "libart-dexlayout",
+        "libbase",
     ],
     target: {
         android: {
diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h
index 0e7d65f..eaf8518 100644
--- a/dexlayout/compact_dex_writer.h
+++ b/dexlayout/compact_dex_writer.h
@@ -22,8 +22,8 @@
 #include <memory>  // For unique_ptr
 #include <unordered_map>
 
+#include "base/utils.h"
 #include "dex_writer.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h
index df098c0..db1898b 100644
--- a/dexlayout/dex_writer.h
+++ b/dexlayout/dex_writer.h
@@ -22,13 +22,12 @@
 #include <functional>
 #include <memory>  // For unique_ptr
 
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "dex/compact_dex_level.h"
 #include "dex_container.h"
 #include "dex/dex_file.h"
 #include "dex_ir.h"
-#include "mem_map.h"
-#include "os.h"
 
 #include <queue>
 
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index c0d6f02..6cb141f 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -27,6 +27,7 @@
 #include "android-base/stringprintf.h"
 
 #include "base/logging.h"  // For InitLogging.
+#include "base/mutex.h"
 #include "base/stringpiece.h"
 
 #include "dexlayout.h"
@@ -470,6 +471,7 @@
   }
 
   // Art specific set up.
+  Locks::Init();
   InitLogging(argv, Runtime::Abort);
   MemMap::Init();
 
diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc
index 9927576..068949c 100644
--- a/dexlayout/dexdiag_test.cc
+++ b/dexlayout/dexdiag_test.cc
@@ -20,9 +20,9 @@
 #include "common_runtime_test.h"
 
 #include "base/file_utils.h"
+#include "base/os.h"
 #include "exec_utils.h"
 #include "oat_file.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 159a4a5..ec0cbe6 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -34,6 +34,8 @@
 #include "android-base/stringprintf.h"
 
 #include "base/logging.h"  // For VLOG_IS_ON.
+#include "base/os.h"
+#include "base/utils.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
@@ -48,8 +50,6 @@
 #include "dex_writer.h"
 #include "jit/profile_compilation_info.h"
 #include "mem_map.h"
-#include "os.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index b79a592..2bca10d 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -31,7 +31,6 @@
 #include "dex_container.h"
 #include "dex/dex_file_layout.h"
 #include "dex_ir.h"
-#include "mem_map.h"
 
 namespace art {
 
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 981f901..6719d08 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/base64_test_util.h"
@@ -31,7 +32,6 @@
 #include "dexlayout.h"
 #include "exec_utils.h"
 #include "jit/profile_compilation_info.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
index ae44848..0b9adbd 100644
--- a/dexlist/dexlist_test.cc
+++ b/dexlist/dexlist_test.cc
@@ -22,12 +22,12 @@
 #include <unistd.h>
 
 #include "arch/instruction_set.h"
+#include "base/os.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "exec_utils.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
-#include "os.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 6d4b3e3..febccf1 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -17,19 +17,19 @@
 #include <string>
 
 #include "base/logging.h"  // For InitLogging.
+#include "base/mutex.h"
+#include "base/os.h"
+#include "base/utils.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 #include "base/file_utils.h"
-#include "base/logging.h"  // For InitLogging.
 #include "compiler_filter.h"
 #include "class_loader_context.h"
 #include "dex/dex_file.h"
 #include "noop_compiler_callbacks.h"
 #include "oat_file_assistant.h"
-#include "os.h"
 #include "runtime.h"
 #include "thread-inl.h"
-#include "utils.h"
 
 namespace art {
 
@@ -142,6 +142,7 @@
     original_argc = argc;
     original_argv = argv;
 
+    Locks::Init();
     InitLogging(argv, Runtime::Abort);
     // Skip over the command name.
     argv++;
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index b5f5d6f..eaf11be 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -487,6 +487,7 @@
   { kMsaMask | (0xf << 22), kMsa | (0x3 << 22) | 0x19, "copy_u", "yX" },
   { kMsaMask | (0xf << 22), kMsa | (0x4 << 22) | 0x19, "insert", "YD" },
   { kMsaMask | (0xff << 18), kMsa | (0xc0 << 18) | 0x1e, "fill", "vkD" },
+  { kMsaMask | (0xff << 18), kMsa | (0xc1 << 18) | 0x1e, "pcnt", "vkm" },
   { kMsaMask | (0x7 << 23), kMsa | (0x6 << 23) | 0x7, "ldi", "kx" },
   { kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" },
   { kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" },
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index bbc8e37..dbdde64 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -1194,11 +1194,19 @@
           opcode1 = opcode_tmp.c_str();
         }
         break;
+      case 0xD8:
+      case 0xD9:
       case 0xDA:
+      case 0xDC:
+      case 0xDD:
       case 0xDE:
       case 0xE0:
       case 0xE3:
+      case 0xE8:
+      case 0xE9:
       case 0xEA:
+      case 0xEC:
+      case 0xED:
       case 0xEE:
         if (prefix[2] == 0x66) {
           src_reg_file = dst_reg_file = SSE;
@@ -1207,11 +1215,19 @@
           src_reg_file = dst_reg_file = MMX;
         }
         switch (*instr) {
+          case 0xD8: opcode1 = "psubusb"; break;
+          case 0xD9: opcode1 = "psubusw"; break;
           case 0xDA: opcode1 = "pminub"; break;
+          case 0xDC: opcode1 = "paddusb"; break;
+          case 0xDD: opcode1 = "paddusw"; break;
           case 0xDE: opcode1 = "pmaxub"; break;
           case 0xE0: opcode1 = "pavgb"; break;
           case 0xE3: opcode1 = "pavgw"; break;
+          case 0xE8: opcode1 = "psubsb"; break;
+          case 0xE9: opcode1 = "psubsw"; break;
           case 0xEA: opcode1 = "pminsw"; break;
+          case 0xEC: opcode1 = "paddsb"; break;
+          case 0xED: opcode1 = "paddsw"; break;
           case 0xEE: opcode1 = "pmaxsw"; break;
         }
         prefix[2] = 0;
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 8aa638a..ddb8fe1 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -30,6 +30,7 @@
 
 #include "art_field-inl.h"
 #include "art_method-inl.h"
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "gc/heap.h"
@@ -40,7 +41,6 @@
 #include "oat.h"
 #include "oat_file.h"
 #include "oat_file_manager.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 
 #include "backtrace/BacktraceMap.h"
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index 80b0c26..52096f0 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -24,13 +24,13 @@
 #include "android-base/stringprintf.h"
 
 #include "arch/instruction_set.h"
+#include "base/os.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "exec_utils.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
-#include "os.h"
 #include "runtime.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 290d398..3c61944 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -14,15 +14,100 @@
 // limitations under the License.
 //
 
+cc_defaults {
+    name: "libartbase_defaults",
+    defaults: ["art_defaults"],
+    host_supported: true,
+    srcs: [
+        "base/allocator.cc",
+        "base/bit_vector.cc",
+        "base/file_magic.cc",
+        "base/hex_dump.cc",
+        "base/logging.cc",
+        "base/os_linux.cc",
+        "base/runtime_debug.cc",
+        "base/safe_copy.cc",
+        "base/scoped_flock.cc",
+        "base/time_utils.cc",
+        "base/unix_file/fd_file.cc",
+        "base/unix_file/random_access_file_utils.cc",
+        "base/utils.cc",
+    ],
+    generated_sources: ["art_libartbase_operator_srcs"],
+    cflags: ["-DBUILDING_LIBART=1"],
+    shared_libs: [
+        // For common macros.
+        "libbase",
+        "liblog",
+    ],
+    export_include_dirs: ["."],
+    // ART's macros.h depends on libbase's macros.h.
+    // Note: runtime_options.h depends on cmdline. But we don't really want to export this
+    //       generically. dex2oat takes care of it itself.
+    export_shared_lib_headers: ["libbase"],
+}
+
+gensrcs {
+    name: "art_libartbase_operator_srcs",
+    cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)",
+    tools: ["generate_operator_out"],
+    srcs: [
+        "base/allocator.h",
+        "base/callee_save_type.h",
+        "base/unix_file/fd_file.h",
+    ],
+    output_extension: "operator_out.cc",
+}
+
+art_cc_library {
+    name: "libartbase",
+    defaults: ["libartbase_defaults"],
+    // Leave the symbols in the shared library so that stack unwinders can
+    // produce meaningful name resolution.
+    strip: {
+        keep_symbols: true,
+    },
+    shared_libs: ["libbase"],
+    export_shared_lib_headers: ["libbase"],
+}
+
+art_cc_library {
+    name: "libartbased",
+    defaults: [
+        "art_debug_defaults",
+        "libartbase_defaults",
+    ],
+    shared_libs: ["libbase"],
+    export_shared_lib_headers: ["libbase"],
+}
+
+// For now many of these tests still use CommonRuntimeTest, almost universally because of
+// ScratchFile and related.
+// TODO: Remove CommonRuntimeTest dependency from these tests.
 art_cc_test {
     name: "art_libartbase_tests",
     defaults: [
         "art_gtest_defaults",
     ],
     srcs: [
+        "base/bit_field_test.cc",
+        "base/bit_string_test.cc",
+        "base/bit_struct_test.cc",
         "base/bit_utils_test.cc",
+        "base/bit_vector_test.cc",
         "base/hash_set_test.cc",
+        "base/hex_dump_test.cc",
+        "base/histogram_test.cc",
         "base/leb128_test.cc",
+        "base/logging_test.cc",
+        "base/safe_copy_test.cc",
+        "base/scoped_flock_test.cc",
+        "base/time_utils_test.cc",
+        "base/transform_array_ref_test.cc",
+        "base/transform_iterator_test.cc",
+        "base/unix_file/fd_file_test.cc",
+        "base/utils_test.cc",
+        "base/variant_map_test.cc",
     ],
     shared_libs: [
         "libbase",
@@ -36,4 +121,3 @@
     shared_libs: ["libbase"],
     export_shared_lib_headers: ["libbase"],
 }
-
diff --git a/runtime/base/aborting.h b/libartbase/base/aborting.h
similarity index 88%
rename from runtime/base/aborting.h
rename to libartbase/base/aborting.h
index 8906c96..c7089af 100644
--- a/runtime/base/aborting.h
+++ b/libartbase/base/aborting.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ABORTING_H_
-#define ART_RUNTIME_BASE_ABORTING_H_
+#ifndef ART_LIBARTBASE_BASE_ABORTING_H_
+#define ART_LIBARTBASE_BASE_ABORTING_H_
 
 #include <atomic>
 
@@ -28,4 +28,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ABORTING_H_
+#endif  // ART_LIBARTBASE_BASE_ABORTING_H_
diff --git a/runtime/base/allocator.cc b/libartbase/base/allocator.cc
similarity index 98%
rename from runtime/base/allocator.cc
rename to libartbase/base/allocator.cc
index 2da88c3..a424145 100644
--- a/runtime/base/allocator.cc
+++ b/libartbase/base/allocator.cc
@@ -21,7 +21,7 @@
 
 #include <android-base/logging.h>
 
-#include "atomic.h"
+#include "base/atomic.h"
 
 namespace art {
 
diff --git a/runtime/base/allocator.h b/libartbase/base/allocator.h
similarity index 96%
rename from runtime/base/allocator.h
rename to libartbase/base/allocator.h
index 3cedb66..d92fe19 100644
--- a/runtime/base/allocator.h
+++ b/libartbase/base/allocator.h
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ALLOCATOR_H_
-#define ART_RUNTIME_BASE_ALLOCATOR_H_
+#ifndef ART_LIBARTBASE_BASE_ALLOCATOR_H_
+#define ART_LIBARTBASE_BASE_ALLOCATOR_H_
 
 #include <type_traits>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/macros.h"
-#include "base/mutex.h"
 
 namespace art {
 
@@ -154,4 +153,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ALLOCATOR_H_
+#endif  // ART_LIBARTBASE_BASE_ALLOCATOR_H_
diff --git a/runtime/base/array_ref.h b/libartbase/base/array_ref.h
similarity index 97%
rename from runtime/base/array_ref.h
rename to libartbase/base/array_ref.h
index 2753c81..1d7bde6 100644
--- a/runtime/base/array_ref.h
+++ b/libartbase/base/array_ref.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ARRAY_REF_H_
-#define ART_RUNTIME_BASE_ARRAY_REF_H_
+#ifndef ART_LIBARTBASE_BASE_ARRAY_REF_H_
+#define ART_LIBARTBASE_BASE_ARRAY_REF_H_
 
 #include <type_traits>
 #include <vector>
@@ -206,4 +206,4 @@
 }  // namespace art
 
 
-#endif  // ART_RUNTIME_BASE_ARRAY_REF_H_
+#endif  // ART_LIBARTBASE_BASE_ARRAY_REF_H_
diff --git a/runtime/base/array_slice.h b/libartbase/base/array_slice.h
similarity index 96%
rename from runtime/base/array_slice.h
rename to libartbase/base/array_slice.h
index a7bce7d..1ef2fba 100644
--- a/runtime/base/array_slice.h
+++ b/libartbase/base/array_slice.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_ARRAY_SLICE_H_
-#define ART_RUNTIME_BASE_ARRAY_SLICE_H_
+#ifndef ART_LIBARTBASE_BASE_ARRAY_SLICE_H_
+#define ART_LIBARTBASE_BASE_ARRAY_SLICE_H_
 
 #include "base/bit_utils.h"
 #include "base/casts.h"
@@ -149,4 +149,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_ARRAY_SLICE_H_
+#endif  // ART_LIBARTBASE_BASE_ARRAY_SLICE_H_
diff --git a/runtime/atomic.h b/libartbase/base/atomic.h
similarity index 64%
rename from runtime/atomic.h
rename to libartbase/base/atomic.h
index 0e2f056..fd34cc6 100644
--- a/runtime/atomic.h
+++ b/libartbase/base/atomic.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_ATOMIC_H_
-#define ART_RUNTIME_ATOMIC_H_
+#ifndef ART_LIBARTBASE_BASE_ATOMIC_H_
+#define ART_LIBARTBASE_BASE_ATOMIC_H_
 
 #include <stdint.h>
 #include <atomic>
@@ -24,167 +24,10 @@
 
 #include <android-base/logging.h>
 
-#include "arch/instruction_set.h"
 #include "base/macros.h"
 
 namespace art {
 
-class Mutex;
-
-// QuasiAtomic encapsulates two separate facilities that we are
-// trying to move away from:  "quasiatomic" 64 bit operations
-// and custom memory fences.  For the time being, they remain
-// exposed.  Clients should be converted to use either class Atomic
-// below whenever possible, and should eventually use C++11 atomics.
-// The two facilities that do not have a good C++11 analog are
-// ThreadFenceForConstructor and Atomic::*JavaData.
-//
-// NOTE: Two "quasiatomic" operations on the exact same memory address
-// are guaranteed to operate atomically with respect to each other,
-// but no guarantees are made about quasiatomic operations mixed with
-// non-quasiatomic operations on the same address, nor about
-// quasiatomic operations that are performed on partially-overlapping
-// memory.
-class QuasiAtomic {
-  static constexpr bool NeedSwapMutexes(InstructionSet isa) {
-    // TODO - mips64 still need this for Cas64 ???
-    return (isa == InstructionSet::kMips) || (isa == InstructionSet::kMips64);
-  }
-
- public:
-  static void Startup();
-
-  static void Shutdown();
-
-  // Reads the 64-bit value at "addr" without tearing.
-  static int64_t Read64(volatile const int64_t* addr) {
-    if (!NeedSwapMutexes(kRuntimeISA)) {
-      int64_t value;
-#if defined(__LP64__)
-      value = *addr;
-#else
-#if defined(__arm__)
-#if defined(__ARM_FEATURE_LPAE)
-      // With LPAE support (such as Cortex-A15) then ldrd is defined not to tear.
-      __asm__ __volatile__("@ QuasiAtomic::Read64\n"
-        "ldrd     %0, %H0, %1"
-        : "=r" (value)
-        : "m" (*addr));
-#else
-      // Exclusive loads are defined not to tear, clearing the exclusive state isn't necessary.
-      __asm__ __volatile__("@ QuasiAtomic::Read64\n"
-        "ldrexd     %0, %H0, %1"
-        : "=r" (value)
-        : "Q" (*addr));
-#endif
-#elif defined(__i386__)
-  __asm__ __volatile__(
-      "movq     %1, %0\n"
-      : "=x" (value)
-      : "m" (*addr));
-#else
-      LOG(FATAL) << "Unsupported architecture";
-#endif
-#endif  // defined(__LP64__)
-      return value;
-    } else {
-      return SwapMutexRead64(addr);
-    }
-  }
-
-  // Writes to the 64-bit value at "addr" without tearing.
-  static void Write64(volatile int64_t* addr, int64_t value) {
-    if (!NeedSwapMutexes(kRuntimeISA)) {
-#if defined(__LP64__)
-      *addr = value;
-#else
-#if defined(__arm__)
-#if defined(__ARM_FEATURE_LPAE)
-    // If we know that ARM architecture has LPAE (such as Cortex-A15) strd is defined not to tear.
-    __asm__ __volatile__("@ QuasiAtomic::Write64\n"
-      "strd     %1, %H1, %0"
-      : "=m"(*addr)
-      : "r" (value));
-#else
-    // The write is done as a swap so that the cache-line is in the exclusive state for the store.
-    int64_t prev;
-    int status;
-    do {
-      __asm__ __volatile__("@ QuasiAtomic::Write64\n"
-        "ldrexd     %0, %H0, %2\n"
-        "strexd     %1, %3, %H3, %2"
-        : "=&r" (prev), "=&r" (status), "+Q"(*addr)
-        : "r" (value)
-        : "cc");
-      } while (UNLIKELY(status != 0));
-#endif
-#elif defined(__i386__)
-      __asm__ __volatile__(
-        "movq     %1, %0"
-        : "=m" (*addr)
-        : "x" (value));
-#else
-      LOG(FATAL) << "Unsupported architecture";
-#endif
-#endif  // defined(__LP64__)
-    } else {
-      SwapMutexWrite64(addr, value);
-    }
-  }
-
-  // Atomically compare the value at "addr" to "old_value", if equal replace it with "new_value"
-  // and return true. Otherwise, don't swap, and return false.
-  // This is fully ordered, i.e. it has C++11 memory_order_seq_cst
-  // semantics (assuming all other accesses use a mutex if this one does).
-  // This has "strong" semantics; if it fails then it is guaranteed that
-  // at some point during the execution of Cas64, *addr was not equal to
-  // old_value.
-  static bool Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
-    if (!NeedSwapMutexes(kRuntimeISA)) {
-      return __sync_bool_compare_and_swap(addr, old_value, new_value);
-    } else {
-      return SwapMutexCas64(old_value, new_value, addr);
-    }
-  }
-
-  // Does the architecture provide reasonable atomic long operations or do we fall back on mutexes?
-  static bool LongAtomicsUseMutexes(InstructionSet isa) {
-    return NeedSwapMutexes(isa);
-  }
-
-  static void ThreadFenceAcquire() {
-    std::atomic_thread_fence(std::memory_order_acquire);
-  }
-
-  static void ThreadFenceRelease() {
-    std::atomic_thread_fence(std::memory_order_release);
-  }
-
-  static void ThreadFenceForConstructor() {
-    #if defined(__aarch64__)
-      __asm__ __volatile__("dmb ishst" : : : "memory");
-    #else
-      std::atomic_thread_fence(std::memory_order_release);
-    #endif
-  }
-
-  static void ThreadFenceSequentiallyConsistent() {
-    std::atomic_thread_fence(std::memory_order_seq_cst);
-  }
-
- private:
-  static Mutex* GetSwapMutex(const volatile int64_t* addr);
-  static int64_t SwapMutexRead64(volatile const int64_t* addr);
-  static void SwapMutexWrite64(volatile int64_t* addr, int64_t val);
-  static bool SwapMutexCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr);
-
-  // We stripe across a bunch of different mutexes to reduce contention.
-  static constexpr size_t kSwapMutexCount = 32;
-  static std::vector<Mutex*>* gSwapMutexes;
-
-  DISALLOW_COPY_AND_ASSIGN(QuasiAtomic);
-};
-
 template<typename T>
 class PACKED(sizeof(T)) Atomic : public std::atomic<T> {
  public:
@@ -409,4 +252,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_ATOMIC_H_
+#endif  // ART_LIBARTBASE_BASE_ATOMIC_H_
diff --git a/runtime/base/bit_field.h b/libartbase/base/bit_field.h
similarity index 95%
rename from runtime/base/bit_field.h
rename to libartbase/base/bit_field.h
index 86007d6..9971735 100644
--- a/runtime/base/bit_field.h
+++ b/libartbase/base/bit_field.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_BIT_FIELD_H_
-#define ART_RUNTIME_BASE_BIT_FIELD_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_FIELD_H_
+#define ART_LIBARTBASE_BASE_BIT_FIELD_H_
 
 #include <android-base/logging.h>
 
@@ -89,4 +89,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_BIT_FIELD_H_
+#endif  // ART_LIBARTBASE_BASE_BIT_FIELD_H_
diff --git a/runtime/base/bit_field_test.cc b/libartbase/base/bit_field_test.cc
similarity index 100%
rename from runtime/base/bit_field_test.cc
rename to libartbase/base/bit_field_test.cc
diff --git a/runtime/base/bit_string.h b/libartbase/base/bit_string.h
similarity index 98%
rename from runtime/base/bit_string.h
rename to libartbase/base/bit_string.h
index 7d9fb70..0e051f3 100644
--- a/runtime/base/bit_string.h
+++ b/libartbase/base/bit_string.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_BIT_STRING_H_
-#define ART_RUNTIME_BASE_BIT_STRING_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_STRING_H_
+#define ART_LIBARTBASE_BASE_BIT_STRING_H_
 
 #include "base/bit_struct.h"
 #include "base/bit_utils.h"
@@ -296,4 +296,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_BIT_STRING_H_
+#endif  // ART_LIBARTBASE_BASE_BIT_STRING_H_
diff --git a/runtime/base/bit_string_test.cc b/libartbase/base/bit_string_test.cc
similarity index 100%
rename from runtime/base/bit_string_test.cc
rename to libartbase/base/bit_string_test.cc
diff --git a/runtime/base/bit_struct.h b/libartbase/base/bit_struct.h
similarity index 98%
rename from runtime/base/bit_struct.h
rename to libartbase/base/bit_struct.h
index 7eb63c6..386b896 100644
--- a/runtime/base/bit_struct.h
+++ b/libartbase/base/bit_struct.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_BIT_STRUCT_H_
-#define ART_RUNTIME_BASE_BIT_STRUCT_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_H_
+#define ART_LIBARTBASE_BASE_BIT_STRUCT_H_
 
 #include "base/bit_utils.h"
 #include "bit_struct_detail.h"
@@ -303,4 +303,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_BIT_STRUCT_H_
+#endif  // ART_LIBARTBASE_BASE_BIT_STRUCT_H_
diff --git a/runtime/base/bit_struct_detail.h b/libartbase/base/bit_struct_detail.h
similarity index 96%
rename from runtime/base/bit_struct_detail.h
rename to libartbase/base/bit_struct_detail.h
index 24f6c4c..facfa61 100644
--- a/runtime/base/bit_struct_detail.h
+++ b/libartbase/base/bit_struct_detail.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
-#define ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_
+#define ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_
 
 #include "base/bit_utils.h"
 #include "globals.h"
@@ -116,4 +116,4 @@
 }  // namespace detail
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
+#endif  // ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_
diff --git a/runtime/base/bit_struct_test.cc b/libartbase/base/bit_struct_test.cc
similarity index 100%
rename from runtime/base/bit_struct_test.cc
rename to libartbase/base/bit_struct_test.cc
diff --git a/runtime/base/bit_vector-inl.h b/libartbase/base/bit_vector-inl.h
similarity index 95%
rename from runtime/base/bit_vector-inl.h
rename to libartbase/base/bit_vector-inl.h
index e67d4e2..7a9f465 100644
--- a/runtime/base/bit_vector-inl.h
+++ b/libartbase/base/bit_vector-inl.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
-#define ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_VECTOR_INL_H_
+#define ART_LIBARTBASE_BASE_BIT_VECTOR_INL_H_
 
 #include "bit_vector.h"
 
@@ -97,4 +97,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
+#endif  // ART_LIBARTBASE_BASE_BIT_VECTOR_INL_H_
diff --git a/runtime/base/bit_vector.cc b/libartbase/base/bit_vector.cc
similarity index 100%
rename from runtime/base/bit_vector.cc
rename to libartbase/base/bit_vector.cc
diff --git a/runtime/base/bit_vector.h b/libartbase/base/bit_vector.h
similarity index 98%
rename from runtime/base/bit_vector.h
rename to libartbase/base/bit_vector.h
index 564092a..2ffa2aa 100644
--- a/runtime/base/bit_vector.h
+++ b/libartbase/base/bit_vector.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_BIT_VECTOR_H_
-#define ART_RUNTIME_BASE_BIT_VECTOR_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_VECTOR_H_
+#define ART_LIBARTBASE_BASE_BIT_VECTOR_H_
 
 #include <stdint.h>
 #include <iterator>
@@ -293,4 +293,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_BIT_VECTOR_H_
+#endif  // ART_LIBARTBASE_BASE_BIT_VECTOR_H_
diff --git a/runtime/base/bit_vector_test.cc b/libartbase/base/bit_vector_test.cc
similarity index 100%
rename from runtime/base/bit_vector_test.cc
rename to libartbase/base/bit_vector_test.cc
diff --git a/runtime/base/bounded_fifo.h b/libartbase/base/bounded_fifo.h
similarity index 92%
rename from runtime/base/bounded_fifo.h
rename to libartbase/base/bounded_fifo.h
index 1520770..444f31a 100644
--- a/runtime/base/bounded_fifo.h
+++ b/libartbase/base/bounded_fifo.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_BOUNDED_FIFO_H_
-#define ART_RUNTIME_BASE_BOUNDED_FIFO_H_
+#ifndef ART_LIBARTBASE_BASE_BOUNDED_FIFO_H_
+#define ART_LIBARTBASE_BASE_BOUNDED_FIFO_H_
 
 #include <android-base/logging.h>
 
@@ -72,4 +72,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_BOUNDED_FIFO_H_
+#endif  // ART_LIBARTBASE_BASE_BOUNDED_FIFO_H_
diff --git a/runtime/base/callee_save_type.h b/libartbase/base/callee_save_type.h
similarity index 91%
rename from runtime/base/callee_save_type.h
rename to libartbase/base/callee_save_type.h
index e9cd63c..3e44a3a 100644
--- a/runtime/base/callee_save_type.h
+++ b/libartbase/base/callee_save_type.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_
-#define ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_
+#ifndef ART_LIBARTBASE_BASE_CALLEE_SAVE_TYPE_H_
+#define ART_LIBARTBASE_BASE_CALLEE_SAVE_TYPE_H_
 
 #include <cstddef>
 #include <ostream>
@@ -44,4 +44,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_
+#endif  // ART_LIBARTBASE_BASE_CALLEE_SAVE_TYPE_H_
diff --git a/runtime/base/dchecked_vector.h b/libartbase/base/dchecked_vector.h
similarity index 97%
rename from runtime/base/dchecked_vector.h
rename to libartbase/base/dchecked_vector.h
index 7236ac3..e9ce6d0 100644
--- a/runtime/base/dchecked_vector.h
+++ b/libartbase/base/dchecked_vector.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_DCHECKED_VECTOR_H_
-#define ART_RUNTIME_BASE_DCHECKED_VECTOR_H_
+#ifndef ART_LIBARTBASE_BASE_DCHECKED_VECTOR_H_
+#define ART_LIBARTBASE_BASE_DCHECKED_VECTOR_H_
 
 #include <algorithm>
 #include <type_traits>
@@ -225,4 +225,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_DCHECKED_VECTOR_H_
+#endif  // ART_LIBARTBASE_BASE_DCHECKED_VECTOR_H_
diff --git a/runtime/base/debug_stack.h b/libartbase/base/debug_stack.h
similarity index 96%
rename from runtime/base/debug_stack.h
rename to libartbase/base/debug_stack.h
index 1331e10..f2d93d4 100644
--- a/runtime/base/debug_stack.h
+++ b/libartbase/base/debug_stack.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_DEBUG_STACK_H_
-#define ART_RUNTIME_BASE_DEBUG_STACK_H_
+#ifndef ART_LIBARTBASE_BASE_DEBUG_STACK_H_
+#define ART_LIBARTBASE_BASE_DEBUG_STACK_H_
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -144,4 +144,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_DEBUG_STACK_H_
+#endif  // ART_LIBARTBASE_BASE_DEBUG_STACK_H_
diff --git a/runtime/base/file_magic.cc b/libartbase/base/file_magic.cc
similarity index 100%
rename from runtime/base/file_magic.cc
rename to libartbase/base/file_magic.cc
diff --git a/runtime/base/file_magic.h b/libartbase/base/file_magic.h
similarity index 87%
rename from runtime/base/file_magic.h
rename to libartbase/base/file_magic.h
index e7bd706..53f551c 100644
--- a/runtime/base/file_magic.h
+++ b/libartbase/base/file_magic.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_FILE_MAGIC_H_
-#define ART_RUNTIME_BASE_FILE_MAGIC_H_
+#ifndef ART_LIBARTBASE_BASE_FILE_MAGIC_H_
+#define ART_LIBARTBASE_BASE_FILE_MAGIC_H_
 
 #include <stdint.h>
 #include <string>
 
-#include "os.h"
+#include "base/os.h"
 
 namespace art {
 
@@ -35,4 +35,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_FILE_MAGIC_H_
+#endif  // ART_LIBARTBASE_BASE_FILE_MAGIC_H_
diff --git a/runtime/base/hex_dump.cc b/libartbase/base/hex_dump.cc
similarity index 100%
rename from runtime/base/hex_dump.cc
rename to libartbase/base/hex_dump.cc
diff --git a/runtime/base/hex_dump.h b/libartbase/base/hex_dump.h
similarity index 91%
rename from runtime/base/hex_dump.h
rename to libartbase/base/hex_dump.h
index 2ce0aef..55f4d53 100644
--- a/runtime/base/hex_dump.h
+++ b/libartbase/base/hex_dump.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_HEX_DUMP_H_
-#define ART_RUNTIME_BASE_HEX_DUMP_H_
+#ifndef ART_LIBARTBASE_BASE_HEX_DUMP_H_
+#define ART_LIBARTBASE_BASE_HEX_DUMP_H_
 
 #include "base/macros.h"
 
@@ -52,4 +52,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_HEX_DUMP_H_
+#endif  // ART_LIBARTBASE_BASE_HEX_DUMP_H_
diff --git a/runtime/base/hex_dump_test.cc b/libartbase/base/hex_dump_test.cc
similarity index 100%
rename from runtime/base/hex_dump_test.cc
rename to libartbase/base/hex_dump_test.cc
diff --git a/runtime/base/histogram-inl.h b/libartbase/base/histogram-inl.h
similarity index 97%
rename from runtime/base/histogram-inl.h
rename to libartbase/base/histogram-inl.h
index 3ce0140..26d01b2 100644
--- a/runtime/base/histogram-inl.h
+++ b/libartbase/base/histogram-inl.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_HISTOGRAM_INL_H_
-#define ART_RUNTIME_BASE_HISTOGRAM_INL_H_
+#ifndef ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
+#define ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
 
 #include <algorithm>
 #include <cmath>
@@ -28,7 +28,7 @@
 
 #include "base/bit_utils.h"
 #include "base/time_utils.h"
-#include "utils.h"
+#include "base/utils.h"
 
 namespace art {
 
@@ -277,4 +277,4 @@
 #pragma clang diagnostic pop
 
 }  // namespace art
-#endif  // ART_RUNTIME_BASE_HISTOGRAM_INL_H_
+#endif  // ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
diff --git a/runtime/base/histogram.h b/libartbase/base/histogram.h
similarity index 96%
rename from runtime/base/histogram.h
rename to libartbase/base/histogram.h
index 7544a9c..39a1866 100644
--- a/runtime/base/histogram.h
+++ b/libartbase/base/histogram.h
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef ART_RUNTIME_BASE_HISTOGRAM_H_
-#define ART_RUNTIME_BASE_HISTOGRAM_H_
+#ifndef ART_LIBARTBASE_BASE_HISTOGRAM_H_
+#define ART_LIBARTBASE_BASE_HISTOGRAM_H_
 
 #include <string>
 #include <vector>
@@ -128,4 +128,4 @@
 };
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_HISTOGRAM_H_
+#endif  // ART_LIBARTBASE_BASE_HISTOGRAM_H_
diff --git a/runtime/base/histogram_test.cc b/libartbase/base/histogram_test.cc
similarity index 100%
rename from runtime/base/histogram_test.cc
rename to libartbase/base/histogram_test.cc
diff --git a/runtime/base/length_prefixed_array.h b/libartbase/base/length_prefixed_array.h
similarity index 95%
rename from runtime/base/length_prefixed_array.h
rename to libartbase/base/length_prefixed_array.h
index 2df5a99..7c09bdd 100644
--- a/runtime/base/length_prefixed_array.h
+++ b/libartbase/base/length_prefixed_array.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_
-#define ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_
+#ifndef ART_LIBARTBASE_BASE_LENGTH_PREFIXED_ARRAY_H_
+#define ART_LIBARTBASE_BASE_LENGTH_PREFIXED_ARRAY_H_
 
 #include <stddef.h>  // for offsetof()
 #include <string.h>  // for memset()
@@ -23,7 +23,7 @@
 #include "base/bit_utils.h"
 #include "base/casts.h"
 #include "base/iteration_range.h"
-#include "stride_iterator.h"
+#include "base/stride_iterator.h"
 
 namespace art {
 
@@ -118,4 +118,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_
+#endif  // ART_LIBARTBASE_BASE_LENGTH_PREFIXED_ARRAY_H_
diff --git a/runtime/base/logging.cc b/libartbase/base/logging.cc
similarity index 97%
rename from runtime/base/logging.cc
rename to libartbase/base/logging.cc
index 78d5429..37b1f82 100644
--- a/runtime/base/logging.cc
+++ b/libartbase/base/logging.cc
@@ -21,8 +21,6 @@
 #include <sstream>
 
 #include "aborting.h"
-#include "mutex.h"
-#include "utils.h"
 
 // Headers for LogMessage::LogLine.
 #ifdef ART_TARGET_ANDROID
@@ -59,8 +57,6 @@
   if (gCmdLine.get() != nullptr) {
     return;
   }
-  // TODO: Move this to a more obvious InitART...
-  Locks::Init();
 
   // Stash the command line for later use. We can use /proc/self/cmdline on Linux to recover this,
   // but we don't have that luxury on the Mac, and there are a couple of argv[0] variants that are
diff --git a/runtime/base/logging.h b/libartbase/base/logging.h
similarity index 96%
rename from runtime/base/logging.h
rename to libartbase/base/logging.h
index c562bdf..fd5fc74 100644
--- a/runtime/base/logging.h
+++ b/libartbase/base/logging.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_LOGGING_H_
-#define ART_RUNTIME_BASE_LOGGING_H_
+#ifndef ART_LIBARTBASE_BASE_LOGGING_H_
+#define ART_LIBARTBASE_BASE_LOGGING_H_
 
 #include <ostream>
 #include <sstream>
@@ -110,4 +110,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_LOGGING_H_
+#endif  // ART_LIBARTBASE_BASE_LOGGING_H_
diff --git a/runtime/base/logging_test.cc b/libartbase/base/logging_test.cc
similarity index 100%
rename from runtime/base/logging_test.cc
rename to libartbase/base/logging_test.cc
diff --git a/runtime/os.h b/libartbase/base/os.h
similarity index 84%
rename from runtime/os.h
rename to libartbase/base/os.h
index 7130fc3..4062d90 100644
--- a/runtime/os.h
+++ b/libartbase/base/os.h
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_OS_H_
-#define ART_RUNTIME_OS_H_
+#ifndef ART_LIBARTBASE_BASE_OS_H_
+#define ART_LIBARTBASE_BASE_OS_H_
+
+#include <stdint.h>
 
 namespace unix_file {
 class FdFile;
@@ -47,12 +49,15 @@
   static File* OpenFileWithFlags(const char* name, int flags, bool auto_flush = true);
 
   // Check if a file exists.
-  static bool FileExists(const char* name);
+  static bool FileExists(const char* name, bool check_file_type = true);
 
   // Check if a directory exists.
   static bool DirectoryExists(const char* name);
+
+  // Get the size of a file (or -1 if it does not exist).
+  static int64_t GetFileSizeBytes(const char* name);
 };
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_OS_H_
+#endif  // ART_LIBARTBASE_BASE_OS_H_
diff --git a/runtime/os_linux.cc b/libartbase/base/os_linux.cc
similarity index 78%
rename from runtime/os_linux.cc
rename to libartbase/base/os_linux.cc
index 1b3e000..cb228bd 100644
--- a/runtime/os_linux.cc
+++ b/libartbase/base/os_linux.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "os.h"
+#include "base/os.h"
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -64,10 +64,14 @@
   return file.release();
 }
 
-bool OS::FileExists(const char* name) {
+bool OS::FileExists(const char* name, bool check_file_type) {
   struct stat st;
   if (stat(name, &st) == 0) {
-    return S_ISREG(st.st_mode);  // TODO: Deal with symlinks?
+    if (check_file_type) {
+      return S_ISREG(st.st_mode);  // TODO: Deal with symlinks?
+    } else {
+      return true;
+    }
   } else {
     return false;
   }
@@ -82,4 +86,15 @@
   }
 }
 
+int64_t OS::GetFileSizeBytes(const char* name) {
+  struct stat st;
+  if (stat(name, &st) == 0) {
+    return st.st_size;  // TODO: Deal with symlinks? According to the documentation,
+                        // the st_size for a symlink is "the length of the pathname
+                        // it contains, without a terminating null byte."
+  } else {
+    return -1;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/base/runtime_debug.cc b/libartbase/base/runtime_debug.cc
similarity index 100%
rename from runtime/base/runtime_debug.cc
rename to libartbase/base/runtime_debug.cc
diff --git a/runtime/base/runtime_debug.h b/libartbase/base/runtime_debug.h
similarity index 93%
rename from runtime/base/runtime_debug.h
rename to libartbase/base/runtime_debug.h
index 89a0361..7d91166 100644
--- a/runtime/base/runtime_debug.h
+++ b/libartbase/base/runtime_debug.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_RUNTIME_DEBUG_H_
-#define ART_RUNTIME_BASE_RUNTIME_DEBUG_H_
+#ifndef ART_LIBARTBASE_BASE_RUNTIME_DEBUG_H_
+#define ART_LIBARTBASE_BASE_RUNTIME_DEBUG_H_
 
 namespace art {
 
@@ -58,4 +58,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_RUNTIME_DEBUG_H_
+#endif  // ART_LIBARTBASE_BASE_RUNTIME_DEBUG_H_
diff --git a/runtime/base/safe_copy.cc b/libartbase/base/safe_copy.cc
similarity index 100%
rename from runtime/base/safe_copy.cc
rename to libartbase/base/safe_copy.cc
diff --git a/runtime/base/safe_copy.h b/libartbase/base/safe_copy.h
similarity index 87%
rename from runtime/base/safe_copy.h
rename to libartbase/base/safe_copy.h
index d0f497c..56cdfec 100644
--- a/runtime/base/safe_copy.h
+++ b/libartbase/base/safe_copy.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_SAFE_COPY_H_
-#define ART_RUNTIME_BASE_SAFE_COPY_H_
+#ifndef ART_LIBARTBASE_BASE_SAFE_COPY_H_
+#define ART_LIBARTBASE_BASE_SAFE_COPY_H_
 
 #include <sys/types.h>
 
@@ -28,4 +28,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_SAFE_COPY_H_
+#endif  // ART_LIBARTBASE_BASE_SAFE_COPY_H_
diff --git a/runtime/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc
similarity index 100%
rename from runtime/base/safe_copy_test.cc
rename to libartbase/base/safe_copy_test.cc
diff --git a/runtime/base/scoped_flock.cc b/libartbase/base/scoped_flock.cc
similarity index 100%
rename from runtime/base/scoped_flock.cc
rename to libartbase/base/scoped_flock.cc
diff --git a/runtime/base/scoped_flock.h b/libartbase/base/scoped_flock.h
similarity index 94%
rename from runtime/base/scoped_flock.h
rename to libartbase/base/scoped_flock.h
index db6c819..476b257 100644
--- a/runtime/base/scoped_flock.h
+++ b/libartbase/base/scoped_flock.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_SCOPED_FLOCK_H_
-#define ART_RUNTIME_BASE_SCOPED_FLOCK_H_
+#ifndef ART_LIBARTBASE_BASE_SCOPED_FLOCK_H_
+#define ART_LIBARTBASE_BASE_SCOPED_FLOCK_H_
 
 #include <memory>
 #include <string>
@@ -23,8 +23,8 @@
 #include <android-base/unique_fd.h>
 
 #include "base/macros.h"
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
-#include "os.h"
 
 namespace art {
 
@@ -85,4 +85,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_SCOPED_FLOCK_H_
+#endif  // ART_LIBARTBASE_BASE_SCOPED_FLOCK_H_
diff --git a/runtime/base/scoped_flock_test.cc b/libartbase/base/scoped_flock_test.cc
similarity index 100%
rename from runtime/base/scoped_flock_test.cc
rename to libartbase/base/scoped_flock_test.cc
diff --git a/runtime/stride_iterator.h b/libartbase/base/stride_iterator.h
similarity index 95%
rename from runtime/stride_iterator.h
rename to libartbase/base/stride_iterator.h
index 511c2c6..67c0d38 100644
--- a/runtime/stride_iterator.h
+++ b/libartbase/base/stride_iterator.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_STRIDE_ITERATOR_H_
-#define ART_RUNTIME_STRIDE_ITERATOR_H_
+#ifndef ART_LIBARTBASE_BASE_STRIDE_ITERATOR_H_
+#define ART_LIBARTBASE_BASE_STRIDE_ITERATOR_H_
 
 #include <iterator>
 
@@ -147,4 +147,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_STRIDE_ITERATOR_H_
+#endif  // ART_LIBARTBASE_BASE_STRIDE_ITERATOR_H_
diff --git a/runtime/base/strlcpy.h b/libartbase/base/strlcpy.h
similarity index 90%
rename from runtime/base/strlcpy.h
rename to libartbase/base/strlcpy.h
index de135ea..98ea34b 100644
--- a/runtime/base/strlcpy.h
+++ b/libartbase/base/strlcpy.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_STRLCPY_H_
-#define ART_RUNTIME_BASE_STRLCPY_H_
+#ifndef ART_LIBARTBASE_BASE_STRLCPY_H_
+#define ART_LIBARTBASE_BASE_STRLCPY_H_
 
 #include <cstdio>
 #include <cstring>
@@ -35,4 +35,4 @@
 
 #endif
 
-#endif  // ART_RUNTIME_BASE_STRLCPY_H_
+#endif  // ART_LIBARTBASE_BASE_STRLCPY_H_
diff --git a/runtime/base/systrace.h b/libartbase/base/systrace.h
similarity index 93%
rename from runtime/base/systrace.h
rename to libartbase/base/systrace.h
index dc2206e..2ff33e8 100644
--- a/runtime/base/systrace.h
+++ b/libartbase/base/systrace.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_SYSTRACE_H_
-#define ART_RUNTIME_BASE_SYSTRACE_H_
+#ifndef ART_LIBARTBASE_BASE_SYSTRACE_H_
+#define ART_LIBARTBASE_BASE_SYSTRACE_H_
 
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 #include <cutils/trace.h>
@@ -80,4 +80,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_SYSTRACE_H_
+#endif  // ART_LIBARTBASE_BASE_SYSTRACE_H_
diff --git a/runtime/base/time_utils.cc b/libartbase/base/time_utils.cc
similarity index 100%
rename from runtime/base/time_utils.cc
rename to libartbase/base/time_utils.cc
diff --git a/runtime/base/time_utils.h b/libartbase/base/time_utils.h
similarity index 95%
rename from runtime/base/time_utils.h
rename to libartbase/base/time_utils.h
index 7648a10..811af5d 100644
--- a/runtime/base/time_utils.h
+++ b/libartbase/base/time_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_TIME_UTILS_H_
-#define ART_RUNTIME_BASE_TIME_UTILS_H_
+#ifndef ART_LIBARTBASE_BASE_TIME_UTILS_H_
+#define ART_LIBARTBASE_BASE_TIME_UTILS_H_
 
 #include <stdint.h>
 #include <time.h>
@@ -101,4 +101,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_TIME_UTILS_H_
+#endif  // ART_LIBARTBASE_BASE_TIME_UTILS_H_
diff --git a/runtime/base/time_utils_test.cc b/libartbase/base/time_utils_test.cc
similarity index 100%
rename from runtime/base/time_utils_test.cc
rename to libartbase/base/time_utils_test.cc
diff --git a/runtime/base/to_str.h b/libartbase/base/to_str.h
similarity index 90%
rename from runtime/base/to_str.h
rename to libartbase/base/to_str.h
index 6b1c84c..74d9584 100644
--- a/runtime/base/to_str.h
+++ b/libartbase/base/to_str.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_TO_STR_H_
-#define ART_RUNTIME_BASE_TO_STR_H_
+#ifndef ART_LIBARTBASE_BASE_TO_STR_H_
+#define ART_LIBARTBASE_BASE_TO_STR_H_
 
 #include <sstream>
 
@@ -47,4 +47,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_TO_STR_H_
+#endif  // ART_LIBARTBASE_BASE_TO_STR_H_
diff --git a/runtime/base/tracking_safe_map.h b/libartbase/base/tracking_safe_map.h
similarity index 85%
rename from runtime/base/tracking_safe_map.h
rename to libartbase/base/tracking_safe_map.h
index 2f3984d..2750de1 100644
--- a/runtime/base/tracking_safe_map.h
+++ b/libartbase/base/tracking_safe_map.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_TRACKING_SAFE_MAP_H_
-#define ART_RUNTIME_BASE_TRACKING_SAFE_MAP_H_
+#ifndef ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_
+#define ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_
 
 #include "base/allocator.h"
 #include "base/safe_map.h"
@@ -29,4 +29,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_TRACKING_SAFE_MAP_H_
+#endif  // ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_
diff --git a/runtime/base/transform_array_ref.h b/libartbase/base/transform_array_ref.h
similarity index 97%
rename from runtime/base/transform_array_ref.h
rename to libartbase/base/transform_array_ref.h
index a4e0bc2..de2739e 100644
--- a/runtime/base/transform_array_ref.h
+++ b/libartbase/base/transform_array_ref.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_TRANSFORM_ARRAY_REF_H_
-#define ART_RUNTIME_BASE_TRANSFORM_ARRAY_REF_H_
+#ifndef ART_LIBARTBASE_BASE_TRANSFORM_ARRAY_REF_H_
+#define ART_LIBARTBASE_BASE_TRANSFORM_ARRAY_REF_H_
 
 #include <type_traits>
 
@@ -193,4 +193,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_TRANSFORM_ARRAY_REF_H_
+#endif  // ART_LIBARTBASE_BASE_TRANSFORM_ARRAY_REF_H_
diff --git a/runtime/base/transform_array_ref_test.cc b/libartbase/base/transform_array_ref_test.cc
similarity index 100%
rename from runtime/base/transform_array_ref_test.cc
rename to libartbase/base/transform_array_ref_test.cc
diff --git a/runtime/base/transform_iterator.h b/libartbase/base/transform_iterator.h
similarity index 97%
rename from runtime/base/transform_iterator.h
rename to libartbase/base/transform_iterator.h
index 9c8f822..82d9f9e 100644
--- a/runtime/base/transform_iterator.h
+++ b/libartbase/base/transform_iterator.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_TRANSFORM_ITERATOR_H_
-#define ART_RUNTIME_BASE_TRANSFORM_ITERATOR_H_
+#ifndef ART_LIBARTBASE_BASE_TRANSFORM_ITERATOR_H_
+#define ART_LIBARTBASE_BASE_TRANSFORM_ITERATOR_H_
 
 #include <iterator>
 #include <type_traits>
@@ -175,4 +175,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_TRANSFORM_ITERATOR_H_
+#endif  // ART_LIBARTBASE_BASE_TRANSFORM_ITERATOR_H_
diff --git a/runtime/base/transform_iterator_test.cc b/libartbase/base/transform_iterator_test.cc
similarity index 100%
rename from runtime/base/transform_iterator_test.cc
rename to libartbase/base/transform_iterator_test.cc
diff --git a/runtime/base/unix_file/README b/libartbase/base/unix_file/README
similarity index 100%
rename from runtime/base/unix_file/README
rename to libartbase/base/unix_file/README
diff --git a/runtime/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc
similarity index 99%
rename from runtime/base/unix_file/fd_file.cc
rename to libartbase/base/unix_file/fd_file.cc
index f9da178..b2881b8 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/libartbase/base/unix_file/fd_file.cc
@@ -31,7 +31,7 @@
 #else
 #include <algorithm>
 #include "base/stl_util.h"
-#include "globals.h"
+#include "base/globals.h"
 #endif
 
 namespace unix_file {
diff --git a/runtime/base/unix_file/fd_file.h b/libartbase/base/unix_file/fd_file.h
similarity index 97%
rename from runtime/base/unix_file/fd_file.h
rename to libartbase/base/unix_file/fd_file.h
index 3fb70f6..fe3317f 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/libartbase/base/unix_file/fd_file.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
+#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_FD_FILE_H_
+#define ART_LIBARTBASE_BASE_UNIX_FILE_FD_FILE_H_
 
 #include <fcntl.h>
 
@@ -192,4 +192,4 @@
 
 }  // namespace unix_file
 
-#endif  // ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
+#endif  // ART_LIBARTBASE_BASE_UNIX_FILE_FD_FILE_H_
diff --git a/runtime/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc
similarity index 100%
rename from runtime/base/unix_file/fd_file_test.cc
rename to libartbase/base/unix_file/fd_file_test.cc
diff --git a/runtime/base/unix_file/random_access_file.h b/libartbase/base/unix_file/random_access_file.h
similarity index 92%
rename from runtime/base/unix_file/random_access_file.h
rename to libartbase/base/unix_file/random_access_file.h
index 31a6dbe..d2124cc 100644
--- a/runtime/base/unix_file/random_access_file.h
+++ b/libartbase/base/unix_file/random_access_file.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
+#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
+#define ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
 
 #include <stdint.h>
 
@@ -65,4 +65,4 @@
 
 }  // namespace unix_file
 
-#endif  // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
+#endif  // ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
diff --git a/runtime/base/unix_file/random_access_file_test.h b/libartbase/base/unix_file/random_access_file_test.h
similarity index 96%
rename from runtime/base/unix_file/random_access_file_test.h
rename to libartbase/base/unix_file/random_access_file_test.h
index 91858c2..1de5f7b 100644
--- a/runtime/base/unix_file/random_access_file_test.h
+++ b/libartbase/base/unix_file/random_access_file_test.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
+#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
+#define ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
 
 #include <errno.h>
 #include <memory>
@@ -180,4 +180,4 @@
 
 }  // namespace unix_file
 
-#endif  // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
+#endif  // ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
diff --git a/runtime/base/unix_file/random_access_file_utils.cc b/libartbase/base/unix_file/random_access_file_utils.cc
similarity index 100%
rename from runtime/base/unix_file/random_access_file_utils.cc
rename to libartbase/base/unix_file/random_access_file_utils.cc
diff --git a/runtime/base/unix_file/random_access_file_utils.h b/libartbase/base/unix_file/random_access_file_utils.h
similarity index 81%
rename from runtime/base/unix_file/random_access_file_utils.h
rename to libartbase/base/unix_file/random_access_file_utils.h
index 30c81c0..47c4c2a 100644
--- a/runtime/base/unix_file/random_access_file_utils.h
+++ b/libartbase/base/unix_file/random_access_file_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
+#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
+#define ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
 
 namespace unix_file {
 
@@ -27,4 +27,4 @@
 
 }  // namespace unix_file
 
-#endif  // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
+#endif  // ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
diff --git a/runtime/utils.cc b/libartbase/base/utils.cc
similarity index 98%
rename from runtime/utils.cc
rename to libartbase/base/utils.cc
index 7246c3d..029cffd 100644
--- a/runtime/utils.cc
+++ b/libartbase/base/utils.cc
@@ -30,8 +30,7 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
-#include "dex/utf-inl.h"
-#include "os.h"
+#include "base/os.h"
 
 #if defined(__APPLE__)
 #include <crt_externs.h>
diff --git a/runtime/utils.h b/libartbase/base/utils.h
similarity index 97%
rename from runtime/utils.h
rename to libartbase/base/utils.h
index 0c3a0a2..c8c5b72 100644
--- a/runtime/utils.h
+++ b/libartbase/base/utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_UTILS_H_
-#define ART_RUNTIME_UTILS_H_
+#ifndef ART_LIBARTBASE_BASE_UTILS_H_
+#define ART_LIBARTBASE_BASE_UTILS_H_
 
 #include <pthread.h>
 #include <stdlib.h>
@@ -25,11 +25,11 @@
 
 #include <android-base/logging.h>
 
-#include "arch/instruction_set.h"
 #include "base/casts.h"
+#include "base/enums.h"
+#include "base/globals.h"
+#include "base/macros.h"
 #include "base/stringpiece.h"
-#include "dex/primitive.h"
-#include "globals.h"
 
 namespace art {
 
@@ -260,4 +260,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_UTILS_H_
+#endif  // ART_LIBARTBASE_BASE_UTILS_H_
diff --git a/libartbase/base/utils_test.cc b/libartbase/base/utils_test.cc
new file mode 100644
index 0000000..892d1fd
--- /dev/null
+++ b/libartbase/base/utils_test.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+class UtilsTest : public testing::Test {};
+
+TEST_F(UtilsTest, PrettySize) {
+  EXPECT_EQ("1GB", PrettySize(1 * GB));
+  EXPECT_EQ("2GB", PrettySize(2 * GB));
+  if (sizeof(size_t) > sizeof(uint32_t)) {
+    EXPECT_EQ("100GB", PrettySize(100 * GB));
+  }
+  EXPECT_EQ("1024KB", PrettySize(1 * MB));
+  EXPECT_EQ("10MB", PrettySize(10 * MB));
+  EXPECT_EQ("100MB", PrettySize(100 * MB));
+  EXPECT_EQ("1024B", PrettySize(1 * KB));
+  EXPECT_EQ("10KB", PrettySize(10 * KB));
+  EXPECT_EQ("100KB", PrettySize(100 * KB));
+  EXPECT_EQ("0B", PrettySize(0));
+  EXPECT_EQ("1B", PrettySize(1));
+  EXPECT_EQ("10B", PrettySize(10));
+  EXPECT_EQ("100B", PrettySize(100));
+  EXPECT_EQ("512B", PrettySize(512));
+}
+
+TEST_F(UtilsTest, Split) {
+  std::vector<std::string> actual;
+  std::vector<std::string> expected;
+
+  expected.clear();
+
+  actual.clear();
+  Split("", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split(":", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  expected.clear();
+  expected.push_back("foo");
+
+  actual.clear();
+  Split(":foo", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split("foo:", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split(":foo:", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  expected.push_back("bar");
+
+  actual.clear();
+  Split("foo:bar", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split(":foo:bar", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split("foo:bar:", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split(":foo:bar:", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  expected.push_back("baz");
+
+  actual.clear();
+  Split("foo:bar:baz", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split(":foo:bar:baz", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split("foo:bar:baz:", ':', &actual);
+  EXPECT_EQ(expected, actual);
+
+  actual.clear();
+  Split(":foo:bar:baz:", ':', &actual);
+  EXPECT_EQ(expected, actual);
+}
+
+TEST_F(UtilsTest, ArrayCount) {
+  int i[64];
+  EXPECT_EQ(ArrayCount(i), 64u);
+  char c[7];
+  EXPECT_EQ(ArrayCount(c), 7u);
+}
+
+TEST_F(UtilsTest, BoundsCheckedCast) {
+  char buffer[64];
+  const char* buffer_end = buffer + ArrayCount(buffer);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end),
+            reinterpret_cast<const uint64_t*>(buffer));
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end),
+            reinterpret_cast<const uint64_t*>(buffer + 56));
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
+}
+
+}  // namespace art
diff --git a/runtime/base/variant_map.h b/libartbase/base/variant_map.h
similarity index 98%
rename from runtime/base/variant_map.h
rename to libartbase/base/variant_map.h
index c480b51..4e02c54 100644
--- a/runtime/base/variant_map.h
+++ b/libartbase/base/variant_map.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_VARIANT_MAP_H_
-#define ART_RUNTIME_BASE_VARIANT_MAP_H_
+#ifndef ART_LIBARTBASE_BASE_VARIANT_MAP_H_
+#define ART_LIBARTBASE_BASE_VARIANT_MAP_H_
 
 #include <memory.h>
 #include <map>
@@ -467,4 +467,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_VARIANT_MAP_H_
+#endif  // ART_LIBARTBASE_BASE_VARIANT_MAP_H_
diff --git a/runtime/base/variant_map_test.cc b/libartbase/base/variant_map_test.cc
similarity index 100%
rename from runtime/base/variant_map_test.cc
rename to libartbase/base/variant_map_test.cc
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index 797b459..988ee03 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -24,6 +24,7 @@
         "dex/descriptors_names.cc",
         "dex/dex_file.cc",
         "dex/dex_file_exception_helpers.cc",
+        "dex/dex_file_layout.cc",
         "dex/dex_file_loader.cc",
         "dex/dex_file_tracking_registrar.cc",
         "dex/dex_file_verifier.cc",
@@ -39,7 +40,6 @@
             static_libs: [
                 "libziparchive",
                 "libz",
-                "libbase",
             ],
             shared_libs: [
                 "libutils",
@@ -54,6 +54,9 @@
     },
     generated_sources: ["dexfile_operator_srcs"],
     shared_libs: [
+        // Important note: relying on libartbase's header_lib is perfectly acceptable.
+        // However, relying on the libartbase shared library introduces further, possibly cyclic,
+	// dependencies for clients outside of ART.
         "liblog",
         // For common macros.
         "libbase",
@@ -77,6 +80,7 @@
     tools: ["generate_operator_out"],
     srcs: [
         "dex/dex_file.h",
+        "dex/dex_file_layout.h",
         "dex/dex_instruction.h",
         "dex/dex_instruction_utils.h",
         "dex/invoke_type.h",
diff --git a/runtime/dex/dex_file_layout.cc b/libdexfile/dex/dex_file_layout.cc
similarity index 82%
rename from runtime/dex/dex_file_layout.cc
rename to libdexfile/dex/dex_file_layout.cc
index d85d61d..1e36e05 100644
--- a/runtime/dex/dex_file_layout.cc
+++ b/libdexfile/dex/dex_file_layout.cc
@@ -18,11 +18,27 @@
 
 #include <sys/mman.h>
 
-#include "base/file_utils.h"
-#include "dex/dex_file.h"
+#include "dex_file.h"
 
 namespace art {
 
+int DexLayoutSection::MadviseLargestPageAlignedRegion(const uint8_t* begin,
+                                                      const uint8_t* end,
+                                                      int advice) {
+  DCHECK_LE(begin, end);
+  begin = AlignUp(begin, kPageSize);
+  end = AlignDown(end, kPageSize);
+  if (begin < end) {
+    // TODO: remove the direct dependency on madvise here.
+    int result = madvise(const_cast<uint8_t*>(begin), end - begin, advice);
+    if (result != 0) {
+      PLOG(WARNING) << "madvise failed " << result;
+    }
+    return result;
+  }
+  return 0;
+}
+
 void DexLayoutSection::Subsection::Madvise(const DexFile* dex_file, int advice) const {
   DCHECK(dex_file != nullptr);
   DCHECK_LT(start_offset_, dex_file->Size());
diff --git a/runtime/dex/dex_file_layout.h b/libdexfile/dex/dex_file_layout.h
similarity index 92%
rename from runtime/dex/dex_file_layout.h
rename to libdexfile/dex/dex_file_layout.h
index 793e3b5..183aefa 100644
--- a/runtime/dex/dex_file_layout.h
+++ b/libdexfile/dex/dex_file_layout.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_
-#define ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_
 
 #include <algorithm>
 #include <cstdint>
@@ -96,6 +96,9 @@
     void Madvise(const DexFile* dex_file, int advice) const;
   };
 
+  // Madvise the largest page-aligned region contained in [begin, end).
+  static int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice);
+
   Subsection parts_[static_cast<size_t>(LayoutType::kLayoutTypeCount)];
 };
 
@@ -121,4 +124,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_
+#endif  // ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 85c7281..dbd90cc 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -34,6 +34,7 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/bit_utils_iterator.h"
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
@@ -69,7 +70,6 @@
 #include "oat.h"
 #include "oat_file-inl.h"
 #include "oat_file_manager.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "stack_map.h"
@@ -172,6 +172,7 @@
     builder_->PrepareDynamicSection(elf_file->GetPath(),
                                     rodata_size,
                                     text_size,
+                                    oat_file_->DataBimgRelRoSize(),
                                     oat_file_->BssSize(),
                                     oat_file_->BssMethodsOffset(),
                                     oat_file_->BssRootsOffset(),
@@ -2179,7 +2180,7 @@
 
     // Intern table is 8-byte aligned.
     uint32_t end_caches = dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size();
-    CHECK_ALIGNED(intern_section.Offset(), sizeof(uint64_t));
+    CHECK_EQ(RoundUp(end_caches, 8U), intern_section.Offset());
     stats_.alignment_bytes += intern_section.Offset() - end_caches;
 
     // Add space between intern table and class table.
diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc
index f125222..34b07d2 100644
--- a/oatdump/oatdump_app_test.cc
+++ b/oatdump/oatdump_app_test.cc
@@ -20,25 +20,29 @@
 
 TEST_F(OatDumpTest, TestAppWithBootImage) {
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}, &error_msg)) << error_msg;
+  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg;
   ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
 }
 TEST_F(OatDumpTest, TestAppWithBootImageStatic) {
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kStatic, {}, &error_msg)) << error_msg;
+  ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg;
   ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestPicAppWithBootImage) {
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--compile-pic"}, &error_msg)) << error_msg;
+  ASSERT_TRUE(
+      GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg))
+      << error_msg;
   ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
 }
 TEST_F(OatDumpTest, TestPicAppWithBootImageStatic) {
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--compile-pic"}, &error_msg)) << error_msg;
+  ASSERT_TRUE(
+      GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg))
+      << error_msg;
   ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
 }
 
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index d4bed6b..fac0bb2 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -25,13 +25,13 @@
 
 #include "arch/instruction_set.h"
 #include "base/file_utils.h"
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "exec_utils.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
-#include "os.h"
-#include "utils.h"
 
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 8b40a7e..07b1529 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -1004,6 +1004,27 @@
   return false;
 }
 
+void EventHandler::SetupFramePopTraceListener(bool enable) {
+  if (enable) {
+    frame_pop_enabled = true;
+    SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable);
+  } else {
+    // remove the listener if we have no outstanding frames.
+    {
+      art::ReaderMutexLock mu(art::Thread::Current(), envs_lock_);
+      for (ArtJvmTiEnv* env : envs) {
+        art::ReaderMutexLock event_mu(art::Thread::Current(), env->event_info_mutex_);
+        if (!env->notify_frames.empty()) {
+          // Leaving FramePop listener since there are unsent FramePop events.
+          return;
+        }
+      }
+      frame_pop_enabled = false;
+    }
+    SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable);
+  }
+}
+
 // Handle special work for the given event type, if necessary.
 void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
   switch (event) {
@@ -1018,14 +1039,14 @@
     case ArtJvmtiEvent::kGarbageCollectionFinish:
       SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
       return;
-    // FramePop can never be disabled once it's been turned on since we would either need to deal
-    // with dangling pointers or have missed events.
-    // TODO We really need to make this not the case anymore.
+    // FramePop can never be disabled once it's been turned on if it was turned off with outstanding
+    // pop-events since we would either need to deal with dangling pointers or have missed events.
     case ArtJvmtiEvent::kFramePop:
-      if (!enable || (enable && frame_pop_enabled)) {
+      if (enable && frame_pop_enabled) {
+        // The frame-pop event was held on by pending events so we don't need to do anything.
         break;
       } else {
-        SetupTraceListener(method_trace_listener_.get(), event, enable);
+        SetupFramePopTraceListener(enable);
         break;
       }
     case ArtJvmtiEvent::kMethodEntry:
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index 8141eff..bf12cb1 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -247,6 +247,9 @@
  private:
   void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable);
 
+  // Specifically handle the FramePop event which it might not always be possible to turn off.
+  void SetupFramePopTraceListener(bool enable);
+
   template <ArtJvmtiEvent kEvent, typename ...Args>
   ALWAYS_INLINE
   inline std::vector<impl::EventHandlerFunc<kEvent>> CollectEvents(art::Thread* thread,
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 16bbee4..ee83c4a 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -115,6 +115,9 @@
     // this before unquickening.
     art::Options options;
     options.compact_dex_level_ = art::CompactDexLevel::kCompactDexLevelNone;
+    // Never verify the output since hidden API flags may cause the dex file verifier to fail.
+    // See b/74063493
+    options.verify_output_ = false;
     // Add a filter to only include the class that has the matching descriptor.
     static constexpr bool kFilterByDescriptor = true;
     if (kFilterByDescriptor) {
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 7a94326..261fe3e 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -39,7 +39,9 @@
 #include "art_jvmti.h"
 #include "base/array_ref.h"
 #include "base/macros.h"
+#include "base/utils.h"
 #include "class_linker.h"
+#include "class_loader_utils.h"
 #include "class_table-inl.h"
 #include "common_throws.h"
 #include "dex/art_dex_file_loader.h"
@@ -72,7 +74,6 @@
 #include "ti_class_loader-inl.h"
 #include "ti_phase.h"
 #include "ti_redefine.h"
-#include "utils.h"
 #include "well_known_classes.h"
 
 namespace openjdkjvmti {
@@ -942,23 +943,12 @@
   art::Handle<art::mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
   std::vector<const art::DexFile*> dex_files;
-  ClassLoaderHelper::VisitDexFileObjects(
-      self,
+  art::VisitClassLoaderDexFiles(
+      soa,
       class_loader,
-      [&] (art::ObjPtr<art::mirror::Object> dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_) {
-        art::StackHandleScope<2> hs(self);
-        art::Handle<art::mirror::Object> h_dex_file(hs.NewHandle(dex_file));
-        art::Handle<art::mirror::LongArray> cookie(
-            hs.NewHandle(ClassLoaderHelper::GetDexFileCookie(h_dex_file)));
-        size_t num_elements = cookie->GetLength();
-        // We need to skip over the oat_file that's the first element. The other elements are all
-        // dex files.
-        for (size_t i = 1; i < num_elements; i++) {
-          dex_files.push_back(
-              reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(cookie->Get(i))));
-        }
-        // Iterate over all dex files.
-        return true;
+      [&](const art::DexFile* dex_file) {
+        dex_files.push_back(dex_file);
+        return true;  // Continue with other dex files.
       });
   // We hold the loader so the dex files won't go away until after this call at worst.
   return CopyClassDescriptors(env, dex_files, count_ptr, classes);
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 4673454..5d430d2 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1402,13 +1402,6 @@
     method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
     // Clear all the intrinsics related flags.
     method.SetNotIntrinsic();
-    // Notify the jit that this method is redefined.
-    art::jit::Jit* jit = driver_->runtime_->GetJit();
-    // Non-invokable methods don't have any JIT data associated with them so we don't need to tell
-    // the jit about them.
-    if (jit != nullptr && method.IsInvokable()) {
-      jit->GetCodeCache()->NotifyMethodRedefined(&method);
-    }
   }
 }
 
@@ -1450,6 +1443,23 @@
   art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData());
   CHECK(!ext.IsNull());
   ext->SetOriginalDexFile(original_dex_file);
+
+  // Notify the jit that all the methods in this class were redefined. Need to do this last since
+  // the jit relies on the dex_file_ being correct (for native methods at least) to find the method
+  // meta-data.
+  art::jit::Jit* jit = driver_->runtime_->GetJit();
+  if (jit != nullptr) {
+    art::PointerSize image_pointer_size =
+        driver_->runtime_->GetClassLinker()->GetImagePointerSize();
+    auto code_cache = jit->GetCodeCache();
+    // Non-invokable methods don't have any JIT data associated with them so we don't need to tell
+    // the jit about them.
+    for (art::ArtMethod& method : mclass->GetDeclaredMethods(image_pointer_size)) {
+      if (method.IsInvokable()) {
+        code_cache->NotifyMethodRedefined(&method);
+      }
+    }
+  }
 }
 
 // Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no new
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 0115772..b9a9a69 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -35,11 +35,14 @@
 #include "base/file_utils.h"
 #include "base/leb128.h"
 #include "base/logging.h"  // For InitLogging.
+#include "base/mutex.h"
 #include "base/memory_tool.h"
+#include "base/os.h"
 #include "base/scoped_flock.h"
 #include "base/stringpiece.h"
 #include "base/unix_file/fd_file.h"
 #include "base/unix_file/random_access_file_utils.h"
+#include "base/utils.h"
 #include "elf_file.h"
 #include "elf_file_impl.h"
 #include "elf_utils.h"
@@ -54,16 +57,16 @@
 #include "mirror/reference.h"
 #include "noop_compiler_callbacks.h"
 #include "offsets.h"
-#include "os.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
-#include "utils.h"
 
 namespace art {
 
 using android::base::StringPrintf;
 
+namespace {
+
 static const OatHeader* GetOatHeader(const ElfFile* elf_file) {
   uint64_t off = 0;
   if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) {
@@ -126,6 +129,38 @@
   return true;
 }
 
+// Holder class for runtime options and related objects.
+class PatchoatRuntimeOptionsHolder {
+ public:
+  PatchoatRuntimeOptionsHolder(const std::string& image_location, InstructionSet isa) {
+    options_.push_back(std::make_pair("compilercallbacks", &callbacks_));
+    img_ = "-Ximage:" + image_location;
+    options_.push_back(std::make_pair(img_.c_str(), nullptr));
+    isa_name_ = GetInstructionSetString(isa);
+    options_.push_back(std::make_pair("imageinstructionset",
+                                      reinterpret_cast<const void*>(isa_name_.c_str())));
+    options_.push_back(std::make_pair("-Xno-sig-chain", nullptr));
+    // We do not want the runtime to attempt to patch the image.
+    options_.push_back(std::make_pair("-Xnorelocate", nullptr));
+    // Don't try to compile.
+    options_.push_back(std::make_pair("-Xnoimage-dex2oat", nullptr));
+    // Do not accept broken image.
+    options_.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr));
+  }
+
+  const RuntimeOptions& GetRuntimeOptions() {
+    return options_;
+  }
+
+ private:
+  RuntimeOptions options_;
+  NoopCompilerCallbacks callbacks_;
+  std::string isa_name_;
+  std::string img_;
+};
+
+}  // namespace
+
 bool PatchOat::GeneratePatch(
     const MemMap& original,
     const MemMap& relocated,
@@ -439,17 +474,10 @@
   TimingLogger::ScopedTiming t("Runtime Setup", timings);
 
   CHECK_NE(isa, InstructionSet::kNone);
-  const char* isa_name = GetInstructionSetString(isa);
 
   // Set up the runtime
-  RuntimeOptions options;
-  NoopCompilerCallbacks callbacks;
-  options.push_back(std::make_pair("compilercallbacks", &callbacks));
-  std::string img = "-Ximage:" + image_location;
-  options.push_back(std::make_pair(img.c_str(), nullptr));
-  options.push_back(std::make_pair("imageinstructionset", reinterpret_cast<const void*>(isa_name)));
-  options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
-  if (!Runtime::Create(options, false)) {
+  PatchoatRuntimeOptionsHolder options_holder(image_location, isa);
+  if (!Runtime::Create(options_holder.GetRuntimeOptions(), false)) {
     LOG(ERROR) << "Unable to initialize runtime";
     return false;
   }
@@ -607,17 +635,10 @@
   TimingLogger::ScopedTiming t("Runtime Setup", timings);
 
   CHECK_NE(isa, InstructionSet::kNone);
-  const char* isa_name = GetInstructionSetString(isa);
 
   // Set up the runtime
-  RuntimeOptions options;
-  NoopCompilerCallbacks callbacks;
-  options.push_back(std::make_pair("compilercallbacks", &callbacks));
-  std::string img = "-Ximage:" + image_location;
-  options.push_back(std::make_pair(img.c_str(), nullptr));
-  options.push_back(std::make_pair("imageinstructionset", reinterpret_cast<const void*>(isa_name)));
-  options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
-  if (!Runtime::Create(options, false)) {
+  PatchoatRuntimeOptionsHolder options_holder(image_location, isa);
+  if (!Runtime::Create(options_holder.GetRuntimeOptions(), false)) {
     LOG(ERROR) << "Unable to initialize runtime";
     return false;
   }
@@ -1177,6 +1198,7 @@
 }
 
 static int patchoat(int argc, char **argv) {
+  Locks::Init();
   InitLogging(argv, Runtime::Abort);
   MemMap::Init();
   const bool debug = kIsDebugBuild;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index feba523..2b1210b 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -21,12 +21,12 @@
 #include "base/enums.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "base/os.h"
 #include "elf_file.h"
 #include "elf_utils.h"
 #include "gc/accounting/space_bitmap.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
-#include "os.h"
 #include "runtime.h"
 
 namespace art {
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index a00b1fa..b509fb4 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -16,8 +16,8 @@
 
 #include "profile_assistant.h"
 
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 6359814..9494f04 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -19,6 +19,7 @@
 #include "android-base/strings.h"
 #include "art_method-inl.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "dex/descriptors_names.h"
 #include "exec_utils.h"
@@ -28,7 +29,6 @@
 #include "obj_ptr-inl.h"
 #include "profile_assistant.h"
 #include "scoped_thread_state_change-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/profman/profman.cc b/profman/profman.cc
index 5551c34..d1cc563 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -34,10 +34,12 @@
 
 #include "base/dumpable.h"
 #include "base/logging.h"  // For InitLogging.
+#include "base/mutex.h"
 #include "base/scoped_flock.h"
 #include "base/stringpiece.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "boot_image_profile.h"
 #include "bytecode_utils.h"
 #include "dex/art_dex_file_loader.h"
@@ -49,7 +51,6 @@
 #include "profile_assistant.h"
 #include "runtime.h"
 #include "type_reference.h"
-#include "utils.h"
 #include "zip_archive.h"
 
 namespace art {
@@ -204,6 +205,7 @@
     original_argc = argc;
     original_argv = argv;
 
+    Locks::Init();
     InitLogging(argv, Runtime::Abort);
 
     // Skip over the command name.
diff --git a/runtime/Android.bp b/runtime/Android.bp
index c017c5f..590a399 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -31,25 +31,14 @@
         "aot_class_linker.cc",
         "art_field.cc",
         "art_method.cc",
-        "atomic.cc",
         "barrier.cc",
-        "base/allocator.cc",
         "base/arena_allocator.cc",
         "base/arena_bit_vector.cc",
-        "base/bit_vector.cc",
-        "base/file_magic.cc",
         "base/file_utils.cc",
-        "base/hex_dump.cc",
-        "base/logging.cc",
         "base/mutex.cc",
-        "base/runtime_debug.cc",
-        "base/safe_copy.cc",
+        "base/quasi_atomic.cc",
         "base/scoped_arena_allocator.cc",
-        "base/scoped_flock.cc",
-        "base/time_utils.cc",
         "base/timing_logger.cc",
-        "base/unix_file/fd_file.cc",
-        "base/unix_file/random_access_file_utils.cc",
         "cha.cc",
         "check_jni.cc",
         "class_linker.cc",
@@ -60,7 +49,6 @@
         "debugger.cc",
         "dex/art_dex_file_loader.cc",
         "dex/dex_file_annotations.cc",
-        "dex/dex_file_layout.cc",
         "dex_to_dex_decompiler.cc",
         "elf_file.cc",
         "exec_utils.cc",
@@ -192,7 +180,6 @@
         "oat_quick_method_header.cc",
         "object_lock.cc",
         "offsets.cc",
-        "os_linux.cc",
         "parsed_options.cc",
         "plugin.cc",
         "quick_exception_handler.cc",
@@ -215,7 +202,6 @@
         "trace.cc",
         "transaction.cc",
         "type_lookup_table.cc",
-        "utils.cc",
         "vdex_file.cc",
         "verifier/instruction_flags.cc",
         "verifier/method_verifier.cc",
@@ -365,7 +351,6 @@
                 // ZipArchive support, the order matters here to get all symbols.
                 "libziparchive",
                 "libz",
-                "libbase",
             ],
         },
         android_arm: {
@@ -406,7 +391,6 @@
     ],
     header_libs: [
         "art_cmdlineparser_headers",
-        "art_libartbase_headers",
         "libnativehelper_header_only",
         "jni_platform_headers",
     ],
@@ -440,13 +424,9 @@
     tools: ["generate_operator_out"],
     srcs: [
         "arch/instruction_set.h",
-        "base/allocator.h",
-        "base/callee_save_type.h",
         "base/mutex.h",
-        "base/unix_file/fd_file.h",
         "class_status.h",
         "debugger.h",
-        "dex/dex_file_layout.h",
         "gc_root.h",
         "gc/allocator_type.h",
         "gc/allocator/rosalloc.h",
@@ -487,8 +467,15 @@
     strip: {
         keep_symbols: true,
     },
-    shared_libs: ["libdexfile"],
-    export_shared_lib_headers: ["libdexfile"],
+    whole_static_libs: [
+        "libartbase",
+    ],
+    shared_libs: [
+        "libdexfile",
+    ],
+    export_shared_lib_headers: [
+        "libdexfile",
+    ],
 }
 
 art_cc_library {
@@ -497,8 +484,15 @@
         "art_debug_defaults",
         "libart_defaults",
     ],
-    shared_libs: ["libdexfiled"],
-    export_shared_lib_headers: ["libdexfiled"],
+    whole_static_libs: [
+        "libartbased",
+    ],
+    shared_libs: [
+        "libdexfiled",
+    ],
+    export_shared_lib_headers: [
+        "libdexfiled",
+    ],
 }
 
 art_cc_library {
@@ -540,23 +534,9 @@
         "arch/x86_64/instruction_set_features_x86_64_test.cc",
         "barrier_test.cc",
         "base/arena_allocator_test.cc",
-        "base/bit_field_test.cc",
-        "base/bit_string_test.cc",
-        "base/bit_struct_test.cc",
-        "base/bit_vector_test.cc",
         "base/file_utils_test.cc",
-        "base/hex_dump_test.cc",
-        "base/histogram_test.cc",
-        "base/logging_test.cc",
         "base/mutex_test.cc",
-        "base/safe_copy_test.cc",
-        "base/scoped_flock_test.cc",
-        "base/time_utils_test.cc",
         "base/timing_logger_test.cc",
-        "base/transform_array_ref_test.cc",
-        "base/transform_iterator_test.cc",
-        "base/variant_map_test.cc",
-        "base/unix_file/fd_file_test.cc",
         "cha_test.cc",
         "class_linker_test.cc",
         "class_loader_context_test.cc",
@@ -615,7 +595,6 @@
         "thread_pool_test.cc",
         "transaction_test.cc",
         "type_lookup_table_test.cc",
-        "utils_test.cc",
         "vdex_file_test.cc",
         "verifier/method_verifier_test.cc",
         "verifier/reg_type_test.cc",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 737d2a8..aa77187 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -601,7 +601,10 @@
      */
 ENTRY art_quick_osr_stub
     SPILL_ALL_CALLEE_SAVE_GPRS             @ Spill regs (9)
+    SAVE_SIZE=9*4
     mov    r11, sp                         @ Save the stack pointer
+    .cfi_def_cfa r11, SAVE_SIZE            @ CFA = r11 + SAVE_SIZE
+    .cfi_remember_state
     mov    r10, r1                         @ Save size of stack
     ldr    r9, [r11, #40]                  @ Move managed thread pointer into r9
     REFRESH_MARKING_REGISTER
@@ -614,14 +617,18 @@
     str    r3, [sp, #8]                    @ Save JValue* result
     mov    ip, #0
     str    ip, [sp]                        @ Store null for ArtMethod* at bottom of frame
-    sub    sp, sp, r1                      @ Reserve space for callee stack
-    mov    r2, r1
-    mov    r1, r0
-    mov    r0, sp
-    bl     memcpy                          @ memcpy (dest r0, src r1, bytes r2)
+    // r11 isn't properly spilled in the osr method, so we need use DWARF expression.
+    // NB: the CFI must be before the call since this is the address gdb will lookup.
+    // NB: gdb expects that cfa_expression returns the CFA value (not address to it).
+    .cfi_escape                            /* CFA = [sp + 4] + SAVE_SIZE */ \
+      0x0f, 6,                             /* DW_CFA_def_cfa_expression(len) */ \
+      0x92, 13, 4,                         /* DW_OP_bregx(reg,offset) */ \
+      0x06,                                /* DW_OP_deref */ \
+      0x23, SAVE_SIZE                      /* DW_OP_plus_uconst(val) */
     bl     .Losr_entry                     @ Call the method
     ldr    r10, [sp, #8]                   @ Restore JValue* result
     ldr    sp, [sp, #4]                    @ Restore saved stack pointer
+    .cfi_def_cfa sp, SAVE_SIZE             @ CFA = sp + SAVE_SIZE
     ldr    r4, [sp, #36]                   @ load shorty
     ldrb   r4, [r4, #0]                    @ load return type
     cmp    r4, #68                         @ Test if result type char == 'D'.
@@ -635,8 +642,15 @@
 .Losr_exit:
     pop    {r4, r5, r6, r7, r8, r9, r10, r11, pc}
 .Losr_entry:
+    .cfi_restore_state
+    .cfi_def_cfa r11, SAVE_SIZE            @ CFA = r11 + SAVE_SIZE
+    sub sp, sp, r10                        @ Reserve space for callee stack
     sub r10, r10, #4
-    str lr, [sp, r10]                     @ Store link register per the compiler ABI
+    str lr, [sp, r10]                      @ Store link register per the compiler ABI
+    mov r2, r10
+    mov r1, r0
+    mov r0, sp
+    bl  memcpy                             @ memcpy (dest r0, src r1, bytes r2)
     bx r6
 END art_quick_osr_stub
 
diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
index 232ec31..987b459 100644
--- a/runtime/arch/arm/quick_entrypoints_cc_arm.cc
+++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
@@ -15,7 +15,7 @@
  */
 
 #include "art_method.h"
-#include "utils.h"  // For RoundUp().
+#include "base/utils.h"  // For RoundUp().
 
 namespace art {
 
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 09fc2c2..375b050 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -613,56 +613,18 @@
 
 
 .macro INVOKE_STUB_CREATE_FRAME
+SAVE_SIZE=6*8   // x4, x5, x19, x20, FP, LR saved.
+    SAVE_TWO_REGS_INCREASE_FRAME x4, x5, SAVE_SIZE
+    SAVE_TWO_REGS x19, x20, 16
+    SAVE_TWO_REGS xFP, xLR, 32
 
-SAVE_SIZE=15*8   // x4, x5, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, SP, LR, FP saved.
-SAVE_SIZE_AND_METHOD=SAVE_SIZE+8
+    mov xFP, sp                            // Use xFP for frame pointer, as it's callee-saved.
+    .cfi_def_cfa_register xFP
 
+    add x10, x2, #(__SIZEOF_POINTER__ + 0xf) // Reserve space for ArtMethod*, arguments and
+    and x10, x10, # ~0xf                   // round up for 16-byte stack alignment.
+    sub sp, sp, x10                        // Adjust SP for ArtMethod*, args and alignment padding.
 
-    mov x9, sp                             // Save stack pointer.
-    .cfi_register sp,x9
-
-    add x10, x2, # SAVE_SIZE_AND_METHOD    // calculate size of frame.
-    sub x10, sp, x10                       // Calculate SP position - saves + ArtMethod* + args
-    and x10, x10, # ~0xf                   // Enforce 16 byte stack alignment.
-    mov sp, x10                            // Set new SP.
-
-    sub x10, x9, #SAVE_SIZE                // Calculate new FP (later). Done here as we must move SP
-    .cfi_def_cfa_register x10              // before this.
-    .cfi_adjust_cfa_offset SAVE_SIZE
-
-    str x28, [x10, #112]
-    .cfi_rel_offset x28, 112
-
-    stp x26, x27, [x10, #96]
-    .cfi_rel_offset x26, 96
-    .cfi_rel_offset x27, 104
-
-    stp x24, x25, [x10, #80]
-    .cfi_rel_offset x24, 80
-    .cfi_rel_offset x25, 88
-
-    stp x22, x23, [x10, #64]
-    .cfi_rel_offset x22, 64
-    .cfi_rel_offset x23, 72
-
-    stp x20, x21, [x10, #48]
-    .cfi_rel_offset x20, 48
-    .cfi_rel_offset x21, 56
-
-    stp x9, x19, [x10, #32]                // Save old stack pointer and x19.
-    .cfi_rel_offset sp, 32
-    .cfi_rel_offset x19, 40
-
-    stp x4, x5, [x10, #16]                 // Save result and shorty addresses.
-    .cfi_rel_offset x4, 16
-    .cfi_rel_offset x5, 24
-
-    stp xFP, xLR, [x10]                    // Store LR & FP.
-    .cfi_rel_offset x29, 0
-    .cfi_rel_offset x30, 8
-
-    mov xFP, x10                           // Use xFP now, as it's callee-saved.
-    .cfi_def_cfa_register x29
     mov xSELF, x3                          // Move thread pointer into SELF register.
 
     // Copy arguments into stack frame.
@@ -677,12 +639,10 @@
     // Copy parameters into the stack. Use numeric label as this is a macro and Clang's assembler
     // does not have unique-id variables.
 1:
-    cmp w2, #0
-    beq 2f
+    cbz w2, 2f
     sub w2, w2, #4      // Need 65536 bytes of range.
     ldr w10, [x1, x2]
     str w10, [x9, x2]
-
     b 1b
 
 2:
@@ -699,29 +659,14 @@
     // Branch to method.
     blr x9
 
-    // Restore return value address and shorty address.
-    ldp x4, x5, [xFP, #16]
-    .cfi_restore x4
-    .cfi_restore x5
+    // Pop the ArtMethod* (null), arguments and alignment padding from the stack.
+    mov sp, xFP
+    .cfi_def_cfa_register sp
 
-    ldr x28, [xFP, #112]
-    .cfi_restore x28
-
-    ldp x26, x27, [xFP, #96]
-    .cfi_restore x26
-    .cfi_restore x27
-
-    ldp x24, x25, [xFP, #80]
-    .cfi_restore x24
-    .cfi_restore x25
-
-    ldp x22, x23, [xFP, #64]
-    .cfi_restore x22
-    .cfi_restore x23
-
-    ldp x20, x21, [xFP, #48]
-    .cfi_restore x20
-    .cfi_restore x21
+    // Restore saved registers including value address and shorty address.
+    RESTORE_TWO_REGS x19, x20, 16
+    RESTORE_TWO_REGS xFP, xLR, 32
+    RESTORE_TWO_REGS_DECREASE_FRAME x4, x5, SAVE_SIZE
 
     // Store result (w0/x0/s0/d0) appropriately, depending on resultType.
     ldrb w10, [x5]
@@ -731,33 +676,28 @@
 
     // Don't set anything for a void type.
     cmp w10, #'V'
-    beq 3f
+    beq 1f
 
     // Is it a double?
     cmp w10, #'D'
-    bne 1f
-    str d0, [x4]
-    b 3f
+    beq 2f
 
-1:  // Is it a float?
+    // Is it a float?
     cmp w10, #'F'
-    bne 2f
-    str s0, [x4]
-    b 3f
+    beq 3f
 
-2:  // Just store x0. Doesn't matter if it is 64 or 32 bits.
+    // Just store x0. Doesn't matter if it is 64 or 32 bits.
     str x0, [x4]
 
-3:  // Finish up.
-    ldp x2, x19, [xFP, #32]   // Restore stack pointer and x19.
-    .cfi_restore x19
-    mov sp, x2
-    .cfi_restore sp
+1:  // Finish up.
+    ret
 
-    ldp xFP, xLR, [xFP]    // Restore old frame pointer and link register.
-    .cfi_restore x29
-    .cfi_restore x30
+2:  // Store double.
+    str d0, [x4]
+    ret
 
+3:  // Store float.
+    str s0, [x4]
     ret
 
 .endm
@@ -1056,7 +996,7 @@
 
 /*  extern"C" void art_quick_osr_stub(void** stack,                x0
  *                                    size_t stack_size_in_bytes,  x1
- *                                    const uin8_t* native_pc,     x2
+ *                                    const uint8_t* native_pc,    x2
  *                                    JValue *result,              x3
  *                                    char   *shorty,              x4
  *                                    Thread *self)                x5
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index b6b24c2..0c45bc9 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -19,7 +19,7 @@
 #include "android-base/strings.h"
 
 #include "base/casts.h"
-#include "utils.h"
+#include "base/utils.h"
 
 #include "arm/instruction_set_features_arm.h"
 #include "arm64/instruction_set_features_arm64.h"
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index badee59..9418caf 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -17,8 +17,9 @@
 #include <string.h>
 
 #include "arch/mips/asm_support_mips.h"
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/logging.h"
+#include "base/quasi_atomic.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/math_entrypoints.h"
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index bdfb942..2acfe14 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -18,7 +18,8 @@
 #include <string.h>
 
 #include "arch/mips64/asm_support_mips64.h"
-#include "atomic.h"
+#include "base/atomic.h"
+#include "base/quasi_atomic.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/math_entrypoints.h"
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index 14b01c5..c9514f5 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -79,6 +79,7 @@
     #define CFI_REL_OFFSET(reg,size) .cfi_rel_offset reg,size
     #define CFI_RESTORE_STATE .cfi_restore_state
     #define CFI_REMEMBER_STATE .cfi_remember_state
+    #define CFI_ESCAPE(...) .cfi_escape __VA_ARGS__
 #else
     // Mac OS' doesn't like cfi_* directives.
     #define CFI_STARTPROC
@@ -90,6 +91,7 @@
     #define CFI_REL_OFFSET(reg,size)
     #define CFI_RESTORE_STATE
     #define CFI_REMEMBER_STATE
+    #define CFI_ESCAPE(...)
 #endif
 
     // Symbols. On a Mac, we need a leading underscore.
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 5a28120..9251161 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2369,20 +2369,28 @@
     PUSH ebx
     PUSH esi
     PUSH edi
+    SAVE_SIZE=20                   // 4 registers and the return address
     mov 4+16(%esp), %esi           // ESI = argument array
     mov 8+16(%esp), %ecx           // ECX = size of args
     mov 12+16(%esp), %ebx          // EBX = pc to call
     mov %esp, %ebp                 // Save stack pointer
+    CFI_DEF_CFA(ebp, SAVE_SIZE)    // CFA = ebp + SAVE_SIZE
+    CFI_REMEMBER_STATE
     andl LITERAL(0xFFFFFFF0), %esp // Align stack
-    PUSH ebp                       // Save old stack pointer
+    pushl %ebp                     // Save old stack pointer
     subl LITERAL(12), %esp         // Align stack
     movl LITERAL(0), (%esp)        // Store null for ArtMethod* slot
+    // ebp isn't properly spilled in the osr method, so we need use DWARF expression.
+    // NB: the CFI must be before the call since this is the address gdb will lookup.
+    // NB: gdb expects that cfa_expression returns the CFA value (not address to it).
+    CFI_ESCAPE(                    /* cfa = [sp + 12] + SAVE_SIZE */ \
+      0x0f, 6,                     /* DW_CFA_def_cfa_expression(len) */ \
+      0x92, 4, 12,                 /* DW_OP_bregx(reg,offset) */ \
+      0x06,                        /* DW_OP_deref */ \
+      0x23, SAVE_SIZE)             /* DW_OP_plus_uconst(val) */
     call .Losr_entry
-
-    // Restore stack pointer.
-    addl LITERAL(12), %esp
-    POP ebp
-    mov %ebp, %esp
+    mov 12(%esp), %esp             // Restore stack pointer.
+    CFI_DEF_CFA(esp, SAVE_SIZE)    // CFA = esp + SAVE_SIZE
 
     // Restore callee saves.
     POP edi
@@ -2405,6 +2413,8 @@
     movss %xmm0, (%ecx)           // Store the floating point result
     ret
 .Losr_entry:
+    CFI_RESTORE_STATE
+    CFI_DEF_CFA(ebp, SAVE_SIZE)   // CFA = ebp + SAVE_SIZE
     subl LITERAL(4), %ecx         // Given stack size contains pushed frame pointer, substract it.
     subl %ecx, %esp
     mov %esp, %edi                // EDI = beginning of stack
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index 3f70958..b867621 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -17,6 +17,7 @@
 #include "art_field.h"
 
 #include "art_field-inl.h"
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "dex/descriptors_names.h"
 #include "gc/accounting/card_table-inl.h"
@@ -26,7 +27,6 @@
 #include "mirror/object_array-inl.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
-#include "utils.h"
 #include "well_known_classes.h"
 
 namespace art {
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 9276994..8bf91d9 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -21,6 +21,7 @@
 
 #include "art_field.h"
 #include "base/callee_save_type.h"
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex/code_item_accessors-inl.h"
@@ -44,7 +45,6 @@
 #include "runtime-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 5d9b729..579e554 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -176,8 +176,9 @@
     return (GetAccessFlags() & kAccFinal) != 0;
   }
 
+  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   bool IsIntrinsic() {
-    return (GetAccessFlags() & kAccIntrinsic) != 0;
+    return (GetAccessFlags<kReadBarrierOption>() & kAccIntrinsic) != 0;
   }
 
   ALWAYS_INLINE void SetIntrinsic(uint32_t intrinsic) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -315,12 +316,13 @@
     return (GetAccessFlags() & kAccPreviouslyWarm) != 0;
   }
 
+  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   void SetPreviouslyWarm() {
-    if (IsIntrinsic()) {
+    if (IsIntrinsic<kReadBarrierOption>()) {
       // kAccPreviouslyWarm overlaps with kAccIntrinsicBits.
       return;
     }
-    AddAccessFlags(kAccPreviouslyWarm);
+    AddAccessFlags<kReadBarrierOption>(kAccPreviouslyWarm);
   }
 
   // Should this method be run in the interpreter and count locks (e.g., failed structured-
@@ -839,8 +841,11 @@
   }
 
   // This setter guarantees atomicity.
+  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   void AddAccessFlags(uint32_t flag) {
-    DCHECK(!IsIntrinsic() || !OverlapsIntrinsicBits(flag) || IsValidIntrinsicUpdate(flag));
+    DCHECK(!IsIntrinsic<kReadBarrierOption>() ||
+           !OverlapsIntrinsicBits(flag) ||
+           IsValidIntrinsicUpdate(flag));
     uint32_t old_access_flags;
     uint32_t new_access_flags;
     do {
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index ecdabba..04bb6ba 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -18,7 +18,7 @@
 
 #include <string>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "common_runtime_test.h"
 #include "mirror/object_array-inl.h"
 #include "thread-current-inl.h"
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index e87f631..292bde0 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -25,9 +25,9 @@
 
 #include <android-base/logging.h>
 
+#include "base/systrace.h"
 #include "mem_map.h"
 #include "mutex.h"
-#include "systrace.h"
 #include "thread-current-inl.h"
 
 namespace art {
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 060b6fa..c301109 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -21,10 +21,10 @@
 #include <stdint.h>
 
 #include "base/bit_utils.h"
+#include "base/debug_stack.h"
+#include "base/dchecked_vector.h"
 #include "base/macros.h"
 #include "base/memory_tool.h"
-#include "dchecked_vector.h"
-#include "debug_stack.h"
 #include "mutex.h"
 
 namespace art {
diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc
index dd3f8d5..1cb3b9c 100644
--- a/runtime/base/file_utils.cc
+++ b/runtime/base/file_utils.cc
@@ -17,10 +17,7 @@
 #include "file_utils.h"
 
 #include <inttypes.h>
-#include <pthread.h>
-#include <sys/mman.h>  // For madvise
 #include <sys/stat.h>
-#include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -47,10 +44,10 @@
 
 #include "base/bit_utils.h"
 #include "base/stl_util.h"
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "dex/dex_file_loader.h"
 #include "globals.h"
-#include "os.h"
 
 #if defined(__APPLE__)
 #include <crt_externs.h>
@@ -308,48 +305,15 @@
   return filename;
 }
 
-bool FileExists(const std::string& filename) {
-  struct stat buffer;
-  return stat(filename.c_str(), &buffer) == 0;
-}
-
-bool FileExistsAndNotEmpty(const std::string& filename) {
-  struct stat buffer;
-  if (stat(filename.c_str(), &buffer) != 0) {
-    return false;
-  }
-  return buffer.st_size > 0;
-}
-
 std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) {
-  const size_t last_ext = filename.find_last_of('.');
-  if (last_ext == std::string::npos) {
+  const size_t last_ext = filename.find_last_of("./");
+  if (last_ext == std::string::npos || filename[last_ext] != '.') {
     return filename + "." + new_extension;
   } else {
     return filename.substr(0, last_ext + 1) + new_extension;
   }
 }
 
-int64_t GetFileSizeBytes(const std::string& filename) {
-  struct stat stat_buf;
-  int rc = stat(filename.c_str(), &stat_buf);
-  return rc == 0 ? stat_buf.st_size : -1;
-}
-
-int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice) {
-  DCHECK_LE(begin, end);
-  begin = AlignUp(begin, kPageSize);
-  end = AlignDown(end, kPageSize);
-  if (begin < end) {
-    int result = madvise(const_cast<uint8_t*>(begin), end - begin, advice);
-    if (result != 0) {
-      PLOG(WARNING) << "madvise failed " << result;
-    }
-    return result;
-  }
-  return 0;
-}
-
 bool LocationIsOnSystem(const char* location) {
   UniqueCPtr<const char[]> path(realpath(location, nullptr));
   return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str());
diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h
index cac0950..7f691d5 100644
--- a/runtime/base/file_utils.h
+++ b/runtime/base/file_utils.h
@@ -65,10 +65,6 @@
 // Returns the vdex filename for the given oat filename.
 std::string GetVdexFilename(const std::string& oat_filename);
 
-// Returns true if the file exists.
-bool FileExists(const std::string& filename);
-bool FileExistsAndNotEmpty(const std::string& filename);
-
 // Returns `filename` with the text after the last occurrence of '.' replaced with
 // `extension`. If `filename` does not contain a period, returns a string containing `filename`,
 // a period, and `new_extension`.
@@ -76,12 +72,6 @@
 //          ReplaceFileExtension("foo", "abc") == "foo.abc"
 std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension);
 
-// Return the file size in bytes or -1 if the file does not exists.
-int64_t GetFileSizeBytes(const std::string& filename);
-
-// Madvise the largest page aligned region within begin and end.
-int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice);
-
 // Return whether the location is on system (i.e. android root).
 bool LocationIsOnSystem(const char* location);
 
diff --git a/runtime/base/file_utils_test.cc b/runtime/base/file_utils_test.cc
index cf6e34d..e74dfe5 100644
--- a/runtime/base/file_utils_test.cc
+++ b/runtime/base/file_utils_test.cc
@@ -94,4 +94,11 @@
   ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), 1 /* overwrite */));
 }
 
+TEST_F(FileUtilsTest, ReplaceFileExtension) {
+  EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file.oat", "vdex"));
+  EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file.oat", "vdex"));
+  EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file", "vdex"));
+  EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file", "vdex"));
+}
+
 }  // namespace art
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 01adbf1..d6dbab4 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -21,9 +21,9 @@
 
 #include "mutex.h"
 
+#include "base/utils.h"
 #include "base/value_object.h"
 #include "thread.h"
-#include "utils.h"
 
 #if ART_USE_FUTEXES
 #include "linux/futex.h"
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 3d2226c..a1f30b6 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -21,7 +21,7 @@
 
 #include "android-base/stringprintf.h"
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/logging.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 4f7001a..4376617 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -26,10 +26,10 @@
 
 #include <android-base/logging.h>
 
-#include "atomic.h"
 #include "base/aborting.h"
+#include "base/atomic.h"
+#include "base/globals.h"
 #include "base/macros.h"
-#include "globals.h"
 
 #if defined(__APPLE__)
 #define ART_USE_FUTEXES 0
@@ -50,6 +50,7 @@
 class SHARED_LOCKABLE MutatorMutex;
 class ScopedContentionRecorder;
 class Thread;
+class Mutex;
 
 // LockLevel is used to impose a lock hierarchy [1] where acquisition of a Mutex at a higher or
 // equal level to a lock a thread holds is invalid. The lock hierarchy achieves a cycle free
diff --git a/runtime/atomic.cc b/runtime/base/quasi_atomic.cc
similarity index 97%
rename from runtime/atomic.cc
rename to runtime/base/quasi_atomic.cc
index 07aceb7..1a82812 100644
--- a/runtime/atomic.cc
+++ b/runtime/base/quasi_atomic.cc
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include "atomic.h"
+#include "base/quasi_atomic.h"
+
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "thread-current-inl.h"
diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h
new file mode 100644
index 0000000..067d01d
--- /dev/null
+++ b/runtime/base/quasi_atomic.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 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_BASE_QUASI_ATOMIC_H_
+#define ART_RUNTIME_BASE_QUASI_ATOMIC_H_
+
+#include <stdint.h>
+#include <atomic>
+#include <limits>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "arch/instruction_set.h"
+#include "base/macros.h"
+
+namespace art {
+
+class Mutex;
+
+// QuasiAtomic encapsulates two separate facilities that we are
+// trying to move away from:  "quasiatomic" 64 bit operations
+// and custom memory fences.  For the time being, they remain
+// exposed.  Clients should be converted to use either class Atomic
+// below whenever possible, and should eventually use C++11 atomics.
+// The two facilities that do not have a good C++11 analog are
+// ThreadFenceForConstructor and Atomic::*JavaData.
+//
+// NOTE: Two "quasiatomic" operations on the exact same memory address
+// are guaranteed to operate atomically with respect to each other,
+// but no guarantees are made about quasiatomic operations mixed with
+// non-quasiatomic operations on the same address, nor about
+// quasiatomic operations that are performed on partially-overlapping
+// memory.
+class QuasiAtomic {
+  static constexpr bool NeedSwapMutexes(InstructionSet isa) {
+    // TODO - mips64 still need this for Cas64 ???
+    return (isa == InstructionSet::kMips) || (isa == InstructionSet::kMips64);
+  }
+
+ public:
+  static void Startup();
+
+  static void Shutdown();
+
+  // Reads the 64-bit value at "addr" without tearing.
+  static int64_t Read64(volatile const int64_t* addr) {
+    if (!NeedSwapMutexes(kRuntimeISA)) {
+      int64_t value;
+#if defined(__LP64__)
+      value = *addr;
+#else
+#if defined(__arm__)
+#if defined(__ARM_FEATURE_LPAE)
+      // With LPAE support (such as Cortex-A15) then ldrd is defined not to tear.
+      __asm__ __volatile__("@ QuasiAtomic::Read64\n"
+        "ldrd     %0, %H0, %1"
+        : "=r" (value)
+        : "m" (*addr));
+#else
+      // Exclusive loads are defined not to tear, clearing the exclusive state isn't necessary.
+      __asm__ __volatile__("@ QuasiAtomic::Read64\n"
+        "ldrexd     %0, %H0, %1"
+        : "=r" (value)
+        : "Q" (*addr));
+#endif
+#elif defined(__i386__)
+  __asm__ __volatile__(
+      "movq     %1, %0\n"
+      : "=x" (value)
+      : "m" (*addr));
+#else
+      LOG(FATAL) << "Unsupported architecture";
+#endif
+#endif  // defined(__LP64__)
+      return value;
+    } else {
+      return SwapMutexRead64(addr);
+    }
+  }
+
+  // Writes to the 64-bit value at "addr" without tearing.
+  static void Write64(volatile int64_t* addr, int64_t value) {
+    if (!NeedSwapMutexes(kRuntimeISA)) {
+#if defined(__LP64__)
+      *addr = value;
+#else
+#if defined(__arm__)
+#if defined(__ARM_FEATURE_LPAE)
+    // If we know that ARM architecture has LPAE (such as Cortex-A15) strd is defined not to tear.
+    __asm__ __volatile__("@ QuasiAtomic::Write64\n"
+      "strd     %1, %H1, %0"
+      : "=m"(*addr)
+      : "r" (value));
+#else
+    // The write is done as a swap so that the cache-line is in the exclusive state for the store.
+    int64_t prev;
+    int status;
+    do {
+      __asm__ __volatile__("@ QuasiAtomic::Write64\n"
+        "ldrexd     %0, %H0, %2\n"
+        "strexd     %1, %3, %H3, %2"
+        : "=&r" (prev), "=&r" (status), "+Q"(*addr)
+        : "r" (value)
+        : "cc");
+      } while (UNLIKELY(status != 0));
+#endif
+#elif defined(__i386__)
+      __asm__ __volatile__(
+        "movq     %1, %0"
+        : "=m" (*addr)
+        : "x" (value));
+#else
+      LOG(FATAL) << "Unsupported architecture";
+#endif
+#endif  // defined(__LP64__)
+    } else {
+      SwapMutexWrite64(addr, value);
+    }
+  }
+
+  // Atomically compare the value at "addr" to "old_value", if equal replace it with "new_value"
+  // and return true. Otherwise, don't swap, and return false.
+  // This is fully ordered, i.e. it has C++11 memory_order_seq_cst
+  // semantics (assuming all other accesses use a mutex if this one does).
+  // This has "strong" semantics; if it fails then it is guaranteed that
+  // at some point during the execution of Cas64, *addr was not equal to
+  // old_value.
+  static bool Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
+    if (!NeedSwapMutexes(kRuntimeISA)) {
+      return __sync_bool_compare_and_swap(addr, old_value, new_value);
+    } else {
+      return SwapMutexCas64(old_value, new_value, addr);
+    }
+  }
+
+  // Does the architecture provide reasonable atomic long operations or do we fall back on mutexes?
+  static bool LongAtomicsUseMutexes(InstructionSet isa) {
+    return NeedSwapMutexes(isa);
+  }
+
+  static void ThreadFenceAcquire() {
+    std::atomic_thread_fence(std::memory_order_acquire);
+  }
+
+  static void ThreadFenceRelease() {
+    std::atomic_thread_fence(std::memory_order_release);
+  }
+
+  static void ThreadFenceForConstructor() {
+    #if defined(__aarch64__)
+      __asm__ __volatile__("dmb ishst" : : : "memory");
+    #else
+      std::atomic_thread_fence(std::memory_order_release);
+    #endif
+  }
+
+  static void ThreadFenceSequentiallyConsistent() {
+    std::atomic_thread_fence(std::memory_order_seq_cst);
+  }
+
+ private:
+  static Mutex* GetSwapMutex(const volatile int64_t* addr);
+  static int64_t SwapMutexRead64(volatile const int64_t* addr);
+  static void SwapMutexWrite64(volatile int64_t* addr, int64_t val);
+  static bool SwapMutexCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr);
+
+  // We stripe across a bunch of different mutexes to reduce contention.
+  static constexpr size_t kSwapMutexCount = 32;
+  static std::vector<Mutex*>* gSwapMutexes;
+
+  DISALLOW_COPY_AND_ASSIGN(QuasiAtomic);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_QUASI_ATOMIC_H_
diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h
index 202902e..a253e2f 100644
--- a/runtime/base/scoped_arena_allocator.h
+++ b/runtime/base/scoped_arena_allocator.h
@@ -20,9 +20,9 @@
 #include <android-base/logging.h>
 
 #include "arena_allocator.h"
+#include "base/debug_stack.h"
+#include "base/globals.h"
 #include "base/macros.h"
-#include "debug_stack.h"
-#include "globals.h"
 
 namespace art {
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c667fe2..72c110a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -38,12 +38,15 @@
 #include "base/casts.h"
 #include "base/leb128.h"
 #include "base/logging.h"
+#include "base/os.h"
+#include "base/quasi_atomic.h"
 #include "base/scoped_arena_containers.h"
 #include "base/scoped_flock.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "base/value_object.h"
 #include "cha.h"
 #include "class_linker-inl.h"
@@ -111,14 +114,12 @@
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
 #include "object_lock.h"
-#include "os.h"
 #include "runtime.h"
 #include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 #include "trace.h"
-#include "utils.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 #include "verifier/method_verifier.h"
 #include "well_known_classes.h"
@@ -267,9 +268,13 @@
   // cannot in general be guaranteed, but in all likelihood leads to breakage down the line.
   if (klass->GetClassLoader() == nullptr && !Runtime::Current()->IsAotCompiler()) {
     std::string tmp;
-    LOG(kIsDebugBuild ? FATAL : WARNING) << klass->GetDescriptor(&tmp)
-                                         << " failed initialization: "
-                                         << self->GetException()->Dump();
+    // We want to LOG(FATAL) on debug builds since this really shouldn't be happening but we need to
+    // make sure to only do it if we don't have AsyncExceptions being thrown around since those
+    // could have caused the error.
+    bool known_impossible = kIsDebugBuild && !Runtime::Current()->AreAsyncExceptionsThrown();
+    LOG(known_impossible ? FATAL : WARNING) << klass->GetDescriptor(&tmp)
+                                            << " failed initialization: "
+                                            << self->GetException()->Dump();
   }
 
   env->ExceptionClear();
@@ -1122,12 +1127,8 @@
   DCHECK(out_dex_file_names != nullptr);
   DCHECK(error_msg != nullptr);
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  ArtField* const dex_path_list_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
-  ArtField* const dex_elements_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
-  CHECK(dex_path_list_field != nullptr);
-  CHECK(dex_elements_field != nullptr);
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> handle(hs.NewHandle(class_loader));
   while (!ClassLinker::IsBootClassLoader(soa, class_loader)) {
     if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
         class_loader->GetClass()) {
@@ -1136,32 +1137,29 @@
       // Unsupported class loader.
       return false;
     }
-    ObjPtr<mirror::Object> dex_path_list = dex_path_list_field->GetObject(class_loader);
-    if (dex_path_list != nullptr) {
-      // DexPathList has an array dexElements of Elements[] which each contain a dex file.
-      ObjPtr<mirror::Object> dex_elements_obj = dex_elements_field->GetObject(dex_path_list);
-      // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
-      // at the mCookie which is a DexFile vector.
-      if (dex_elements_obj != nullptr) {
-        ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements =
-            dex_elements_obj->AsObjectArray<mirror::Object>();
-        // Reverse order since we insert the parent at the front.
-        for (int32_t i = dex_elements->GetLength() - 1; i >= 0; --i) {
-          ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
-          if (element == nullptr) {
-            *error_msg = StringPrintf("Null dex element at index %d", i);
-            return false;
-          }
-          ObjPtr<mirror::String> name;
-          if (!GetDexPathListElementName(element, &name)) {
-            *error_msg = StringPrintf("Invalid dex path list element at index %d", i);
-            return false;
-          }
-          if (name != nullptr) {
-            out_dex_file_names->push_front(name.Ptr());
-          }
-        }
+    // Get element names. Sets error to true on failure.
+    auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      if (element == nullptr) {
+        *error_msg = "Null dex element";
+        *error = true;  // Null element is a critical error.
+        return false;   // Had an error, stop the visit.
       }
+      ObjPtr<mirror::String> name;
+      if (!GetDexPathListElementName(element, &name)) {
+        *error_msg = "Invalid dex path list element";
+        *error = false;  // Invalid element is not a critical error.
+        return false;    // Stop the visit.
+      }
+      if (name != nullptr) {
+        out_dex_file_names->push_front(name.Ptr());
+      }
+      return true;  // Continue with the next Element.
+    };
+    bool error = VisitClassLoaderDexElements(soa, handle, add_element_names, /* error */ false);
+    if (error) {
+      // An error occurred during DexPathList Element visiting.
+      return false;
     }
     class_loader = class_loader->GetParent();
   }
@@ -1813,21 +1811,6 @@
     header.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
   }
 
-  if (!app_image) {
-    // Make the string intern table and class table immutable for boot image.
-    // PIC app oat files may mmap a read-only copy into their own .bss section,
-    // so enforce that the data in the boot image tables remains unchanged.
-    //
-    // We cannot do that for app image even after the fixup as some interned
-    // String references may actually end up pointing to moveable Strings.
-    uint8_t* const_section_begin = space->Begin() + header.GetBootImageConstantTablesOffset();
-    CheckedCall(mprotect,
-                "protect constant tables",
-                const_section_begin,
-                header.GetBootImageConstantTablesSize(),
-                PROT_READ);
-  }
-
   ClassTable* class_table = nullptr;
   {
     WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -2444,71 +2427,33 @@
     const char* descriptor,
     size_t hash,
     Handle<mirror::ClassLoader> class_loader) {
-  CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+  DCHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
       << "Unexpected class loader for descriptor " << descriptor;
 
-  Thread* self = soa.Self();
-  ArtField* const cookie_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-  ArtField* const dex_file_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-  ObjPtr<mirror::Object> dex_path_list =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
-          GetObject(class_loader.Get());
-  if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
-    // DexPathList has an array dexElements of Elements[] which each contain a dex file.
-    ObjPtr<mirror::Object> dex_elements_obj =
-        jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
-            GetObject(dex_path_list);
-    // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
-    // at the mCookie which is a DexFile vector.
-    if (dex_elements_obj != nullptr) {
-      StackHandleScope<1> hs(self);
-      Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
-          hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
-      for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
-        ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
-        if (element == nullptr) {
-          // Should never happen, fall back to java code to throw a NPE.
-          break;
-        }
-        ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
-        if (dex_file != nullptr) {
-          ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
-          if (long_array == nullptr) {
-            // This should never happen so log a warning.
-            LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
-            break;
-          }
-          int32_t long_array_size = long_array->GetLength();
-          // First element is the oat file.
-          for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
-            const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
-                long_array->GetWithoutChecks(j)));
-            const DexFile::ClassDef* dex_class_def =
-                OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
-            if (dex_class_def != nullptr) {
-              ObjPtr<mirror::Class> klass = DefineClass(self,
-                                                        descriptor,
-                                                        hash,
-                                                        class_loader,
-                                                        *cp_dex_file,
-                                                        *dex_class_def);
-              if (klass == nullptr) {
-                CHECK(self->IsExceptionPending()) << descriptor;
-                self->ClearException();
-                // TODO: Is it really right to break here, and not check the other dex files?
-                return nullptr;
-              }
-              return klass;
-            }
-          }
-        }
+  ObjPtr<mirror::Class> ret;
+  auto define_class = [&](const DexFile* cp_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) {
+    const DexFile::ClassDef* dex_class_def =
+        OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
+    if (dex_class_def != nullptr) {
+      ObjPtr<mirror::Class> klass = DefineClass(soa.Self(),
+                                                descriptor,
+                                                hash,
+                                                class_loader,
+                                                *cp_dex_file,
+                                                *dex_class_def);
+      if (klass == nullptr) {
+        CHECK(soa.Self()->IsExceptionPending()) << descriptor;
+        soa.Self()->ClearException();
+        // TODO: Is it really right to break here, and not check the other dex files?
       }
+      ret = klass;
+      return false;  // Found a Class (or error == nullptr), stop visit.
     }
-    self->AssertNoPendingException();
-  }
-  return nullptr;
+    return true;  // Continue with the next DexFile.
+  };
+
+  VisitClassLoaderDexFiles(soa, class_loader, define_class);
+  return ret;
 }
 
 mirror::Class* ClassLinker::FindClass(Thread* self,
@@ -3405,9 +3350,10 @@
   CHECK_EQ(dex_cache_location, dex_file_suffix);
   const OatFile* oat_file =
       (dex_file.GetOatDexFile() != nullptr) ? dex_file.GetOatDexFile()->GetOatFile() : nullptr;
-  // Clean up pass to remove null dex caches. Also check if we need to initialize OatFile .bss.
-  // Null dex caches can occur due to class unloading and we are lazily removing null entries.
-  bool initialize_oat_file_bss = (oat_file != nullptr);
+  // Clean up pass to remove null dex caches; null dex caches can occur due to class unloading
+  // and we are lazily removing null entries. Also check if we need to initialize OatFile data
+  // (.data.bimg.rel.ro and .bss sections) needed for code execution.
+  bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable();
   JavaVMExt* const vm = self->GetJniEnv()->GetVm();
   for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) {
     DexCacheData data = *it;
@@ -3415,15 +3361,36 @@
       vm->DeleteWeakGlobalRef(self, data.weak_root);
       it = dex_caches_.erase(it);
     } else {
-      if (initialize_oat_file_bss &&
+      if (initialize_oat_file_data &&
           it->dex_file->GetOatDexFile() != nullptr &&
           it->dex_file->GetOatDexFile()->GetOatFile() == oat_file) {
-        initialize_oat_file_bss = false;  // Already initialized.
+        initialize_oat_file_data = false;  // Already initialized.
       }
       ++it;
     }
   }
-  if (initialize_oat_file_bss) {
+  if (initialize_oat_file_data) {
+    // Initialize the .data.bimg.rel.ro section.
+    if (!oat_file->GetBootImageRelocations().empty()) {
+      uint8_t* reloc_begin = const_cast<uint8_t*>(oat_file->DataBimgRelRoBegin());
+      CheckedCall(mprotect,
+                  "un-protect boot image relocations",
+                  reloc_begin,
+                  oat_file->DataBimgRelRoSize(),
+                  PROT_READ | PROT_WRITE);
+      uint32_t boot_image_begin = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(
+          Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin()));
+      for (const uint32_t& relocation : oat_file->GetBootImageRelocations()) {
+        const_cast<uint32_t&>(relocation) += boot_image_begin;
+      }
+      CheckedCall(mprotect,
+                  "protect boot image relocations",
+                  reloc_begin,
+                  oat_file->DataBimgRelRoSize(),
+                  PROT_READ);
+    }
+
+    // Initialize the .bss section.
     // TODO: Pre-initialize from boot/app image?
     ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod();
     for (ArtMethod*& entry : oat_file->GetBssMethods()) {
@@ -4977,8 +4944,14 @@
   // interface since we can still avoid the traversal. This is purely a performance optimization.
   if (result) {
     // TODO This should be done in a better way
-    ObjectLock<mirror::Class> lock(self, iface);
-    iface->SetRecursivelyInitialized();
+    // Note: Use a try-lock to avoid blocking when someone else is holding the lock on this
+    //       interface. It is bad (Java) style, but not impossible. Marking the recursive
+    //       initialization is a performance optimization (to avoid another idempotent visit
+    //       for other implementing classes/interfaces), and can be revisited later.
+    ObjectTryLock<mirror::Class> lock(self, iface);
+    if (lock.Acquired()) {
+      iface->SetRecursivelyInitialized();
+    }
   }
   return result;
 }
@@ -7901,42 +7874,19 @@
     if (loader->GetClass() == path_class_loader ||
         loader->GetClass() == dex_class_loader ||
         loader->GetClass() == delegate_last_class_loader) {
-      ArtField* const cookie_field =
-          jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-      ArtField* const dex_file_field =
-          jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-      ObjPtr<mirror::Object> dex_path_list =
-          jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
-              GetObject(loader);
-      if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
-        ObjPtr<mirror::Object> dex_elements_obj =
-            jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
-            GetObject(dex_path_list);
-        if (dex_elements_obj != nullptr) {
-          ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements =
-              dex_elements_obj->AsObjectArray<mirror::Object>();
-          oss << "(";
-          const char* path_separator = "";
-          for (int32_t i = 0; i != dex_elements->GetLength(); ++i) {
-            ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
-            ObjPtr<mirror::Object> dex_file =
-                (element != nullptr) ? dex_file_field->GetObject(element) : nullptr;
-            ObjPtr<mirror::LongArray> long_array =
-                (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr;
-            if (long_array != nullptr) {
-              int32_t long_array_size = long_array->GetLength();
-              // First element is the oat file.
-              for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
-                const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(
-                    static_cast<uintptr_t>(long_array->GetWithoutChecks(j)));
-                oss << path_separator << cp_dex_file->GetLocation();
-                path_separator = ":";
-              }
-            }
-          }
-          oss << ")";
-        }
-      }
+      oss << "(";
+      ScopedObjectAccessUnchecked soa(Thread::Current());
+      StackHandleScope<1> hs(soa.Self());
+      Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
+      const char* path_separator = "";
+      VisitClassLoaderDexFiles(soa,
+                               handle,
+                               [&](const DexFile* dex_file) {
+                                 oss << path_separator << dex_file->GetLocation();
+                                 path_separator = ":";
+                                 return true;  // Continue with the next DexFile.
+                               });
+      oss << ")";
     }
   }
 
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 712e3ae..8d6b3d2 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1316,7 +1316,6 @@
   friend class ImageDumper;  // for DexLock
   friend struct linker::CompilationHelper;  // For Compile in ImageTest.
   friend class linker::ImageWriter;  // for GetClassRoots
-  friend class linker::OatWriter;  // for boot image string/class table slot address lookup.
   friend class JniCompilerTest;  // for GetRuntimeQuickGenericJniStub
   friend class JniInternalTest;  // for GetRuntimeQuickGenericJniStub
   friend class VMClassLoader;  // for LookupClass and FindClassInBaseDexClassLoader.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 80ef654..8cd0604 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1596,6 +1596,63 @@
   LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c);
 }
 
+TEST_F(ClassLinkerTest, PrettyClass) {
+  ScopedObjectAccess soa(Thread::Current());
+  EXPECT_EQ("null", mirror::Class::PrettyClass(nullptr));
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
+  ASSERT_TRUE(c != nullptr);
+  mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
+  EXPECT_EQ("java.lang.Class<java.lang.String[]>", mirror::Class::PrettyClass(o->GetClass()));
+}
+
+TEST_F(ClassLinkerTest, PrettyClassAndClassLoader) {
+  ScopedObjectAccess soa(Thread::Current());
+  EXPECT_EQ("null", mirror::Class::PrettyClassAndClassLoader(nullptr));
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
+  ASSERT_TRUE(c != nullptr);
+  mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
+  EXPECT_EQ("java.lang.Class<java.lang.String[],null>",
+            mirror::Class::PrettyClassAndClassLoader(o->GetClass()));
+}
+
+TEST_F(ClassLinkerTest, PrettyField) {
+  ScopedObjectAccess soa(Thread::Current());
+  EXPECT_EQ("null", ArtField::PrettyField(nullptr));
+
+  mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(),
+                                                                   "Ljava/lang/String;");
+
+  ArtField* f;
+  f = java_lang_String->FindDeclaredInstanceField("count", "I");
+  EXPECT_EQ("int java.lang.String.count", f->PrettyField());
+  EXPECT_EQ("java.lang.String.count", f->PrettyField(false));
+}
+
+TEST_F(ClassLinkerTest, JniShortName_JniLongName) {
+  ScopedObjectAccess soa(Thread::Current());
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
+  ASSERT_TRUE(c != nullptr);
+  ArtMethod* m;
+
+  m = c->FindClassMethod("charAt", "(I)C", kRuntimePointerSize);
+  ASSERT_TRUE(m != nullptr);
+  ASSERT_FALSE(m->IsDirect());
+  EXPECT_EQ("Java_java_lang_String_charAt", m->JniShortName());
+  EXPECT_EQ("Java_java_lang_String_charAt__I", m->JniLongName());
+
+  m = c->FindClassMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize);
+  ASSERT_TRUE(m != nullptr);
+  ASSERT_FALSE(m->IsDirect());
+  EXPECT_EQ("Java_java_lang_String_indexOf", m->JniShortName());
+  EXPECT_EQ("Java_java_lang_String_indexOf__Ljava_lang_String_2I", m->JniLongName());
+
+  m = c->FindClassMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize);
+  ASSERT_TRUE(m != nullptr);
+  ASSERT_TRUE(m->IsStatic());
+  EXPECT_EQ("Java_java_lang_String_copyValueOf", m->JniShortName());
+  EXPECT_EQ("Java_java_lang_String_copyValueOf___3CII", m->JniLongName());
+}
+
 class ClassLinkerClassLoaderTest : public ClassLinkerTest {
  protected:
   // Verifies that the class identified by the given descriptor is loaded with
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index d160a51..1439f11 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -17,8 +17,12 @@
 #ifndef ART_RUNTIME_CLASS_LOADER_UTILS_H_
 #define ART_RUNTIME_CLASS_LOADER_UTILS_H_
 
+#include "art_field-inl.h"
+#include "base/mutex.h"
 #include "handle_scope.h"
+#include "jni_internal.h"
 #include "mirror/class_loader.h"
+#include "native/dalvik_system_DexFile.h"
 #include "scoped_thread_state_change-inl.h"
 #include "well_known_classes.h"
 
@@ -26,7 +30,7 @@
 
 // Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
 // (they both have the same behaviour with respect to class lockup order)
-static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                    Handle<mirror::ClassLoader> class_loader)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   mirror::Class* class_loader_class = class_loader->GetClass();
@@ -37,7 +41,7 @@
           soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
 }
 
-static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                       Handle<mirror::ClassLoader> class_loader)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   mirror::Class* class_loader_class = class_loader->GetClass();
@@ -45,6 +49,114 @@
       soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
 }
 
+// Visit the DexPathList$Element instances in the given classloader with the given visitor.
+// Constraints on the visitor:
+//   * The visitor should return true to continue visiting more Elements.
+//   * The last argument of the visitor is an out argument of RetType. It will be returned
+//     when the visitor ends the visit (by returning false).
+// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
+template <typename Visitor, typename RetType>
+inline RetType VisitClassLoaderDexElements(ScopedObjectAccessAlreadyRunnable& soa,
+                                           Handle<mirror::ClassLoader> class_loader,
+                                           Visitor fn,
+                                           RetType defaultReturn)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Thread* self = soa.Self();
+  ObjPtr<mirror::Object> dex_path_list =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
+          GetObject(class_loader.Get());
+  if (dex_path_list != nullptr) {
+    // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+    ObjPtr<mirror::Object> dex_elements_obj =
+        jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+            GetObject(dex_path_list);
+    // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+    // at the mCookie which is a DexFile vector.
+    if (dex_elements_obj != nullptr) {
+      StackHandleScope<1> hs(self);
+      Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+          hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+      for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+        ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
+        if (element == nullptr) {
+          // Should never happen, fail.
+          break;
+        }
+        RetType ret_value;
+        if (!fn(element, &ret_value)) {
+          return ret_value;
+        }
+      }
+    }
+    self->AssertNoPendingException();
+  }
+  return defaultReturn;
+}
+
+// Visit the DexFiles in the given classloader with the given visitor.
+// Constraints on the visitor:
+//   * The visitor should return true to continue visiting more DexFiles.
+//   * The last argument of the visitor is an out argument of RetType. It will be returned
+//     when the visitor ends the visit (by returning false).
+// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
+template <typename Visitor, typename RetType>
+inline RetType VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+                                        Handle<mirror::ClassLoader> class_loader,
+                                        Visitor fn,
+                                        RetType defaultReturn)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ArtField* const cookie_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+  ArtField* const dex_file_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+  if (dex_file_field == nullptr || cookie_field == nullptr) {
+    return defaultReturn;
+  }
+  auto visit_dex_files = [&](ObjPtr<mirror::Object> element, RetType* ret)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
+    if (dex_file != nullptr) {
+      ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
+      if (long_array == nullptr) {
+        // This should never happen so log a warning.
+        LOG(WARNING) << "Null DexFile::mCookie";
+        *ret = defaultReturn;
+        return true;
+      }
+      int32_t long_array_size = long_array->GetLength();
+      // First element is the oat file.
+      for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
+        const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
+            long_array->GetWithoutChecks(j)));
+        RetType ret_value;
+        if (!fn(cp_dex_file, /* out */ &ret_value)) {
+          *ret = ret_value;
+          return false;
+        }
+      }
+    }
+    return true;
+  };
+
+  return VisitClassLoaderDexElements(soa, class_loader, visit_dex_files, defaultReturn);
+}
+
+// Simplified version of the above, w/o out argument.
+template <typename Visitor>
+inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+                                     Handle<mirror::ClassLoader> class_loader,
+                                     Visitor fn)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  auto helper = [&fn](const art::DexFile* dex_file, void** ATTRIBUTE_UNUSED)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return fn(dex_file);
+  };
+  VisitClassLoaderDexFiles<decltype(helper), void*>(soa,
+                                                    class_loader,
+                                                    helper,
+                                                    /* default */ nullptr);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_CLASS_LOADER_UTILS_H_
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 48129b1..52e9f82 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -295,7 +295,6 @@
   std::vector<const OatFile*> oat_files_ GUARDED_BY(lock_);
 
   friend class linker::ImageWriter;  // for InsertWithoutLocks.
-  friend class linker::OatWriter;  // for boot class TableSlot address lookup.
 };
 
 }  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index e4fbc86..05159e2 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -30,10 +30,13 @@
 #include "base/file_utils.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/mutex.h"
+#include "base/os.h"
 #include "base/runtime_debug.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
+#include "class_loader_utils.h"
 #include "compiler_callbacks.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file-inl.h"
@@ -51,7 +54,6 @@
 #include "mirror/class_loader.h"
 #include "native/dalvik_system_DexFile.h"
 #include "noop_compiler_callbacks.h"
-#include "os.h"
 #include "runtime-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
@@ -63,6 +65,7 @@
   // everything else. In case you want to see all messages, comment out the line.
   setenv("ANDROID_LOG_TAGS", "*:e", 1);
 
+  art::Locks::Init();
   art::InitLogging(argv, art::Runtime::Abort);
   LOG(INFO) << "Running main() from common_runtime_test.cc...";
   testing::InitGoogleTest(&argc, argv);
@@ -541,58 +544,23 @@
 std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(
     ScopedObjectAccess& soa,
     Handle<mirror::ClassLoader> class_loader) {
-  std::vector<const DexFile*> ret;
-
   DCHECK(
       (class_loader->GetClass() ==
           soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
       (class_loader->GetClass() ==
           soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader)));
 
-  // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
-  // We need to get the DexPathList and loop through it.
-  ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-  ArtField* dex_file_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-  ObjPtr<mirror::Object> dex_path_list =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
-          GetObject(class_loader.Get());
-  if (dex_path_list != nullptr && dex_file_field!= nullptr && cookie_field != nullptr) {
-    // DexPathList has an array dexElements of Elements[] which each contain a dex file.
-    ObjPtr<mirror::Object> dex_elements_obj =
-        jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
-            GetObject(dex_path_list);
-    // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
-    // at the mCookie which is a DexFile vector.
-    if (dex_elements_obj != nullptr) {
-      StackHandleScope<1> hs(soa.Self());
-      Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
-          hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
-      for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
-        ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
-        if (element == nullptr) {
-          // Should never happen, fall back to java code to throw a NPE.
-          break;
-        }
-        ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
-        if (dex_file != nullptr) {
-          ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
-          DCHECK(long_array != nullptr);
-          int32_t long_array_size = long_array->GetLength();
-          for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
-            const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
-                long_array->GetWithoutChecks(j)));
-            if (cp_dex_file == nullptr) {
-              LOG(WARNING) << "Null DexFile";
-              continue;
-            }
-            ret.push_back(cp_dex_file);
-          }
-        }
-      }
-    }
-  }
-
+  std::vector<const DexFile*> ret;
+  VisitClassLoaderDexFiles(soa,
+                           class_loader,
+                           [&](const DexFile* cp_dex_file) {
+                             if (cp_dex_file == nullptr) {
+                               LOG(WARNING) << "Null DexFile";
+                             } else {
+                               ret.push_back(cp_dex_file);
+                             }
+                             return true;
+                           });
   return ret;
 }
 
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 85b0dbb..83a1f9a 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -26,13 +26,13 @@
 
 #include "arch/instruction_set.h"
 #include "base/mutex.h"
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/compact_dex_level.h"
 #include "globals.h"
 // TODO: Add inl file and avoid including inl.
 #include "obj_ptr-inl.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 
 namespace art {
@@ -332,12 +332,6 @@
     return; \
   }
 
-#define TEST_DISABLED_FOR_COMPACT_DEX() \
-  if (kDefaultCompactDexLevel != CompactDexLevel::kCompactDexLevelNone) { \
-    printf("WARNING: TEST DISABLED FOR COMPACT DEX\n"); \
-    return; \
-  }
-
 #define TEST_DISABLED_FOR_HEAP_POISONING() \
   if (kPoisonHeapReferences) { \
     printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 8f65c66..7484dd9 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -884,8 +884,8 @@
 
 // WrongMethodTypeException
 
-void ThrowWrongMethodTypeException(mirror::MethodType* expected_type,
-                                   mirror::MethodType* actual_type) {
+void ThrowWrongMethodTypeException(ObjPtr<mirror::MethodType> expected_type,
+                                   ObjPtr<mirror::MethodType> actual_type) {
   ThrowException("Ljava/lang/invoke/WrongMethodTypeException;",
                  nullptr,
                  StringPrintf("Expected %s but was %s",
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index e9baa4f..29a056e 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -270,8 +270,8 @@
 
 // WrongMethodTypeException
 
-void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
-                                   mirror::MethodType* callsite_type)
+void ThrowWrongMethodTypeException(ObjPtr<mirror::MethodType> callee_type,
+                                   ObjPtr<mirror::MethodType> callsite_type)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
 }  // namespace art
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
index 7b2dd05..bda64eb 100644
--- a/runtime/compiler_filter.cc
+++ b/runtime/compiler_filter.cc
@@ -16,7 +16,7 @@
 
 #include "compiler_filter.h"
 
-#include "utils.h"
+#include "base/utils.h"
 
 namespace art {
 
diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc
index 25d4dd0..6e2cfec 100644
--- a/runtime/dex/art_dex_file_loader_test.cc
+++ b/runtime/dex/art_dex_file_loader_test.cc
@@ -19,6 +19,7 @@
 #include <memory>
 
 #include "art_dex_file_loader.h"
+#include "base/os.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
@@ -29,7 +30,6 @@
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_loader.h"
 #include "mem_map.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
 
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 75642fc..00a95cc 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -24,7 +24,9 @@
 #include <gtest/gtest.h>
 
 #include "base/file_utils.h"
+#include "base/os.h"
 #include "base/stl_util.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "compiler_callbacks.h"
 #include "dex/art_dex_file_loader.h"
@@ -33,9 +35,7 @@
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
 #include "oat_file_assistant.h"
-#include "os.h"
 #include "runtime.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index b466181..719b4af 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -28,9 +28,9 @@
 #include "base/leb128.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "elf_file_impl.h"
 #include "elf_utils.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index b1c9395..ab9e6fa 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -21,9 +21,9 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/os.h"
 // Explicitly include our own elf.h to avoid Linux and other dependencies.
 #include "./elf.h"
-#include "os.h"
 
 namespace art {
 template <typename ElfTypes>
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 1ab67ec..ed5885f 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -18,6 +18,7 @@
 
 #include "art_method-inl.h"
 #include "base/enums.h"
+#include "base/quasi_atomic.h"
 #include "callee_save_frame.h"
 #include "dex/dex_file_types.h"
 #include "entrypoints/entrypoint_utils-inl.h"
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index f8d8271..6b103bf 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -25,7 +25,7 @@
 
 #include <android-base/logging.h>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/macros.h"
 #include "mem_map.h"
 #include "stack_reference.h"
diff --git a/runtime/gc/accounting/bitmap-inl.h b/runtime/gc/accounting/bitmap-inl.h
index bf153f5..a71b212 100644
--- a/runtime/gc/accounting/bitmap-inl.h
+++ b/runtime/gc/accounting/bitmap-inl.h
@@ -23,7 +23,7 @@
 
 #include <android-base/logging.h>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/bit_utils.h"
 
 namespace art {
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index adca5c8..14f5d0e 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -21,7 +21,7 @@
 
 #include <android-base/logging.h>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/bit_utils.h"
 #include "mem_map.h"
 #include "space_bitmap.h"
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index 934e57a..4eea607 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -19,13 +19,13 @@
 #include <sys/mman.h>
 
 #include "base/systrace.h"
+#include "base/utils.h"
 #include "card_table-inl.h"
 #include "gc/heap.h"
 #include "gc/space/space.h"
 #include "heap_bitmap.h"
 #include "mem_map.h"
 #include "runtime.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index cb2479f..87965ed 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -18,14 +18,14 @@
 
 #include <string>
 
-#include "atomic.h"
+#include "base/atomic.h"
+#include "base/utils.h"
 #include "common_runtime_test.h"
 #include "handle_scope-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/string-inl.h"  // Strings are easiest to allocate
 #include "scoped_thread_state_change-inl.h"
 #include "thread_pool.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/gc/accounting/remembered_set.h b/runtime/gc/accounting/remembered_set.h
index e9376a9..b96f0d3 100644
--- a/runtime/gc/accounting/remembered_set.h
+++ b/runtime/gc/accounting/remembered_set.h
@@ -18,8 +18,9 @@
 #define ART_RUNTIME_GC_ACCOUNTING_REMEMBERED_SET_H_
 
 #include "base/allocator.h"
+#include "base/globals.h"
+#include "base/mutex.h"
 #include "base/safe_map.h"
-#include "globals.h"
 
 #include <set>
 #include <vector>
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index 354b9e1..384e3c2 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -23,7 +23,7 @@
 
 #include <android-base/logging.h>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/bit_utils.h"
 
 namespace art {
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
index 4570e9c..e508d5f 100644
--- a/runtime/gc/allocator/dlmalloc.cc
+++ b/runtime/gc/allocator/dlmalloc.cc
@@ -59,8 +59,8 @@
 
 #include <sys/mman.h>
 
-#include "globals.h"
-#include "utils.h"
+#include "base/globals.h"
+#include "base/utils.h"
 
 extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) {
   // Is this chunk in use?
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index a78813b..b10c504 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -20,6 +20,7 @@
 #include "base/enums.h"
 #include "base/file_utils.h"
 #include "base/histogram-inl.h"
+#include "base/quasi_atomic.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "debugger.h"
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index fa34270..5e3692e 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -26,6 +26,7 @@
 #include "base/mutex-inl.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "gc/gc_pause_listener.h"
 #include "gc/heap.h"
@@ -34,7 +35,6 @@
 #include "runtime.h"
 #include "thread-current-inl.h"
 #include "thread_list.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 4122809..e774959 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -20,7 +20,7 @@
 #include <deque>
 #include <memory>  // For unique_ptr.
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 53b899e..5e0fe06 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -19,7 +19,7 @@
 
 #include <memory>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "barrier.h"
 #include "base/macros.h"
 #include "base/mutex.h"
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index fc77c17..d1d45c8 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -19,7 +19,7 @@
 
 #include <memory>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 6735961..41ee183 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -20,7 +20,9 @@
 #include "heap.h"
 
 #include "allocation_listener.h"
+#include "base/quasi_atomic.h"
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "gc/accounting/atomic_stack.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/allocation_record.h"
@@ -34,7 +36,6 @@
 #include "obj_ptr-inl.h"
 #include "runtime.h"
 #include "thread-inl.h"
-#include "utils.h"
 #include "verify_object.h"
 
 namespace art {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 19b4acd..e8f720b 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -32,6 +32,7 @@
 #include "base/histogram-inl.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/memory_tool.h"
+#include "base/os.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
@@ -81,7 +82,6 @@
 #include "mirror/reference-inl.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "obj_ptr-inl.h"
-#include "os.h"
 #include "reflection.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
@@ -143,6 +143,10 @@
 static constexpr size_t kPartialTlabSize = 16 * KB;
 static constexpr bool kUsePartialTlabs = true;
 
+// Use Max heap for 2 seconds, this is smaller than the usual 5s window since we don't want to leave
+// allocate with relaxed ergonomics for that long.
+static constexpr size_t kPostForkMaxHeapDurationMS = 2000;
+
 #if defined(__LP64__) || !defined(ADDRESS_SANITIZER)
 // 300 MB (0x12c00000) - (default non-moving space capacity).
 static uint8_t* const kPreferredAllocSpaceBegin =
@@ -3539,6 +3543,12 @@
 }
 
 void Heap::ClearGrowthLimit() {
+  if (max_allowed_footprint_ == growth_limit_ && growth_limit_ < capacity_) {
+    max_allowed_footprint_ = capacity_;
+    concurrent_start_bytes_ =
+         std::max(max_allowed_footprint_, kMinConcurrentRemainingBytes) -
+         kMinConcurrentRemainingBytes;
+  }
   growth_limit_ = capacity_;
   ScopedObjectAccess soa(Thread::Current());
   for (const auto& space : continuous_spaces_) {
@@ -4110,5 +4120,32 @@
              << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation";
 }
 
+class Heap::TriggerPostForkCCGcTask : public HeapTask {
+ public:
+  explicit TriggerPostForkCCGcTask(uint64_t target_time) : HeapTask(target_time) {}
+  void Run(Thread* self) OVERRIDE {
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    // Trigger a GC, if not already done. The first GC after fork, whenever
+    // takes place, will adjust the thresholds to normal levels.
+    if (heap->max_allowed_footprint_ == heap->growth_limit_) {
+      heap->RequestConcurrentGC(self, kGcCauseBackground, false);
+    }
+  }
+};
+
+void Heap::PostForkChildAction(Thread* self) {
+  // Temporarily increase max_allowed_footprint_ and concurrent_start_bytes_ to
+  // max values to avoid GC during app launch.
+  if (collector_type_ == kCollectorTypeCC && !IsLowMemoryMode()) {
+    // Set max_allowed_footprint_ to the largest allowed value.
+    SetIdealFootprint(growth_limit_);
+    // Set concurrent_start_bytes_ to half of the heap size.
+    concurrent_start_bytes_ = std::max(max_allowed_footprint_ / 2, GetBytesAllocated());
+
+    GetTaskProcessor()->AddTask(
+        self, new TriggerPostForkCCGcTask(NanoTime() + MsToNs(kPostForkMaxHeapDurationMS)));
+  }
+}
+
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 592172f..d9eff7b 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -26,7 +26,7 @@
 
 #include "allocator_type.h"
 #include "arch/instruction_set.h"
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "base/runtime_debug.h"
@@ -832,10 +832,13 @@
 
   const Verification* GetVerification() const;
 
+  void PostForkChildAction(Thread* self);
+
  private:
   class ConcurrentGCTask;
   class CollectorTransitionTask;
   class HeapTrimTask;
+  class TriggerPostForkCCGcTask;
 
   // Compact source space to target space. Returns the collector used.
   collector::GarbageCollector* Compact(space::ContinuousMemMapAllocSpace* target_space,
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index c59642f..356f3ec 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -17,6 +17,7 @@
 #include "reference_processor.h"
 
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "collector/garbage_collector.h"
 #include "java_vm_ext.h"
 #include "mirror/class-inl.h"
@@ -28,7 +29,6 @@
 #include "reflection.h"
 #include "scoped_thread_state_change-inl.h"
 #include "task_processor.h"
-#include "utils.h"
 #include "well_known_classes.h"
 
 namespace art {
diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h
index c48d48c..af77881 100644
--- a/runtime/gc/reference_queue.h
+++ b/runtime/gc/reference_queue.h
@@ -21,7 +21,7 @@
 #include <string>
 #include <vector>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/mutex.h"
 #include "base/timing_logger.h"
 #include "globals.h"
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index a3eef90..025c3f0 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -18,6 +18,7 @@
 
 #include "base/logging.h"  // For VLOG.
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "gc/accounting/card_table.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
@@ -30,7 +31,6 @@
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "thread_list.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 366eb53..c100bc0 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -32,10 +32,12 @@
 #include "base/enums.h"
 #include "base/file_utils.h"
 #include "base/macros.h"
+#include "base/os.h"
 #include "base/scoped_flock.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file_loader.h"
 #include "exec_utils.h"
@@ -46,10 +48,8 @@
 #include "mirror/object-inl.h"
 #include "mirror/object-refvisitor-inl.h"
 #include "oat_file.h"
-#include "os.h"
 #include "runtime.h"
 #include "space-inl.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index 6ce81e9..14deb6f 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -23,13 +23,13 @@
 #include "android-base/stringprintf.h"
 
 #include "base/file_utils.h"
+#include "base/globals.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
-#include "globals.h"
-#include "os.h"
+#include "base/utils.h"
 #include "runtime.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index d2efb10..512cde4 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -25,12 +25,12 @@
 #include "base/macros.h"
 #include "base/memory_tool.h"
 #include "base/mutex-inl.h"
+#include "base/os.h"
 #include "base/stl_util.h"
 #include "gc/accounting/heap_bitmap-inl.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
 #include "image.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 #include "space-inl.h"
 #include "thread-current-inl.h"
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index 17274b5..0965560 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -19,6 +19,7 @@
 #include "android-base/stringprintf.h"
 
 #include "base/logging.h"  // For VLOG
+#include "base/utils.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
@@ -30,7 +31,6 @@
 #include "runtime.h"
 #include "thread.h"
 #include "thread_list.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 3a685cb..e786536 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -19,6 +19,7 @@
 
 #include "base/logging.h"  // For VLOG.
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "gc/accounting/card_table.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
@@ -29,7 +30,6 @@
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "thread_list.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 12bccb3..7af19fa 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -20,7 +20,7 @@
 #include <memory>
 #include <string>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "gc/accounting/space_bitmap.h"
diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc
index fddb3f2..cde155f 100644
--- a/runtime/gc/space/zygote_space.cc
+++ b/runtime/gc/space/zygote_space.cc
@@ -17,12 +17,12 @@
 #include "zygote_space.h"
 
 #include "base/mutex-inl.h"
+#include "base/utils.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
 #include "runtime.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 52ee516..aa716f1 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -44,6 +44,7 @@
 #include "base/array_ref.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
@@ -64,7 +65,6 @@
 #include "mirror/class-inl.h"
 #include "mirror/class.h"
 #include "mirror/object-refvisitor-inl.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
 
diff --git a/runtime/image.cc b/runtime/image.cc
index 9940622..56fee9d 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -18,15 +18,15 @@
 
 #include "base/bit_utils.h"
 #include "base/length_prefixed_array.h"
+#include "base/utils.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object_array.h"
-#include "utils.h"
 
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '5', '\0' };  // Bitstring type check off.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '6', '\0' };  // No image tables in .bss.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index 159a308..fe544cc 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -19,7 +19,6 @@
 
 #include <string.h>
 
-#include "base/bit_utils.h"
 #include "base/enums.h"
 #include "globals.h"
 #include "mirror/object.h"
@@ -327,22 +326,6 @@
     return boot_image_size_ != 0u;
   }
 
-  uint32_t GetBootImageConstantTablesOffset() const {
-    // Interned strings table and class table for boot image are mmapped read only.
-    DCHECK(!IsAppImage());
-    const ImageSection& interned_strings = GetInternedStringsSection();
-    DCHECK_ALIGNED(interned_strings.Offset(), kPageSize);
-    return interned_strings.Offset();
-  }
-
-  uint32_t GetBootImageConstantTablesSize() const {
-    uint32_t start_offset = GetBootImageConstantTablesOffset();
-    const ImageSection& class_table = GetClassTableSection();
-    DCHECK_LE(start_offset, class_table.Offset());
-    size_t tables_size = class_table.Offset() + class_table.Size() - start_offset;
-    return RoundUp(tables_size, kPageSize);
-  }
-
   // Visit mirror::Objects in the section starting at base.
   // TODO: Delete base parameter if it is always equal to GetImageBegin.
   void VisitObjects(ObjectVisitor* visitor,
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 5187831..3b9cc0f 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -18,6 +18,7 @@
 
 #include "base/dumpable-inl.h"
 #include "base/systrace.h"
+#include "base/utils.h"
 #include "java_vm_ext.h"
 #include "jni_internal.h"
 #include "nth_caller_visitor.h"
@@ -25,7 +26,6 @@
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
-#include "utils.h"
 
 #include <cstdlib>
 
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index b6055cb..84a148f 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -21,7 +21,7 @@
 #include "arch/context.h"
 #include "art_field-inl.h"
 #include "art_method-inl.h"
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/callee_save_type.h"
 #include "class_linker.h"
 #include "debugger.h"
@@ -209,9 +209,7 @@
         : StackVisitor(thread_in, context, kInstrumentationStackWalk),
           instrumentation_stack_(thread_in->GetInstrumentationStack()),
           instrumentation_exit_pc_(instrumentation_exit_pc),
-          reached_existing_instrumentation_frames_(false),
-          should_be_at_top_(false),
-          instrumentation_stack_depth_(0),
+          reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0),
           last_return_pc_(0) {
     }
 
@@ -235,20 +233,6 @@
         return true;  // Continue.
       }
       uintptr_t return_pc = GetReturnPc();
-      if (UNLIKELY(should_be_at_top_)) {
-        std::string thread_name;
-        GetThread()->GetThreadName(thread_name);
-        uint32_t dex_pc = dex::kDexNoIndex;
-        if (last_return_pc_ != 0 &&
-            GetCurrentOatQuickMethodHeader() != nullptr) {
-          dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_);
-        }
-        LOG(FATAL) << "While walking " << thread_name << " Reached unexpected frame above what "
-                   << "should have been top. Method is " << GetMethod()->PrettyMethod()
-                   << " return_pc: " << std::hex << return_pc
-                   << " dex pc: " << dex_pc;
-        UNREACHABLE();
-      }
       if (kVerboseInstrumentation) {
         LOG(INFO) << "  Installing exit stub in " << DescribeLocation();
       }
@@ -272,7 +256,7 @@
         }
 
         // We've reached a frame which has already been installed with instrumentation exit stub.
-        // We should have already installed instrumentation on previous frames.
+        // We should have already installed instrumentation or be interpreter on previous frames.
         reached_existing_instrumentation_frames_ = true;
 
         const InstrumentationStackFrame& frame =
@@ -283,21 +267,25 @@
         if (kVerboseInstrumentation) {
           LOG(INFO) << "Ignoring already instrumented " << frame.Dump();
         }
-      } else if (UNLIKELY(reached_existing_instrumentation_frames_)) {
-        // If tracing was enabled we might have had all methods have the instrumentation frame
-        // except the runtime transition method at the very top of the stack. This isn't really a
-        // problem since the transition method just goes back into the runtime and never leaves it
-        // so it can be ignored.
-        should_be_at_top_ = true;
-        DCHECK(m->IsRuntimeMethod()) << "Expected method to be runtime method at start of thread "
-                                     << "but was " << m->PrettyMethod();
-        if (kVerboseInstrumentation) {
-          LOG(INFO) << "reached expected top frame " << m->PrettyMethod();
-        }
-        // Don't bother continuing on the upcalls on non-debug builds.
-        return kIsDebugBuild ? true : false;
       } else {
         CHECK_NE(return_pc, 0U);
+        if (UNLIKELY(reached_existing_instrumentation_frames_ && !m->IsRuntimeMethod())) {
+          // We already saw an existing instrumentation frame so this should be a runtime-method
+          // inserted by the interpreter or runtime.
+          std::string thread_name;
+          GetThread()->GetThreadName(thread_name);
+          uint32_t dex_pc = dex::kDexNoIndex;
+          if (last_return_pc_ != 0 &&
+              GetCurrentOatQuickMethodHeader() != nullptr) {
+            dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_);
+          }
+          LOG(FATAL) << "While walking " << thread_name << " found unexpected non-runtime method"
+                     << " without instrumentation exit return or interpreter frame."
+                     << " method is " << GetMethod()->PrettyMethod()
+                     << " return_pc is " << std::hex << return_pc
+                     << " dex pc: " << dex_pc;
+          UNREACHABLE();
+        }
         InstrumentationStackFrame instrumentation_frame(
             m->IsRuntimeMethod() ? nullptr : GetThisObject(),
             m,
@@ -335,7 +323,6 @@
     std::vector<uint32_t> dex_pcs_;
     const uintptr_t instrumentation_exit_pc_;
     bool reached_existing_instrumentation_frames_;
-    bool should_be_at_top_;
     size_t instrumentation_stack_depth_;
     uintptr_t last_return_pc_;
   };
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 05f2794..c9127d6 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -19,7 +19,7 @@
 
 #include <unordered_set>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/allocator.h"
 #include "base/hash_set.h"
 #include "base/mutex.h"
@@ -227,7 +227,6 @@
     // modifying the zygote intern table. The back of table is modified when strings are interned.
     std::vector<UnorderedSet> tables_;
 
-    friend class linker::OatWriter;  // for boot image string table slot address lookup.
     ART_FRIEND_TEST(InternTableTest, CrossHash);
   };
 
@@ -287,7 +286,6 @@
   // Weak root state, used for concurrent system weak processing and more.
   gc::WeakRootState weak_root_state_ GUARDED_BY(Locks::intern_table_lock_);
 
-  friend class linker::OatWriter;  // for boot image string table slot address lookup.
   friend class Transaction;
   ART_FRIEND_TEST(InternTableTest, CrossHash);
   DISALLOW_COPY_AND_ASSIGN(InternTable);
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 380a981..8a85ee4 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -900,171 +900,489 @@
   }
 }
 
+static JValue ConvertScalarBootstrapArgument(jvalue value) {
+  // value either contains a primitive scalar value if it corresponds
+  // to a primitive type, or it contains an integer value if it
+  // corresponds to an object instance reference id (e.g. a string id).
+  return JValue::FromPrimitive(value.j);
+}
+
+static ObjPtr<mirror::Class> GetClassForBootstrapArgument(EncodedArrayValueIterator::ValueType type)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  switch (type) {
+    case EncodedArrayValueIterator::ValueType::kBoolean:
+    case EncodedArrayValueIterator::ValueType::kByte:
+    case EncodedArrayValueIterator::ValueType::kChar:
+    case EncodedArrayValueIterator::ValueType::kShort:
+      // These types are disallowed by JVMS. Treat as integers. This
+      // will result in CCE's being raised if the BSM has one of these
+      // types.
+    case EncodedArrayValueIterator::ValueType::kInt:
+      return class_linker->FindPrimitiveClass('I');
+    case EncodedArrayValueIterator::ValueType::kLong:
+      return class_linker->FindPrimitiveClass('J');
+    case EncodedArrayValueIterator::ValueType::kFloat:
+      return class_linker->FindPrimitiveClass('F');
+    case EncodedArrayValueIterator::ValueType::kDouble:
+      return class_linker->FindPrimitiveClass('D');
+    case EncodedArrayValueIterator::ValueType::kMethodType:
+      return mirror::MethodType::StaticClass();
+    case EncodedArrayValueIterator::ValueType::kMethodHandle:
+      return mirror::MethodHandle::StaticClass();
+    case EncodedArrayValueIterator::ValueType::kString:
+      return mirror::String::GetJavaLangString();
+    case EncodedArrayValueIterator::ValueType::kType:
+      return mirror::Class::GetJavaLangClass();
+    case EncodedArrayValueIterator::ValueType::kField:
+    case EncodedArrayValueIterator::ValueType::kMethod:
+    case EncodedArrayValueIterator::ValueType::kEnum:
+    case EncodedArrayValueIterator::ValueType::kArray:
+    case EncodedArrayValueIterator::ValueType::kAnnotation:
+    case EncodedArrayValueIterator::ValueType::kNull:
+      return nullptr;
+  }
+}
+
+static bool GetArgumentForBootstrapMethod(Thread* self,
+                                          ArtMethod* referrer,
+                                          EncodedArrayValueIterator::ValueType type,
+                                          const JValue* encoded_value,
+                                          JValue* decoded_value)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // The encoded_value contains either a scalar value (IJDF) or a
+  // scalar DEX file index to a reference type to be materialized.
+  switch (type) {
+    case EncodedArrayValueIterator::ValueType::kInt:
+    case EncodedArrayValueIterator::ValueType::kFloat:
+      decoded_value->SetI(encoded_value->GetI());
+      return true;
+    case EncodedArrayValueIterator::ValueType::kLong:
+    case EncodedArrayValueIterator::ValueType::kDouble:
+      decoded_value->SetJ(encoded_value->GetJ());
+      return true;
+    case EncodedArrayValueIterator::ValueType::kMethodType: {
+      StackHandleScope<2> hs(self);
+      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+      Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+      uint32_t index = static_cast<uint32_t>(encoded_value->GetI());
+      ClassLinker* cl = Runtime::Current()->GetClassLinker();
+      ObjPtr<mirror::MethodType> o = cl->ResolveMethodType(self, index, dex_cache, class_loader);
+      if (UNLIKELY(o.IsNull())) {
+        DCHECK(self->IsExceptionPending());
+        return false;
+      }
+      decoded_value->SetL(o);
+      return true;
+    }
+    case EncodedArrayValueIterator::ValueType::kMethodHandle: {
+      uint32_t index = static_cast<uint32_t>(encoded_value->GetI());
+      ClassLinker* cl = Runtime::Current()->GetClassLinker();
+      ObjPtr<mirror::MethodHandle> o = cl->ResolveMethodHandle(self, index, referrer);
+      if (UNLIKELY(o.IsNull())) {
+        DCHECK(self->IsExceptionPending());
+        return false;
+      }
+      decoded_value->SetL(o);
+      return true;
+    }
+    case EncodedArrayValueIterator::ValueType::kString: {
+      StackHandleScope<1> hs(self);
+      Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+      dex::StringIndex index(static_cast<uint32_t>(encoded_value->GetI()));
+      ClassLinker* cl = Runtime::Current()->GetClassLinker();
+      ObjPtr<mirror::String> o = cl->ResolveString(index, dex_cache);
+      if (UNLIKELY(o.IsNull())) {
+        DCHECK(self->IsExceptionPending());
+        return false;
+      }
+      decoded_value->SetL(o);
+      return true;
+    }
+    case EncodedArrayValueIterator::ValueType::kType: {
+      StackHandleScope<2> hs(self);
+      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+      Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+      dex::TypeIndex index(static_cast<uint32_t>(encoded_value->GetI()));
+      ClassLinker* cl = Runtime::Current()->GetClassLinker();
+      ObjPtr<mirror::Class> o = cl->ResolveType(index, dex_cache, class_loader);
+      if (UNLIKELY(o.IsNull())) {
+        DCHECK(self->IsExceptionPending());
+        return false;
+      }
+      decoded_value->SetL(o);
+      return true;
+    }
+    case EncodedArrayValueIterator::ValueType::kBoolean:
+    case EncodedArrayValueIterator::ValueType::kByte:
+    case EncodedArrayValueIterator::ValueType::kChar:
+    case EncodedArrayValueIterator::ValueType::kShort:
+    case EncodedArrayValueIterator::ValueType::kField:
+    case EncodedArrayValueIterator::ValueType::kMethod:
+    case EncodedArrayValueIterator::ValueType::kEnum:
+    case EncodedArrayValueIterator::ValueType::kArray:
+    case EncodedArrayValueIterator::ValueType::kAnnotation:
+    case EncodedArrayValueIterator::ValueType::kNull:
+      // Unreachable - unsupported types that have been checked when
+      // determining the effect call site type based on the bootstrap
+      // argument types.
+      UNREACHABLE();
+  }
+}
+
+static bool PackArgumentForBootstrapMethod(Thread* self,
+                                           ArtMethod* referrer,
+                                           CallSiteArrayValueIterator* it,
+                                           ShadowFrameSetter* setter)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  auto type = it->GetValueType();
+  const JValue encoded_value = ConvertScalarBootstrapArgument(it->GetJavaValue());
+  JValue decoded_value;
+  if (!GetArgumentForBootstrapMethod(self, referrer, type, &encoded_value, &decoded_value)) {
+    return false;
+  }
+  switch (it->GetValueType()) {
+    case EncodedArrayValueIterator::ValueType::kInt:
+    case EncodedArrayValueIterator::ValueType::kFloat:
+      setter->Set(static_cast<uint32_t>(decoded_value.GetI()));
+      return true;
+    case EncodedArrayValueIterator::ValueType::kLong:
+    case EncodedArrayValueIterator::ValueType::kDouble:
+      setter->SetLong(decoded_value.GetJ());
+      return true;
+    case EncodedArrayValueIterator::ValueType::kMethodType:
+    case EncodedArrayValueIterator::ValueType::kMethodHandle:
+    case EncodedArrayValueIterator::ValueType::kString:
+    case EncodedArrayValueIterator::ValueType::kType:
+      setter->SetReference(decoded_value.GetL());
+      return true;
+    case EncodedArrayValueIterator::ValueType::kBoolean:
+    case EncodedArrayValueIterator::ValueType::kByte:
+    case EncodedArrayValueIterator::ValueType::kChar:
+    case EncodedArrayValueIterator::ValueType::kShort:
+    case EncodedArrayValueIterator::ValueType::kField:
+    case EncodedArrayValueIterator::ValueType::kMethod:
+    case EncodedArrayValueIterator::ValueType::kEnum:
+    case EncodedArrayValueIterator::ValueType::kArray:
+    case EncodedArrayValueIterator::ValueType::kAnnotation:
+    case EncodedArrayValueIterator::ValueType::kNull:
+      // Unreachable - unsupported types that have been checked when
+      // determining the effect call site type based on the bootstrap
+      // argument types.
+      UNREACHABLE();
+  }
+}
+
+static bool PackCollectorArrayForBootstrapMethod(Thread* self,
+                                                 ArtMethod* referrer,
+                                                 ObjPtr<mirror::Class> array_type,
+                                                 int32_t array_length,
+                                                 CallSiteArrayValueIterator* it,
+                                                 ShadowFrameSetter* setter)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<1> hs(self);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  JValue decoded_value;
+
+#define COLLECT_PRIMITIVE_ARRAY(Descriptor, Type)                       \
+  Handle<mirror::Type ## Array> array =                                 \
+      hs.NewHandle(mirror::Type ## Array::Alloc(self, array_length));   \
+  if (array.IsNull()) {                                                 \
+    return false;                                                       \
+  }                                                                     \
+  for (int32_t i = 0; it->HasNext(); it->Next(), ++i) {                 \
+    auto type = it->GetValueType();                                     \
+    DCHECK_EQ(type, EncodedArrayValueIterator::ValueType::k ## Type);   \
+    const JValue encoded_value =                                        \
+        ConvertScalarBootstrapArgument(it->GetJavaValue());             \
+    GetArgumentForBootstrapMethod(self,                                 \
+                                  referrer,                             \
+                                  type,                                 \
+                                  &encoded_value,                       \
+                                  &decoded_value);                      \
+    array->Set(i, decoded_value.Get ## Descriptor());                   \
+  }                                                                     \
+  setter->SetReference(array.Get());                                    \
+  return true;
+
+#define COLLECT_REFERENCE_ARRAY(T, Type)                                \
+  Handle<mirror::ObjectArray<T>> array =                                \
+      hs.NewHandle(mirror::ObjectArray<T>::Alloc(self,                  \
+                                                 array_type,            \
+                                                 array_length));        \
+  if (array.IsNull()) {                                                 \
+    return false;                                                       \
+  }                                                                     \
+  for (int32_t i = 0; it->HasNext(); it->Next(), ++i) {                 \
+    auto type = it->GetValueType();                                     \
+    DCHECK_EQ(type, EncodedArrayValueIterator::ValueType::k ## Type);   \
+    const JValue encoded_value =                                        \
+        ConvertScalarBootstrapArgument(it->GetJavaValue());             \
+    if (!GetArgumentForBootstrapMethod(self,                            \
+                                       referrer,                        \
+                                       type,                            \
+                                       &encoded_value,                  \
+                                       &decoded_value)) {               \
+      return false;                                                     \
+    }                                                                   \
+    ObjPtr<mirror::Object> o = decoded_value.GetL();                    \
+    if (Runtime::Current()->IsActiveTransaction()) {                    \
+      array->Set<true>(i, ObjPtr<T>::DownCast(o));                      \
+    } else {                                                            \
+      array->Set<false>(i, ObjPtr<T>::DownCast(o));                     \
+    }                                                                   \
+  }                                                                     \
+  setter->SetReference(array.Get());                                    \
+  return true;
+
+  if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('I')) {
+    COLLECT_PRIMITIVE_ARRAY(I, Int);
+  } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('J')) {
+    COLLECT_PRIMITIVE_ARRAY(J, Long);
+  } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('F')) {
+    COLLECT_PRIMITIVE_ARRAY(F, Float);
+  } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('D')) {
+    COLLECT_PRIMITIVE_ARRAY(D, Double);
+  } else if (array_type->GetComponentType() == mirror::MethodType::StaticClass()) {
+    COLLECT_REFERENCE_ARRAY(mirror::MethodType, MethodType);
+  } else if (array_type->GetComponentType() == mirror::MethodHandle::StaticClass()) {
+    COLLECT_REFERENCE_ARRAY(mirror::MethodHandle, MethodHandle);
+  } else if (array_type->GetComponentType() == mirror::String::GetJavaLangString()) {
+    COLLECT_REFERENCE_ARRAY(mirror::String, String);
+  } else if (array_type->GetComponentType() == mirror::Class::GetJavaLangClass()) {
+    COLLECT_REFERENCE_ARRAY(mirror::Class, Type);
+  } else {
+    UNREACHABLE();
+  }
+  #undef COLLECT_PRIMITIVE_ARRAY
+  #undef COLLECT_REFERENCE_ARRAY
+}
+
+static ObjPtr<mirror::MethodType> BuildCallSiteForBootstrapMethod(Thread* self,
+                                                                  const DexFile* dex_file,
+                                                                  uint32_t call_site_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
+  CallSiteArrayValueIterator it(*dex_file, csi);
+  DCHECK_GE(it.Size(), 1u);
+
+  StackHandleScope<2> hs(self);
+  // Create array for parameter types.
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+  mirror::Class* class_array_type =
+      Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type);
+  Handle<mirror::ObjectArray<mirror::Class>> ptypes = hs.NewHandle(
+      mirror::ObjectArray<mirror::Class>::Alloc(self,
+                                                class_array_type,
+                                                static_cast<int>(it.Size())));
+  if (ptypes.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  // Populate the first argument with an instance of j.l.i.MethodHandles.Lookup
+  // that the runtime will construct.
+  ptypes->Set(0, mirror::MethodHandlesLookup::StaticClass());
+  it.Next();
+
+  // The remaining parameter types are derived from the types of
+  // arguments present in the DEX file.
+  int index = 1;
+  while (it.HasNext()) {
+    ObjPtr<mirror::Class> ptype = GetClassForBootstrapArgument(it.GetValueType());
+    if (ptype.IsNull()) {
+      ThrowClassCastException("Unsupported bootstrap argument type");
+      return nullptr;
+    }
+    ptypes->Set(index, ptype);
+    index++;
+    it.Next();
+  }
+  DCHECK_EQ(static_cast<size_t>(index), it.Size());
+
+  // By definition, the return type is always a j.l.i.CallSite.
+  Handle<mirror::Class> rtype = hs.NewHandle(mirror::CallSite::StaticClass());
+  return mirror::MethodType::Create(self, rtype, ptypes);
+}
+
 static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
                                                       ShadowFrame& shadow_frame,
                                                       uint32_t call_site_idx)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<7> hs(self);
+  // There are three mandatory arguments expected from the call site
+  // value array in the DEX file: the bootstrap method handle, the
+  // method name to pass to the bootstrap method, and the method type
+  // to pass to the bootstrap method.
+  static constexpr size_t kMandatoryArgumentsCount = 3;
   ArtMethod* referrer = shadow_frame.GetMethod();
   const DexFile* dex_file = referrer->GetDexFile();
   const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
-
-  StackHandleScope<10> hs(self);
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
-
   CallSiteArrayValueIterator it(*dex_file, csi);
-  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  if (it.Size() < kMandatoryArgumentsCount) {
+    ThrowBootstrapMethodError("Truncated bootstrap arguments (%zu < %zu)",
+                              it.Size(), kMandatoryArgumentsCount);
+    return nullptr;
+  }
+
+  if (it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) {
+    ThrowBootstrapMethodError("First bootstrap argument is not a method handle");
+    return nullptr;
+  }
+
+  uint32_t bsm_index = static_cast<uint32_t>(it.GetJavaValue().i);
+  it.Next();
+
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Handle<mirror::MethodHandle>
-      bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(self, method_handle_idx, referrer)));
-  if (bootstrap.IsNull()) {
+  Handle<mirror::MethodHandle> bsm =
+      hs.NewHandle(class_linker->ResolveMethodHandle(self, bsm_index, referrer));
+  if (bsm.IsNull()) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
-  Handle<mirror::MethodType> bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType());
-  it.Next();
 
-  DCHECK_EQ(static_cast<size_t>(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size());
-  const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs();
+  if (bsm->GetHandleKind() != mirror::MethodHandle::Kind::kInvokeStatic) {
+    // JLS suggests also accepting constructors. This is currently
+    // hard as constructor invocations happen via transformers in ART
+    // today. The constructor would need to be a class derived from java.lang.invoke.CallSite.
+    ThrowBootstrapMethodError("Unsupported bootstrap method invocation kind");
+    return nullptr;
+  }
+
+  // Construct the local call site type information based on the 3
+  // mandatory arguments provided by the runtime and the static arguments
+  // in the DEX file. We will use these arguments to build a shadow frame.
+  MutableHandle<mirror::MethodType> call_site_type =
+      hs.NewHandle(BuildCallSiteForBootstrapMethod(self, dex_file, call_site_idx));
+  if (call_site_type.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  // Check if this BSM is targeting a variable arity method. If so,
+  // we'll need to collect the trailing arguments into an array.
+  Handle<mirror::Array> collector_arguments;
+  int32_t collector_arguments_length;
+  if (bsm->GetTargetMethod()->IsVarargs()) {
+    int number_of_bsm_parameters = bsm->GetMethodType()->GetNumberOfPTypes();
+    if (number_of_bsm_parameters == 0) {
+      ThrowBootstrapMethodError("Variable arity BSM does not have any arguments");
+      return nullptr;
+    }
+    Handle<mirror::Class> collector_array_class =
+        hs.NewHandle(bsm->GetMethodType()->GetPTypes()->Get(number_of_bsm_parameters - 1));
+    if (!collector_array_class->IsArrayClass()) {
+      ThrowBootstrapMethodError("Variable arity BSM does not have array as final argument");
+      return nullptr;
+    }
+    // The call site may include no arguments to be collected. In this
+    // case the number of arguments must be at least the number of BSM
+    // parameters less the collector array.
+    if (call_site_type->GetNumberOfPTypes() < number_of_bsm_parameters - 1) {
+      ThrowWrongMethodTypeException(bsm->GetMethodType(), call_site_type.Get());
+      return nullptr;
+    }
+    // Check all the arguments to be collected match the collector array component type.
+    for (int i = number_of_bsm_parameters - 1; i < call_site_type->GetNumberOfPTypes(); ++i) {
+      if (call_site_type->GetPTypes()->Get(i) != collector_array_class->GetComponentType()) {
+        ThrowClassCastException(collector_array_class->GetComponentType(),
+                                call_site_type->GetPTypes()->Get(i));
+        return nullptr;
+      }
+    }
+    // Update the call site method type so it now includes the collector array.
+    int32_t collector_arguments_start = number_of_bsm_parameters - 1;
+    collector_arguments_length = call_site_type->GetNumberOfPTypes() - number_of_bsm_parameters + 1;
+    call_site_type.Assign(
+        mirror::MethodType::CollectTrailingArguments(self,
+                                                     call_site_type.Get(),
+                                                     collector_array_class.Get(),
+                                                     collector_arguments_start));
+    if (call_site_type.IsNull()) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+  } else {
+    collector_arguments_length = 0;
+  }
+
+  if (call_site_type->GetNumberOfPTypes() != bsm->GetMethodType()->GetNumberOfPTypes()) {
+    ThrowWrongMethodTypeException(bsm->GetMethodType(), call_site_type.Get());
+    return nullptr;
+  }
+
+  // BSM invocation has a different set of exceptions that
+  // j.l.i.MethodHandle.invoke(). Scan arguments looking for CCE
+  // "opportunities". Unfortunately we cannot just leave this to the
+  // method handle invocation as this might generate a WMTE.
+  for (int32_t i = 0; i < call_site_type->GetNumberOfPTypes(); ++i) {
+    ObjPtr<mirror::Class> from = call_site_type->GetPTypes()->Get(i);
+    ObjPtr<mirror::Class> to = bsm->GetMethodType()->GetPTypes()->Get(i);
+    if (!IsParameterTypeConvertible(from, to)) {
+      ThrowClassCastException(from, to);
+      return nullptr;
+    }
+  }
+  if (!IsReturnTypeConvertible(call_site_type->GetRType(), bsm->GetMethodType()->GetRType())) {
+    ThrowClassCastException(bsm->GetMethodType()->GetRType(), call_site_type->GetRType());
+    return nullptr;
+  }
 
   // Set-up a shadow frame for invoking the bootstrap method handle.
   ShadowFrameAllocaUniquePtr bootstrap_frame =
-      CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC());
+      CREATE_SHADOW_FRAME(call_site_type->NumberOfVRegs(),
+                          nullptr,
+                          referrer,
+                          shadow_frame.GetDexPC());
   ScopedStackedShadowFramePusher pusher(
       self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction);
-  size_t vreg = 0;
+  ShadowFrameSetter setter(bootstrap_frame.get(), 0u);
 
   // The first parameter is a MethodHandles lookup instance.
-  {
-    Handle<mirror::Class> lookup_class =
-        hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass());
-    ObjPtr<mirror::MethodHandlesLookup> lookup =
-        mirror::MethodHandlesLookup::Create(self, lookup_class);
-    if (lookup.IsNull()) {
-      DCHECK(self->IsExceptionPending());
-      return nullptr;
-    }
-    bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr());
-  }
-
-  // The second parameter is the name to lookup.
-  {
-    dex::StringIndex name_idx(static_cast<uint32_t>(it.GetJavaValue().i));
-    ObjPtr<mirror::String> name = class_linker->ResolveString(name_idx, dex_cache);
-    if (name.IsNull()) {
-      DCHECK(self->IsExceptionPending());
-      return nullptr;
-    }
-    bootstrap_frame->SetVRegReference(vreg++, name.Ptr());
-  }
-  it.Next();
-
-  // The third parameter is the method type associated with the name.
-  uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
-  Handle<mirror::MethodType> method_type(hs.NewHandle(
-      class_linker->ResolveMethodType(self, method_type_idx, dex_cache, class_loader)));
-  if (method_type.IsNull()) {
+  Handle<mirror::Class> lookup_class =
+      hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass());
+  ObjPtr<mirror::MethodHandlesLookup> lookup =
+      mirror::MethodHandlesLookup::Create(self, lookup_class);
+  if (lookup.IsNull()) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
-  bootstrap_frame->SetVRegReference(vreg++, method_type.Get());
-  it.Next();
+  setter.SetReference(lookup);
 
-  // Append remaining arguments (if any).
-  while (it.HasNext()) {
-    const jvalue& jvalue = it.GetJavaValue();
-    switch (it.GetValueType()) {
-      case EncodedArrayValueIterator::ValueType::kBoolean:
-      case EncodedArrayValueIterator::ValueType::kByte:
-      case EncodedArrayValueIterator::ValueType::kChar:
-      case EncodedArrayValueIterator::ValueType::kShort:
-      case EncodedArrayValueIterator::ValueType::kInt:
-        bootstrap_frame->SetVReg(vreg, jvalue.i);
-        vreg += 1;
-        break;
-      case EncodedArrayValueIterator::ValueType::kLong:
-        bootstrap_frame->SetVRegLong(vreg, jvalue.j);
-        vreg += 2;
-        break;
-      case EncodedArrayValueIterator::ValueType::kFloat:
-        bootstrap_frame->SetVRegFloat(vreg, jvalue.f);
-        vreg += 1;
-        break;
-      case EncodedArrayValueIterator::ValueType::kDouble:
-        bootstrap_frame->SetVRegDouble(vreg, jvalue.d);
-        vreg += 2;
-        break;
-      case EncodedArrayValueIterator::ValueType::kMethodType: {
-        uint32_t idx = static_cast<uint32_t>(jvalue.i);
-        ObjPtr<mirror::MethodType> ref =
-            class_linker->ResolveMethodType(self, idx, dex_cache, class_loader);
-        if (ref.IsNull()) {
-          DCHECK(self->IsExceptionPending());
-          return nullptr;
-        }
-        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
-        vreg += 1;
-        break;
+  // Pack the remaining arguments into the frame.
+  int number_of_arguments = call_site_type->GetNumberOfPTypes();
+  int argument_index;
+  for (argument_index = 1; argument_index < number_of_arguments; ++argument_index) {
+    if (argument_index == number_of_arguments - 1 &&
+        call_site_type->GetPTypes()->Get(argument_index)->IsArrayClass()) {
+      ObjPtr<mirror::Class> array_type = call_site_type->GetPTypes()->Get(argument_index);
+      if (!PackCollectorArrayForBootstrapMethod(self,
+                                                referrer,
+                                                array_type,
+                                                collector_arguments_length,
+                                                &it,
+                                                &setter)) {
+        DCHECK(self->IsExceptionPending());
+        return nullptr;
       }
-      case EncodedArrayValueIterator::ValueType::kMethodHandle: {
-        uint32_t idx = static_cast<uint32_t>(jvalue.i);
-        ObjPtr<mirror::MethodHandle> ref =
-            class_linker->ResolveMethodHandle(self, idx, referrer);
-        if (ref.IsNull()) {
-          DCHECK(self->IsExceptionPending());
-          return nullptr;
-        }
-        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
-        vreg += 1;
-        break;
-      }
-      case EncodedArrayValueIterator::ValueType::kString: {
-        dex::StringIndex idx(static_cast<uint32_t>(jvalue.i));
-        ObjPtr<mirror::String> ref = class_linker->ResolveString(idx, dex_cache);
-        if (ref.IsNull()) {
-          DCHECK(self->IsExceptionPending());
-          return nullptr;
-        }
-        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
-        vreg += 1;
-        break;
-      }
-      case EncodedArrayValueIterator::ValueType::kType: {
-        dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i));
-        ObjPtr<mirror::Class> ref = class_linker->ResolveType(idx, dex_cache, class_loader);
-        if (ref.IsNull()) {
-          DCHECK(self->IsExceptionPending());
-          return nullptr;
-        }
-        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
-        vreg += 1;
-        break;
-      }
-      case EncodedArrayValueIterator::ValueType::kNull:
-        bootstrap_frame->SetVRegReference(vreg, nullptr);
-        vreg += 1;
-        break;
-      case EncodedArrayValueIterator::ValueType::kField:
-      case EncodedArrayValueIterator::ValueType::kMethod:
-      case EncodedArrayValueIterator::ValueType::kEnum:
-      case EncodedArrayValueIterator::ValueType::kArray:
-      case EncodedArrayValueIterator::ValueType::kAnnotation:
-        // Unreachable based on current EncodedArrayValueIterator::Next().
-        UNREACHABLE();
+    } else if (!PackArgumentForBootstrapMethod(self, referrer, &it, &setter)) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
     }
-
     it.Next();
   }
+  DCHECK(!it.HasNext());
+  DCHECK(setter.Done());
 
   // Invoke the bootstrap method handle.
   JValue result;
-  RangeInstructionOperands operands(0, vreg);
-  bool invoke_success = MethodHandleInvokeExact(self,
-                                                *bootstrap_frame,
-                                                bootstrap,
-                                                bootstrap_method_type,
-                                                &operands,
-                                                &result);
+  RangeInstructionOperands operands(0, bootstrap_frame->NumberOfVRegs());
+  bool invoke_success = MethodHandleInvoke(self,
+                                           *bootstrap_frame,
+                                           bsm,
+                                           call_site_type,
+                                           &operands,
+                                           &result);
   if (!invoke_success) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
@@ -1077,31 +1395,20 @@
     return nullptr;
   }
 
-  // Check the result type is a subclass of CallSite.
+  // Check the result type is a subclass of j.l.i.CallSite.
   if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) {
     ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass());
     return nullptr;
   }
 
+  // Check the call site target is not null as we're going to invoke it.
   Handle<mirror::CallSite> call_site =
       hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
-  // Check the call site target is not null as we're going to invoke it.
   Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
   if (UNLIKELY(target.IsNull())) {
-    ThrowClassCastException("Bootstrap method did not return a callsite");
+    ThrowClassCastException("Bootstrap method returned a CallSite with a null target");
     return nullptr;
   }
-
-  // Check the target method type matches the method type requested modulo the receiver
-  // needs to be compatible rather than exact.
-  Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
-  if (UNLIKELY(!target_method_type->IsExactMatch(method_type.Get()) &&
-               !IsParameterTypeConvertible(target_method_type->GetPTypes()->GetWithoutChecks(0),
-                                           method_type->GetPTypes()->GetWithoutChecks(0)))) {
-    ThrowWrongMethodTypeException(target_method_type.Get(), method_type.Get());
-    return nullptr;
-  }
-
   return call_site.Get();
 }
 
@@ -1129,8 +1436,11 @@
     call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx));
     if (UNLIKELY(call_site.IsNull())) {
       CHECK(self->IsExceptionPending());
-      ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
-                                       call_site_idx);
+      if (!self->GetException()->IsError()) {
+        // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error.
+        ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
+                                         call_site_idx);
+      }
       result->SetJ(0);
       return false;
     }
@@ -1139,9 +1449,6 @@
     call_site.Assign(winning_call_site);
   }
 
-  // CallSite.java checks the re-assignment of the call site target
-  // when mutating call site targets. We only check the target is
-  // non-null and has the right type during bootstrap method execution.
   Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
   Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
   DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs());
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index d8f858e..e35d80f 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -17,6 +17,7 @@
 #include "interpreter_switch_impl.h"
 
 #include "base/enums.h"
+#include "base/quasi_atomic.h"
 #include "dex/dex_file_types.h"
 #include "experimental_flags.h"
 #include "interpreter_common.h"
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 9c7645a..2a9ef2c 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -18,6 +18,8 @@
  * Mterp entry point and support functions.
  */
 #include "mterp.h"
+
+#include "base/quasi_atomic.h"
 #include "debugger.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "interpreter/interpreter_common.h"
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index a0b58ef..600561b 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -33,6 +33,7 @@
 #include "base/casts.h"
 #include "base/enums.h"
 #include "base/macros.h"
+#include "base/quasi_atomic.h"
 #include "class_linker.h"
 #include "common_throws.h"
 #include "dex/descriptors_names.h"
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index b491c3e..bf1d665 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -17,7 +17,7 @@
 #ifndef ART_RUNTIME_JDWP_JDWP_H_
 #define ART_RUNTIME_JDWP_JDWP_H_
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/mutex.h"
 #include "jdwp/jdwp_bits.h"
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 90cac85..291a983 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -22,7 +22,7 @@
 
 #include "android-base/stringprintf.h"
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/hex_dump.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index 63f5dc8..557b032 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -22,7 +22,7 @@
 
 #include "android-base/stringprintf.h"
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/time_utils.h"
 #include "debugger.h"
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 6d99ad0..a757960 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -23,6 +23,7 @@
 #include "base/logging.h"  // For VLOG.
 #include "base/memory_tool.h"
 #include "base/runtime_debug.h"
+#include "base/utils.h"
 #include "debugger.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "interpreter/interpreter.h"
@@ -38,7 +39,6 @@
 #include "stack_map.h"
 #include "thread-inl.h"
 #include "thread_list.h"
-#include "utils.h"
 
 namespace art {
 namespace jit {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 68a3647..b2d58da 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -22,6 +22,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "base/logging.h"  // For VLOG.
+#include "base/quasi_atomic.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
@@ -327,33 +328,20 @@
 
 class ScopedCodeCacheWrite : ScopedTrace {
  public:
-  explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false)
+  explicit ScopedCodeCacheWrite(MemMap* code_map)
       : ScopedTrace("ScopedCodeCacheWrite"),
-        code_map_(code_map),
-        only_for_tlb_shootdown_(only_for_tlb_shootdown) {
+        code_map_(code_map) {
     ScopedTrace trace("mprotect all");
-    CheckedCall(mprotect,
-                "make code writable",
-                code_map_->Begin(),
-                only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(),
-                kProtAll);
+    CheckedCall(mprotect, "make code writable", code_map_->Begin(), code_map_->Size(), kProtAll);
   }
   ~ScopedCodeCacheWrite() {
     ScopedTrace trace("mprotect code");
-    CheckedCall(mprotect,
-                "make code protected",
-                code_map_->Begin(),
-                only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(),
-                kProtCode);
+    CheckedCall(mprotect, "make code protected", code_map_->Begin(), code_map_->Size(), kProtCode);
   }
 
  private:
   MemMap* const code_map_;
 
-  // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to
-  // one page.
-  const bool only_for_tlb_shootdown_;
-
   DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite);
 };
 
@@ -683,7 +671,11 @@
 
 static void ClearMethodCounter(ArtMethod* method, bool was_warm) {
   if (was_warm) {
-    method->SetPreviouslyWarm();
+    // Don't do any read barrier, as the declaring class of `method` may
+    // be in the process of being GC'ed (reading the declaring class is done
+    // when DCHECKing the declaring class is resolved, which we know it is
+    // at this point).
+    method->SetPreviouslyWarm<kWithoutReadBarrier>();
   }
   // We reset the counter to 1 so that the profile knows that the method was executed at least once.
   // This is required for layout purposes.
@@ -811,8 +803,6 @@
       FillRootTable(roots_data, roots);
       {
         // Flush data cache, as compiled code references literals in it.
-        // We also need a TLB shootdown to act as memory barrier across cores.
-        ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true);
         FlushDataCache(reinterpret_cast<char*>(roots_data),
                        reinterpret_cast<char*>(roots_data + data_size));
       }
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 0d1311f..16335c6 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -19,8 +19,8 @@
 
 #include "instrumentation.h"
 
-#include "atomic.h"
 #include "base/arena_containers.h"
+#include "base/atomic.h"
 #include "base/histogram-inl.h"
 #include "base/macros.h"
 #include "base/mutex.h"
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 7be29c9..1bbce4f 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -37,16 +37,16 @@
 #include "base/file_utils.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/mutex.h"
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "base/scoped_flock.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "dex/dex_file_loader.h"
 #include "jit/profiling_info.h"
-#include "os.h"
-#include "utils.h"
 #include "zip_archive.h"
 
 namespace art {
@@ -278,7 +278,7 @@
   // access and fail immediately if we can't.
   bool result = Save(fd);
   if (result) {
-    int64_t size = GetFileSizeBytes(filename);
+    int64_t size = OS::GetFileSizeBytes(filename.c_str());
     if (size != -1) {
       VLOG(profiler)
         << "Successfully saved profile info to " << filename << " Size: "
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 7e09b6b..a0f6bf8 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -20,9 +20,9 @@
 #include <set>
 #include <vector>
 
-#include "atomic.h"
 #include "base/arena_containers.h"
 #include "base/arena_object.h"
+#include "base/atomic.h"
 #include "base/safe_map.h"
 #include "bit_memory_region.h"
 #include "dex/dex_cache_resolved_classes.h"
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 4c73d87..b78fcac 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -25,8 +25,8 @@
 
 #include "art_field-inl.h"
 #include "art_method-inl.h"
-#include "atomic.h"
 #include "base/allocator.h"
+#include "base/atomic.h"
 #include "base/enums.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/mutex.h"
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 26acef0..b9d51c1 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -35,10 +35,10 @@
 #include "base/allocator.h"
 #include "base/bit_utils.h"
 #include "base/file_utils.h"
+#include "base/globals.h"
 #include "base/logging.h"  // For VLOG_IS_ON.
 #include "base/memory_tool.h"
-#include "globals.h"
-#include "utils.h"
+#include "base/utils.h"
 
 #ifndef MAP_ANONYMOUS
 #define MAP_ANONYMOUS MAP_ANON
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 7e60a5c..fce3d06 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -174,19 +174,26 @@
       : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {}
 
   ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs());
     shadow_frame_->SetVReg(arg_index_++, value);
   }
 
   ALWAYS_INLINE void SetReference(ObjPtr<mirror::Object> value)
       REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs());
     shadow_frame_->SetVRegReference(arg_index_++, value.Ptr());
   }
 
   ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs());
     shadow_frame_->SetVRegLong(arg_index_, value);
     arg_index_ += 2;
   }
 
+  ALWAYS_INLINE bool Done() const {
+    return arg_index_ == shadow_frame_->NumberOfVRegs();
+  }
+
  private:
   ShadowFrame* shadow_frame_;
   size_t arg_index_;
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 25283bc..ea202e7 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -16,6 +16,7 @@
 
 #include "array-inl.h"
 
+#include "base/utils.h"
 #include "class-inl.h"
 #include "class.h"
 #include "class_linker-inl.h"
@@ -26,7 +27,6 @@
 #include "object-inl.h"
 #include "object_array-inl.h"
 #include "thread.h"
-#include "utils.h"
 
 namespace art {
 namespace mirror {
diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h
index db244a5..93f2748 100644
--- a/runtime/mirror/call_site.h
+++ b/runtime/mirror/call_site.h
@@ -17,8 +17,8 @@
 #ifndef ART_RUNTIME_MIRROR_CALL_SITE_H_
 #define ART_RUNTIME_MIRROR_CALL_SITE_H_
 
+#include "base/utils.h"
 #include "mirror/method_handle_impl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 86d538e..ee7d217 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -23,6 +23,7 @@
 #include "art_method.h"
 #include "base/array_slice.h"
 #include "base/length_prefixed_array.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "class_loader.h"
 #include "common_throws.h"
@@ -39,7 +40,6 @@
 #include "reference-inl.h"
 #include "runtime.h"
 #include "string.h"
-#include "utils.h"
 
 namespace art {
 namespace mirror {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 7a09391..3f4e841 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -21,6 +21,7 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/logging.h"  // For VLOG.
+#include "base/utils.h"
 #include "class-inl.h"
 #include "class_ext.h"
 #include "class_linker-inl.h"
@@ -40,7 +41,6 @@
 #include "runtime.h"
 #include "thread.h"
 #include "throwable.h"
-#include "utils.h"
 #include "well_known_classes.h"
 
 namespace art {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 6000317..ea06567 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -21,6 +21,8 @@
 #include "base/casts.h"
 #include "base/enums.h"
 #include "base/iteration_range.h"
+#include "base/stride_iterator.h"
+#include "base/utils.h"
 #include "class_flags.h"
 #include "class_status.h"
 #include "dex/dex_file.h"
@@ -33,9 +35,7 @@
 #include "object.h"
 #include "object_array.h"
 #include "read_barrier_option.h"
-#include "stride_iterator.h"
 #include "thread.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index c18b219..0819579 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -19,13 +19,13 @@
 #include "art_method-inl.h"
 #include "base/casts.h"
 #include "base/enums.h"
+#include "base/utils.h"
 #include "class-inl.h"
 #include "dex/dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "object-inl.h"
 #include "object_array.h"
 #include "stack_trace_element.h"
-#include "utils.h"
 #include "well_known_classes.h"
 
 namespace art {
diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h
index 9bfa4d6..23626f4 100644
--- a/runtime/mirror/emulated_stack_frame.h
+++ b/runtime/mirror/emulated_stack_frame.h
@@ -17,12 +17,12 @@
 #ifndef ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_
 #define ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_
 
+#include "base/utils.h"
 #include "dex/dex_instruction.h"
 #include "method_type.h"
 #include "object.h"
 #include "stack.h"
 #include "string.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h
index dd8d45e..fefcb2e 100644
--- a/runtime/mirror/method_handles_lookup.h
+++ b/runtime/mirror/method_handles_lookup.h
@@ -17,11 +17,11 @@
 #ifndef ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
 #define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
 
+#include "base/utils.h"
 #include "gc_root.h"
 #include "handle.h"
 #include "obj_ptr.h"
 #include "object.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 6ac5012..45f7a87 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -23,6 +23,18 @@
 namespace art {
 namespace mirror {
 
+namespace {
+
+ObjPtr<ObjectArray<Class>> AllocatePTypesArray(Thread* self, int count)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<Class> class_type = Class::GetJavaLangClass();
+  ObjPtr<Class> class_array_type =
+      Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type);
+  return ObjectArray<Class>::Alloc(self, class_array_type, count);
+}
+
+}  // namespace
+
 GcRoot<Class> MethodType::static_class_;
 
 MethodType* MethodType::Create(Thread* const self,
@@ -47,18 +59,41 @@
 MethodType* MethodType::CloneWithoutLeadingParameter(Thread* const self,
                                                      ObjPtr<MethodType> method_type) {
   StackHandleScope<3> hs(self);
-  Handle<Class> rtype = hs.NewHandle(method_type->GetRType());
   Handle<ObjectArray<Class>> src_ptypes = hs.NewHandle(method_type->GetPTypes());
-  ObjPtr<Class> class_type = Class::GetJavaLangClass();
-  ObjPtr<Class> class_array_type =
-      Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type);
-  const int32_t dst_ptypes_count = src_ptypes->GetLength() - 1;
-  Handle<ObjectArray<Class>> dst_ptypes = hs.NewHandle(
-      ObjectArray<Class>::Alloc(self, class_array_type, dst_ptypes_count));
+  Handle<Class> dst_rtype = hs.NewHandle(method_type->GetRType());
+  const int32_t dst_ptypes_count = method_type->GetNumberOfPTypes() - 1;
+  Handle<ObjectArray<Class>> dst_ptypes = hs.NewHandle(AllocatePTypesArray(self, dst_ptypes_count));
+  if (dst_ptypes.IsNull()) {
+    return nullptr;
+  }
   for (int32_t i = 0; i < dst_ptypes_count; ++i) {
     dst_ptypes->Set(i, src_ptypes->Get(i + 1));
   }
-  return Create(self, rtype, dst_ptypes);
+  return Create(self, dst_rtype, dst_ptypes);
+}
+
+MethodType* MethodType::CollectTrailingArguments(Thread* self,
+                                                 ObjPtr<MethodType> method_type,
+                                                 ObjPtr<Class> collector_array_class,
+                                                 int32_t start_index) {
+  int32_t ptypes_length = method_type->GetNumberOfPTypes();
+  if (start_index > ptypes_length) {
+    return method_type.Ptr();
+  }
+
+  StackHandleScope<4> hs(self);
+  Handle<Class> collector_class = hs.NewHandle(collector_array_class);
+  Handle<Class> dst_rtype = hs.NewHandle(method_type->GetRType());
+  Handle<ObjectArray<Class>> src_ptypes = hs.NewHandle(method_type->GetPTypes());
+  Handle<ObjectArray<Class>> dst_ptypes = hs.NewHandle(AllocatePTypesArray(self, start_index + 1));
+  if (dst_ptypes.IsNull()) {
+    return nullptr;
+  }
+  for (int32_t i = 0; i < start_index; ++i) {
+    dst_ptypes->Set(i, src_ptypes->Get(i));
+  }
+  dst_ptypes->Set(start_index, collector_class.Get());
+  return Create(self, dst_rtype, dst_ptypes);
 }
 
 size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index edd9910..771162a 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -17,10 +17,10 @@
 #ifndef ART_RUNTIME_MIRROR_METHOD_TYPE_H_
 #define ART_RUNTIME_MIRROR_METHOD_TYPE_H_
 
+#include "base/utils.h"
 #include "object_array.h"
 #include "object.h"
 #include "string.h"
-#include "utils.h"
 
 namespace art {
 
@@ -40,6 +40,14 @@
                                                   ObjPtr<MethodType> method_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Collects trailing parameter types into an array. Assumes caller
+  // has checked trailing arguments are all of the same type.
+  static MethodType* CollectTrailingArguments(Thread* const self,
+                                              ObjPtr<MethodType> method_type,
+                                              ObjPtr<Class> collector_array_class,
+                                              int32_t start_index)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   static Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
     return static_class_.Read();
   }
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 7fdaa32..55dd514 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -22,7 +22,7 @@
 #include "array-inl.h"
 #include "art_field.h"
 #include "art_method.h"
-#include "atomic.h"
+#include "base/atomic.h"
 #include "class-inl.h"
 #include "class_flags.h"
 #include "class_linker.h"
diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h
index 126cb04..aeaa850 100644
--- a/runtime/mirror/object-readbarrier-inl.h
+++ b/runtime/mirror/object-readbarrier-inl.h
@@ -19,7 +19,7 @@
 
 #include "object.h"
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "heap_poisoning.h"
 #include "lock_word-inl.h"
 #include "object_reference-inl.h"
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 816ac69..95f82cb 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -17,7 +17,7 @@
 #ifndef ART_RUNTIME_MIRROR_OBJECT_H_
 #define ART_RUNTIME_MIRROR_OBJECT_H_
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/casts.h"
 #include "base/enums.h"
 #include "globals.h"
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 5cfc987..086d2f4 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -24,6 +24,7 @@
 #include "android-base/stringprintf.h"
 
 #include "array-inl.h"
+#include "base/utils.h"
 #include "class.h"
 #include "gc/heap.h"
 #include "handle_scope-inl.h"
@@ -31,7 +32,6 @@
 #include "object-inl.h"
 #include "runtime.h"
 #include "thread.h"
-#include "utils.h"
 
 namespace art {
 namespace mirror {
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index 7fd9c71..cf1f85d 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -17,7 +17,7 @@
 #ifndef ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_
 #define ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/mutex.h"  // For Locks::mutator_lock_.
 #include "globals.h"
 #include "heap_poisoning.h"
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 32a99eb..5306eac 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -787,5 +787,23 @@
   }
 }
 
+TEST_F(ObjectTest, PrettyTypeOf) {
+  ScopedObjectAccess soa(Thread::Current());
+  EXPECT_EQ("null", mirror::Object::PrettyTypeOf(nullptr));
+
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::String> s(hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "")));
+  EXPECT_EQ("java.lang.String", mirror::Object::PrettyTypeOf(s.Get()));
+
+  Handle<mirror::ShortArray> a(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 2)));
+  EXPECT_EQ("short[]", mirror::Object::PrettyTypeOf(a.Get()));
+
+  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
+  ASSERT_TRUE(c != nullptr);
+  mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
+  EXPECT_EQ("java.lang.String[]", mirror::Object::PrettyTypeOf(o));
+  EXPECT_EQ("java.lang.Class<java.lang.String[]>", mirror::Object::PrettyTypeOf(o->GetClass()));
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 8c2a49c..a60861c 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -22,14 +22,14 @@
 
 #include "array.h"
 #include "base/bit_utils.h"
+#include "base/globals.h"
+#include "base/utils.h"
 #include "class.h"
 #include "common_throws.h"
 #include "dex/utf.h"
 #include "gc/heap-inl.h"
-#include "globals.h"
 #include "runtime.h"
 #include "thread.h"
-#include "utils.h"
 
 namespace art {
 namespace mirror {
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index a7a6d08..7006973 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -20,6 +20,7 @@
 
 #include "art_method-inl.h"
 #include "base/enums.h"
+#include "base/utils.h"
 #include "class-inl.h"
 #include "dex/dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
@@ -28,7 +29,6 @@
 #include "object_array.h"
 #include "stack_trace_element.h"
 #include "string.h"
-#include "utils.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -75,6 +75,10 @@
   return !InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_RuntimeException));
 }
 
+bool Throwable::IsError() {
+  return InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error));
+}
+
 int32_t Throwable::GetStackDepth() {
   ObjPtr<Object> stack_state = GetStackState();
   if (stack_state == nullptr || !stack_state->IsObjectArray()) {
diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h
index fb45228..b901ca2 100644
--- a/runtime/mirror/throwable.h
+++ b/runtime/mirror/throwable.h
@@ -44,6 +44,7 @@
   void SetCause(ObjPtr<Throwable> cause) REQUIRES_SHARED(Locks::mutator_lock_);
   void SetStackState(ObjPtr<Object> state) REQUIRES_SHARED(Locks::mutator_lock_);
   bool IsCheckedException() REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsError() REQUIRES_SHARED(Locks::mutator_lock_);
 
   static Class* GetJavaLangThrowable() REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK(!java_lang_Throwable_.IsNull());
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 0c9c65a..2a938da 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -23,6 +23,7 @@
 #include "art_method-inl.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/mutex.h"
+#include "base/quasi_atomic.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
diff --git a/runtime/monitor.h b/runtime/monitor.h
index f150a8c..384ebbe 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -25,8 +25,8 @@
 #include <list>
 #include <vector>
 
-#include "atomic.h"
 #include "base/allocator.h"
+#include "base/atomic.h"
 #include "base/mutex.h"
 #include "gc_root.h"
 #include "lock_word.h"
diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h
index 80bae7f..c6b0b0b 100644
--- a/runtime/monitor_pool.h
+++ b/runtime/monitor_pool.h
@@ -22,7 +22,7 @@
 #include "base/allocator.h"
 #ifdef __LP64__
 #include <stdint.h>
-#include "atomic.h"
+#include "base/atomic.h"
 #include "runtime.h"
 #else
 #include "base/stl_util.h"     // STLDeleteElements
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index 7d89652..bff8d76 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -18,7 +18,7 @@
 
 #include <string>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "barrier.h"
 #include "base/time_utils.h"
 #include "class_linker-inl.h"
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 5d18b6e..13319c4 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -22,7 +22,9 @@
 
 #include "base/file_utils.h"
 #include "base/logging.h"
+#include "base/os.h"
 #include "base/stl_util.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include <class_loader_context.h>
 #include "common_throws.h"
@@ -43,10 +45,8 @@
 #include "oat_file.h"
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
-#include "os.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
-#include "utils.h"
 #include "well_known_classes.h"
 #include "zip_archive.h"
 
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 08e471b..8913569 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -306,6 +306,8 @@
     LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
   }
 
+  Runtime::Current()->GetHeap()->PostForkChildAction(thread);
+
   // Update tracing.
   if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) {
     Trace::TraceOutputMode output_mode = Trace::GetOutputMode();
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 688ae19..13275d9 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -20,6 +20,7 @@
 #include "nativehelper/jni_macros.h"
 
 #include "art_field-inl.h"
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
 #include "common_throws.h"
@@ -31,7 +32,6 @@
 #include "native_util.h"
 #include "reflection-inl.h"
 #include "scoped_fast_native_object_access-inl.h"
-#include "utils.h"
 #include "well_known_classes.h"
 
 namespace art {
diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc
index 1ab9109..b80b20c 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -20,13 +20,13 @@
 #include "nativehelper/jni_macros.h"
 
 #include "art_method-inl.h"
+#include "base/utils.h"
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "jni_internal.h"
 #include "native_util.h"
 #include "scoped_fast_native_object_access-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
index bd4b0fe..c003297 100644
--- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
+++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc
@@ -19,7 +19,8 @@
 #include "nativehelper/jni_macros.h"
 
 #include "arch/instruction_set.h"
-#include "atomic.h"
+#include "base/atomic.h"
+#include "base/quasi_atomic.h"
 #include "jni_internal.h"
 #include "native_util.h"
 
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 1af65a3..25f984f 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -24,6 +24,7 @@
 
 #include "nativehelper/jni_macros.h"
 
+#include "base/quasi_atomic.h"
 #include "common_throws.h"
 #include "gc/accounting/card_table-inl.h"
 #include "jni_internal.h"
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 4d4bab7..c26c26e 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -32,23 +32,25 @@
 #include <vector>
 
 #include <linux/unistd.h>
+#include <poll.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <sys/time.h>
 #include <sys/types.h>
 
 #include "android-base/stringprintf.h"
+#include "android-base/strings.h"
 
 #include "arch/instruction_set.h"
 #include "base/aborting.h"
 #include "base/file_utils.h"
 #include "base/memory_tool.h"
 #include "base/mutex.h"
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "oat_quick_method_header.h"
-#include "os.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 
 #endif
 
@@ -145,21 +147,14 @@
   bool prefix_written = false;
 
   for (;;) {
-    constexpr uint32_t kWaitTimeExpectedMicros = 500 * 1000;
-    constexpr uint32_t kWaitTimeUnexpectedMicros = 50 * 1000;
+    constexpr uint32_t kWaitTimeExpectedMilli = 500;
+    constexpr uint32_t kWaitTimeUnexpectedMilli = 50;
 
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = expected > 0 ? kWaitTimeExpectedMicros : kWaitTimeUnexpectedMicros;
-
-    fd_set rfds;
-    FD_ZERO(&rfds);
-    FD_SET(in, &rfds);
-
-    int retval = TEMP_FAILURE_RETRY(select(in + 1, &rfds, nullptr, nullptr, &tv));
-
-    if (retval < 0) {
-      // Other side may have crashed or other errors.
+    int timeout = expected > 0 ? kWaitTimeExpectedMilli : kWaitTimeUnexpectedMilli;
+    struct pollfd read_fd{in, POLLIN, 0};
+    int retval = TEMP_FAILURE_RETRY(poll(&read_fd, 1, timeout));
+    if (retval == -1) {
+      // An error occurred.
       pipe->reset();
       return;
     }
@@ -169,19 +164,23 @@
       return;
     }
 
-    DCHECK_EQ(retval, 1);
+    if (!(read_fd.revents & POLLIN)) {
+      // addr2line call exited.
+      pipe->reset();
+      return;
+    }
 
     constexpr size_t kMaxBuffer = 128;  // Relatively small buffer. Should be OK as we're on an
     // alt stack, but just to be sure...
     char buffer[kMaxBuffer];
     memset(buffer, 0, kMaxBuffer);
     int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1));
-
-    if (bytes_read < 0) {
+    if (bytes_read <= 0) {
       // This should not really happen...
       pipe->reset();
       return;
     }
+    buffer[bytes_read] = '\0';
 
     char* tmp = buffer;
     while (*tmp != 0) {
@@ -219,8 +218,10 @@
                       std::unique_ptr<Addr2linePipe>* pipe /* inout */) {
   DCHECK(pipe != nullptr);
 
-  if (map_src == "[vdso]") {
-    // Special-case this, our setup has problems with this.
+  if (map_src == "[vdso]" || android::base::EndsWith(map_src, ".vdex")) {
+    // addr2line will not work on the vdso.
+    // vdex files are special frames injected for the interpreter
+    // so they don't have any line number information available.
     return;
   }
 
diff --git a/runtime/oat.h b/runtime/oat.h
index 292c9d6..01d3914 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Math.pow() intrinsic.
-  static constexpr uint8_t kOatVersion[] = { '1', '3', '8', '\0' };
+  // Last oat version changed reason: Retrieve Class* and String* from .data.bimg.rel.ro .
+  static constexpr uint8_t kOatVersion[] = { '1', '4', '0', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 3576683..cfbcda3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -40,9 +40,11 @@
 #include "base/enums.h"
 #include "base/file_utils.h"
 #include "base/logging.h"  // For VLOG_IS_ON.
+#include "base/os.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file_loader.h"
 #include "dex/dex_file_types.h"
@@ -58,10 +60,8 @@
 #include "oat.h"
 #include "oat_file-inl.h"
 #include "oat_file_manager.h"
-#include "os.h"
 #include "runtime.h"
 #include "type_lookup_table.h"
-#include "utils.h"
 #include "vdex_file.h"
 
 namespace art {
@@ -343,6 +343,19 @@
   // Readjust to be non-inclusive upper bound.
   end_ += sizeof(uint32_t);
 
+  data_bimg_rel_ro_begin_ = FindDynamicSymbolAddress("oatdatabimgrelro", &symbol_error_msg);
+  if (data_bimg_rel_ro_begin_ != nullptr) {
+    data_bimg_rel_ro_end_ =
+        FindDynamicSymbolAddress("oatdatabimgrelrolastword", &symbol_error_msg);
+    if (data_bimg_rel_ro_end_ == nullptr) {
+      *error_msg =
+          StringPrintf("Failed to find oatdatabimgrelrolastword symbol in '%s'", file_path.c_str());
+      return false;
+    }
+    // Readjust to be non-inclusive upper bound.
+    data_bimg_rel_ro_end_ += sizeof(uint32_t);
+  }
+
   bss_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbss", &symbol_error_msg));
   if (bss_begin_ == nullptr) {
     // No .bss section.
@@ -399,37 +412,6 @@
   return true;
 }
 
-static inline bool MapConstantTables(const gc::space::ImageSpace* space,
-                                     uint8_t* address) {
-  // If MREMAP_DUP is ever merged to Linux kernel, use it to avoid the unnecessary open()/close().
-  // Note: The current approach relies on the filename still referencing the same inode.
-
-  File file(space->GetImageFilename(), O_RDONLY, /* checkUsage */ false);
-  if (!file.IsOpened()) {
-    LOG(ERROR) << "Failed to open boot image file " << space->GetImageFilename();
-    return false;
-  }
-
-  uint32_t offset = space->GetImageHeader().GetBootImageConstantTablesOffset();
-  uint32_t size = space->GetImageHeader().GetBootImageConstantTablesSize();
-  std::string error_msg;
-  std::unique_ptr<MemMap> mem_map(MemMap::MapFileAtAddress(address,
-                                                           size,
-                                                           PROT_READ,
-                                                           MAP_PRIVATE,
-                                                           file.Fd(),
-                                                           offset,
-                                                           /* low_4gb */ false,
-                                                           /* reuse */ true,
-                                                           file.GetPath().c_str(),
-                                                           &error_msg));
-  if (mem_map == nullptr) {
-    LOG(ERROR) << "Failed to mmap boot image tables from file " << space->GetImageFilename();
-    return false;
-  }
-  return true;
-}
-
 static bool ReadIndexBssMapping(OatFile* oat_file,
                                 /*inout*/const uint8_t** oat,
                                 size_t dex_file_index,
@@ -536,6 +518,17 @@
   }
   const uint8_t* oat = Begin() + oat_dex_files_offset;  // Jump to the OatDexFile records.
 
+  if (!IsAligned<sizeof(uint32_t)>(data_bimg_rel_ro_begin_) ||
+      !IsAligned<sizeof(uint32_t)>(data_bimg_rel_ro_end_) ||
+      data_bimg_rel_ro_begin_ > data_bimg_rel_ro_end_) {
+    *error_msg = StringPrintf("In oat file '%s' found unaligned or unordered databimgrelro "
+                                  "symbol(s): begin = %p, end = %p",
+                              GetLocation().c_str(),
+                              data_bimg_rel_ro_begin_,
+                              data_bimg_rel_ro_end_);
+    return false;
+  }
+
   DCHECK_GE(static_cast<size_t>(pointer_size), alignof(GcRoot<mirror::Object>));
   if (!IsAligned<kPageSize>(bss_begin_) ||
       !IsAlignedParam(bss_methods_, static_cast<size_t>(pointer_size)) ||
@@ -564,12 +557,15 @@
     return false;
   }
 
-  uint8_t* after_tables =
-      (bss_methods_ != nullptr) ? bss_methods_ : bss_roots_;  // May be null.
-  uint8_t* boot_image_tables = (bss_begin_ == after_tables) ? nullptr : bss_begin_;
-  uint8_t* boot_image_tables_end =
-      (bss_begin_ == after_tables) ? nullptr : (after_tables != nullptr) ? after_tables : bss_end_;
-  DCHECK_EQ(boot_image_tables != nullptr, boot_image_tables_end != nullptr);
+  if (bss_methods_ != nullptr && bss_methods_ != bss_begin_) {
+    *error_msg = StringPrintf("In oat file '%s' found unexpected .bss gap before 'oatbssmethods': "
+                                  "begin = %p, methods = %p",
+                              GetLocation().c_str(),
+                              bss_begin_,
+                              bss_methods_);
+    return false;
+  }
+
   uint32_t dex_file_count = GetOatHeader().GetDexFileCount();
   oat_dex_files_storage_.reserve(dex_file_count);
   for (size_t i = 0; i < dex_file_count; i++) {
@@ -849,39 +845,28 @@
     }
   }
 
-  if (boot_image_tables != nullptr) {
-    Runtime* runtime = Runtime::Current();
+  Runtime* runtime = Runtime::Current();
+
+  if (DataBimgRelRoBegin() != nullptr) {
+    // Make .data.bimg.rel.ro read only. ClassLinker shall make it writable for relocation.
+    uint8_t* reloc_begin = const_cast<uint8_t*>(DataBimgRelRoBegin());
+    CheckedCall(mprotect, "protect relocations", reloc_begin, DataBimgRelRoSize(), PROT_READ);
     if (UNLIKELY(runtime == nullptr)) {
-      // This must be oatdump without boot image. Make sure the .bss is inaccessible.
-      CheckedCall(mprotect, "protect bss", const_cast<uint8_t*>(BssBegin()), BssSize(), PROT_NONE);
+      // This must be oatdump without boot image.
     } else if (!IsExecutable()) {
-      // Do not try to mmap boot image tables into .bss if the oat file is not executable.
+      // Do not check whether we have a boot image if the oat file is not executable.
+    } else if (UNLIKELY(runtime->GetHeap()->GetBootImageSpaces().empty())) {
+      *error_msg = StringPrintf("Cannot load oat file '%s' with .data.bimg.rel.ro as executable "
+                                    "without boot image.",
+                                GetLocation().c_str());
+      return false;
     } else {
-      // Map boot image tables into the .bss. The reserved size must match size of the tables.
-      size_t reserved_size = static_cast<size_t>(boot_image_tables_end - boot_image_tables);
-      size_t tables_size = 0u;
-      for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) {
-        tables_size += space->GetImageHeader().GetBootImageConstantTablesSize();
-        DCHECK_ALIGNED(tables_size, kPageSize);
-      }
-      if (tables_size != reserved_size) {
-        *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, "
-                                      " %zu bytes, should be %zu.",
-                                  GetLocation().c_str(),
-                                  reserved_size,
-                                  tables_size);
-        return false;
-      }
-      for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-        uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize();
-        if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) {
-          return false;
-        }
-        boot_image_tables += current_tables_size;
-      }
-      DCHECK(boot_image_tables == boot_image_tables_end);
+      // ClassLinker shall perform the relocation when we register a dex file from
+      // this oat file. We do not do the relocation here to avoid dirtying the pages
+      // if the code is never actually ready to be executed.
     }
   }
+
   return true;
 }
 
@@ -1513,6 +1498,8 @@
       vdex_(nullptr),
       begin_(nullptr),
       end_(nullptr),
+      data_bimg_rel_ro_begin_(nullptr),
+      data_bimg_rel_ro_end_(nullptr),
       bss_begin_(nullptr),
       bss_end_(nullptr),
       bss_methods_(nullptr),
@@ -1542,22 +1529,6 @@
   return end_;
 }
 
-const uint8_t* OatFile::BssBegin() const {
-  return bss_begin_;
-}
-
-const uint8_t* OatFile::BssEnd() const {
-  return bss_end_;
-}
-
-const uint8_t* OatFile::VdexBegin() const {
-  return vdex_begin_;
-}
-
-const uint8_t* OatFile::VdexEnd() const {
-  return vdex_end_;
-}
-
 const uint8_t* OatFile::DexBegin() const {
   return vdex_->Begin();
 }
@@ -1566,6 +1537,16 @@
   return vdex_->End();
 }
 
+ArrayRef<const uint32_t> OatFile::GetBootImageRelocations() const {
+  if (data_bimg_rel_ro_begin_ != nullptr) {
+    const uint32_t* relocations = reinterpret_cast<const uint32_t*>(data_bimg_rel_ro_begin_);
+    const uint32_t* relocations_end = reinterpret_cast<const uint32_t*>(data_bimg_rel_ro_end_);
+    return ArrayRef<const uint32_t>(relocations, relocations_end - relocations);
+  } else {
+    return ArrayRef<const uint32_t>();
+  }
+}
+
 ArrayRef<ArtMethod*> OatFile::GetBssMethods() const {
   if (bss_methods_ != nullptr) {
     ArtMethod** methods = reinterpret_cast<ArtMethod**>(bss_methods_);
@@ -1812,9 +1793,9 @@
     // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices.
     // Other devices have enough page cache to get performance benefits from loading more pages
     // into the page cache.
-    MadviseLargestPageAlignedRegion(dex_file.Begin(),
-                                    dex_file.Begin() + dex_file.Size(),
-                                    MADV_RANDOM);
+    DexLayoutSection::MadviseLargestPageAlignedRegion(dex_file.Begin(),
+                                                      dex_file.Begin() + dex_file.Size(),
+                                                      MADV_RANDOM);
   }
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
   if (oat_dex_file != nullptr) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 255a31b..24868dd 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -23,9 +23,11 @@
 
 #include "base/array_ref.h"
 #include "base/mutex.h"
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "base/stringpiece.h"
 #include "base/tracking_safe_map.h"
+#include "base/utils.h"
 #include "class_status.h"
 #include "compiler_filter.h"
 #include "dex/dex_file.h"
@@ -34,9 +36,7 @@
 #include "index_bss_mapping.h"
 #include "mirror/object.h"
 #include "oat.h"
-#include "os.h"
 #include "type_lookup_table.h"
-#include "utils.h"
 
 namespace art {
 
@@ -275,6 +275,10 @@
     return p >= Begin() && p < End();
   }
 
+  size_t DataBimgRelRoSize() const {
+    return DataBimgRelRoEnd() - DataBimgRelRoBegin();
+  }
+
   size_t BssSize() const {
     return BssEnd() - BssBegin();
   }
@@ -300,15 +304,19 @@
   const uint8_t* Begin() const;
   const uint8_t* End() const;
 
-  const uint8_t* BssBegin() const;
-  const uint8_t* BssEnd() const;
+  const uint8_t* DataBimgRelRoBegin() const { return data_bimg_rel_ro_begin_; }
+  const uint8_t* DataBimgRelRoEnd() const { return data_bimg_rel_ro_end_; }
 
-  const uint8_t* VdexBegin() const;
-  const uint8_t* VdexEnd() const;
+  const uint8_t* BssBegin() const { return bss_begin_; }
+  const uint8_t* BssEnd() const { return bss_end_; }
+
+  const uint8_t* VdexBegin() const { return vdex_begin_; }
+  const uint8_t* VdexEnd() const { return vdex_end_; }
 
   const uint8_t* DexBegin() const;
   const uint8_t* DexEnd() const;
 
+  ArrayRef<const uint32_t> GetBootImageRelocations() const;
   ArrayRef<ArtMethod*> GetBssMethods() const;
   ArrayRef<GcRoot<mirror::Object>> GetBssGcRoots() const;
 
@@ -355,6 +363,12 @@
   // Pointer to end of oat region for bounds checking.
   const uint8_t* end_;
 
+  // Pointer to the .data.bimg.rel.ro section, if present, otherwise null.
+  const uint8_t* data_bimg_rel_ro_begin_;
+
+  // Pointer to the end of the .data.bimg.rel.ro section, if present, otherwise null.
+  const uint8_t* data_bimg_rel_ro_end_;
+
   // Pointer to the .bss section, if present, otherwise null.
   uint8_t* bss_begin_;
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 6bf05b7..5888c37 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -25,7 +25,9 @@
 
 #include "base/file_utils.h"
 #include "base/logging.h"  // For VLOG.
+#include "base/os.h"
 #include "base/stl_util.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "compiler_filter.h"
 #include "dex/art_dex_file_loader.h"
@@ -35,10 +37,8 @@
 #include "gc/space/image_space.h"
 #include "image.h"
 #include "oat.h"
-#include "os.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
-#include "utils.h"
 #include "vdex_file.h"
 #include "class_loader_context.h"
 
@@ -1261,18 +1261,37 @@
     return ReleaseFile();
   }
 
-  if (Status() == kOatRelocationOutOfDate) {
-    // We are loading an oat file for runtime use that needs relocation.
-    // Reload the file non-executable to ensure that we interpret out of the
-    // dex code in the oat file rather than trying to execute the unrelocated
-    // compiled code.
-    oat_file_assistant_->load_executable_ = false;
-    Reset();
-    if (IsUseable()) {
-      CHECK(!IsExecutable());
-      return ReleaseFile();
-    }
+  switch (Status()) {
+    case kOatBootImageOutOfDate:
+      // OutOfDate may be either a mismatched image, or a missing image.
+      if (oat_file_assistant_->HasOriginalDexFiles()) {
+        // If there are original dex files, it is better to use them (to avoid a potential
+        // quickening mismatch because the boot image changed).
+        break;
+      }
+      // If we do not accept the oat file, we may not have access to dex bytecode at all. Grudgingly
+      // go forward.
+      FALLTHROUGH_INTENDED;
+
+    case kOatRelocationOutOfDate:
+      // We are loading an oat file for runtime use that needs relocation.
+      // Reload the file non-executable to ensure that we interpret out of the
+      // dex code in the oat file rather than trying to execute the unrelocated
+      // compiled code.
+      oat_file_assistant_->load_executable_ = false;
+      Reset();
+      if (IsUseable()) {
+        CHECK(!IsExecutable());
+        return ReleaseFile();
+      }
+      break;
+
+    case kOatUpToDate:
+    case kOatCannotOpen:
+    case kOatDexOutOfDate:
+      break;
   }
+
   return std::unique_ptr<OatFile>();
 }
 
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6da49a9..8d6ec00 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -23,12 +23,12 @@
 #include <string>
 
 #include "arch/instruction_set.h"
+#include "base/os.h"
 #include "base/scoped_flock.h"
 #include "base/unix_file/fd_file.h"
 #include "compiler_filter.h"
 #include "class_loader_context.h"
 #include "oat_file.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 72f7d02..676071b 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -27,16 +27,16 @@
 #include "android-base/strings.h"
 
 #include "art_field-inl.h"
+#include "base/os.h"
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "class_loader_context.h"
 #include "common_runtime_test.h"
 #include "dexopt_test.h"
 #include "oat_file.h"
 #include "oat_file_manager.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index e419444..f6fb9de 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -469,6 +469,9 @@
 
   // Get the oat file on disk.
   std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
+  VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
+            << reinterpret_cast<uintptr_t>(oat_file.get())
+            << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
 
   if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 4443255..f0966b7 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -19,10 +19,10 @@
 
 #include "arch/instruction_set.h"
 #include "base/macros.h"
+#include "base/utils.h"
 #include "method_info.h"
 #include "quick/quick_method_frame_info.h"
 #include "stack_map.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c61ecc8..5518eb2 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -23,13 +23,13 @@
 #include "base/file_utils.h"
 #include "base/macros.h"
 #include "base/stringpiece.h"
+#include "base/utils.h"
 #include "debugger.h"
 #include "gc/heap.h"
 #include "monitor.h"
 #include "runtime.h"
 #include "ti/agent.h"
 #include "trace.h"
-#include "utils.h"
 
 #include "cmdline_parser.h"
 #include "runtime_options.h"
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index a77d100..58f6c04 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -19,6 +19,7 @@
 
 #include "read_barrier.h"
 
+#include "base/utils.h"
 #include "gc/accounting/read_barrier_table.h"
 #include "gc/collector/concurrent_copying-inl.h"
 #include "gc/heap.h"
@@ -26,7 +27,6 @@
 #include "mirror/object_reference.h"
 #include "mirror/reference.h"
 #include "runtime.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index a6df27b..d62cbdb 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -19,6 +19,7 @@
 #include "android-base/stringprintf.h"
 
 #include "base/mutex.h"
+#include "base/utils.h"
 #include "gc/allocation_record.h"
 #include "gc/heap.h"
 #include "indirect_reference_table.h"
@@ -30,7 +31,6 @@
 #include "mirror/string-inl.h"
 #include "runtime-inl.h"
 #include "thread.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index 87432ab..26fb021 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -21,13 +21,13 @@
 
 #include "android-base/stringprintf.h"
 
+#include "base/utils.h"
 #include "common_throws.h"
 #include "dex/descriptors_names.h"
 #include "dex/primitive.h"
 #include "jvalue-inl.h"
 #include "mirror/object-inl.h"
 #include "obj_ptr-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 4442fc6..7d9d342 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -56,16 +56,20 @@
 #include "art_method-inl.h"
 #include "asm_support.h"
 #include "asm_support_check.h"
-#include "atomic.h"
 #include "base/aborting.h"
 #include "base/arena_allocator.h"
+#include "base/atomic.h"
 #include "base/dumpable.h"
 #include "base/enums.h"
 #include "base/file_utils.h"
 #include "base/memory_tool.h"
+#include "base/mutex.h"
+#include "base/os.h"
+#include "base/quasi_atomic.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "compiler_callbacks.h"
 #include "debugger.h"
@@ -141,7 +145,6 @@
 #include "oat_file.h"
 #include "oat_file_manager.h"
 #include "object_callbacks.h"
-#include "os.h"
 #include "parsed_options.h"
 #include "quick/quick_method_frame_info.h"
 #include "reflection.h"
@@ -157,7 +160,6 @@
 #include "ti/agent.h"
 #include "trace.h"
 #include "transaction.h"
-#include "utils.h"
 #include "vdex_file.h"
 #include "verifier/method_verifier.h"
 #include "well_known_classes.h"
@@ -621,6 +623,7 @@
 bool Runtime::ParseOptions(const RuntimeOptions& raw_options,
                            bool ignore_unrecognized,
                            RuntimeArgumentMap* runtime_options) {
+  Locks::Init();
   InitLogging(/* argv */ nullptr, Abort);  // Calls Locks::Init() as a side effect.
   bool parsed = ParsedOptions::Parse(raw_options, ignore_unrecognized, runtime_options);
   if (!parsed) {
@@ -1275,7 +1278,7 @@
   jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider);
   switch (jdwp_provider_) {
     case JdwpProvider::kNone: {
-      LOG(INFO) << "Disabling all JDWP support.";
+      VLOG(jdwp) << "Disabling all JDWP support.";
       if (!jdwp_options_.empty()) {
         bool has_transport = jdwp_options_.find("transport") != std::string::npos;
         const char* transport_internal = !has_transport ? "transport=dt_android_adb," : "";
@@ -2215,7 +2218,7 @@
     LOG(WARNING) << "JIT profile information will not be recorded: profile filename is empty.";
     return;
   }
-  if (!FileExists(profile_output_filename)) {
+  if (!OS::FileExists(profile_output_filename.c_str(), false /*check_file_type*/)) {
     LOG(WARNING) << "JIT profile information will not be recorded: profile file does not exits.";
     return;
   }
diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h
index 06d6627..3fba441 100644
--- a/runtime/runtime_common.h
+++ b/runtime/runtime_common.h
@@ -31,8 +31,8 @@
 #include <iomanip>
 
 #include "base/dumpable.h"
+#include "base/utils.h"
 #include "native_stack_dump.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc
index bce0d81..f8c680d 100644
--- a/runtime/runtime_options.cc
+++ b/runtime/runtime_options.cc
@@ -18,13 +18,13 @@
 
 #include <memory>
 
+#include "base/utils.h"
 #include "debugger.h"
 #include "gc/heap.h"
 #include "monitor.h"
 #include "runtime.h"
 #include "thread_list.h"
 #include "trace.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 9c3afbb..d590ad5 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -36,18 +36,18 @@
 #include "arch/instruction_set.h"
 #include "base/file_utils.h"
 #include "base/logging.h"  // For GetCmdLine.
+#include "base/os.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "gc/heap.h"
 #include "jit/profile_saver.h"
-#include "os.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "signal_set.h"
 #include "thread.h"
 #include "thread_list.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 4cdf015..5b03c2d 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -45,6 +45,7 @@
 #include "base/systrace.h"
 #include "base/timing_logger.h"
 #include "base/to_str.h"
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "debugger.h"
 #include "dex/descriptors_names.h"
@@ -90,7 +91,6 @@
 #include "stack_map.h"
 #include "thread-inl.h"
 #include "thread_list.h"
-#include "utils.h"
 #include "verifier/method_verifier.h"
 #include "verify_object.h"
 #include "well_known_classes.h"
diff --git a/runtime/thread.h b/runtime/thread.h
index 295685e..6549fc1 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -28,7 +28,7 @@
 
 #include "arch/context.h"
 #include "arch/instruction_set.h"
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/enums.h"
 #include "base/macros.h"
 #include "base/mutex.h"
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index 9673eee..d05fecf 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -19,7 +19,7 @@
 #include <signal.h>
 
 #include "base/logging.h"  // For VLOG.
-#include "utils.h"
+#include "base/utils.h"
 
 namespace art {
 
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 386cdf0..bec1150 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -29,9 +29,9 @@
 #include "base/casts.h"
 #include "base/stl_util.h"
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "runtime.h"
 #include "thread-current-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc
index 28aa21f..895a108 100644
--- a/runtime/thread_pool_test.cc
+++ b/runtime/thread_pool_test.cc
@@ -18,7 +18,7 @@
 
 #include <string>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "common_runtime_test.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
diff --git a/runtime/trace.cc b/runtime/trace.cc
index d97dcb5..0f321b6 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -24,10 +24,12 @@
 #include "art_method-inl.h"
 #include "base/casts.h"
 #include "base/enums.h"
+#include "base/os.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "common_throws.h"
 #include "debugger.h"
@@ -41,12 +43,10 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "nativehelper/scoped_local_ref.h"
-#include "os.h"
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread.h"
 #include "thread_list.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/trace.h b/runtime/trace.h
index 7ce12da..86b8d00 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -26,12 +26,12 @@
 #include <unordered_map>
 #include <vector>
 
-#include "atomic.h"
+#include "base/atomic.h"
 #include "base/macros.h"
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "globals.h"
 #include "instrumentation.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc
index 925a908..7e204fc 100644
--- a/runtime/type_lookup_table.cc
+++ b/runtime/type_lookup_table.cc
@@ -20,9 +20,9 @@
 #include <memory>
 
 #include "base/bit_utils.h"
+#include "base/utils.h"
 #include "dex/dex_file-inl.h"
 #include "dex/utf-inl.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
deleted file mode 100644
index e67e93f..0000000
--- a/runtime/utils_test.cc
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "utils.h"
-
-#include <libgen.h>
-#include <stdlib.h>
-
-#include "base/enums.h"
-#include "base/file_utils.h"
-#include "base/stl_util.h"
-#include "class_linker-inl.h"
-#include "common_runtime_test.h"
-#include "exec_utils.h"
-#include "handle_scope-inl.h"
-#include "mirror/array-inl.h"
-#include "mirror/array.h"
-#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/string.h"
-#include "scoped_thread_state_change-inl.h"
-
-#include "base/memory_tool.h"
-
-namespace art {
-
-class UtilsTest : public CommonRuntimeTest {};
-
-TEST_F(UtilsTest, PrettyTypeOf) {
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ("null", mirror::Object::PrettyTypeOf(nullptr));
-
-  StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::String> s(hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "")));
-  EXPECT_EQ("java.lang.String", mirror::Object::PrettyTypeOf(s.Get()));
-
-  Handle<mirror::ShortArray> a(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 2)));
-  EXPECT_EQ("short[]", mirror::Object::PrettyTypeOf(a.Get()));
-
-  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
-  ASSERT_TRUE(c != nullptr);
-  mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
-  EXPECT_EQ("java.lang.String[]", mirror::Object::PrettyTypeOf(o));
-  EXPECT_EQ("java.lang.Class<java.lang.String[]>", mirror::Object::PrettyTypeOf(o->GetClass()));
-}
-
-TEST_F(UtilsTest, PrettyClass) {
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ("null", mirror::Class::PrettyClass(nullptr));
-  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
-  ASSERT_TRUE(c != nullptr);
-  mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
-  EXPECT_EQ("java.lang.Class<java.lang.String[]>", mirror::Class::PrettyClass(o->GetClass()));
-}
-
-TEST_F(UtilsTest, PrettyClassAndClassLoader) {
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ("null", mirror::Class::PrettyClassAndClassLoader(nullptr));
-  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
-  ASSERT_TRUE(c != nullptr);
-  mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
-  EXPECT_EQ("java.lang.Class<java.lang.String[],null>",
-            mirror::Class::PrettyClassAndClassLoader(o->GetClass()));
-}
-
-TEST_F(UtilsTest, PrettyField) {
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ("null", ArtField::PrettyField(nullptr));
-
-  mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(),
-                                                                   "Ljava/lang/String;");
-
-  ArtField* f;
-  f = java_lang_String->FindDeclaredInstanceField("count", "I");
-  EXPECT_EQ("int java.lang.String.count", f->PrettyField());
-  EXPECT_EQ("java.lang.String.count", f->PrettyField(false));
-}
-
-TEST_F(UtilsTest, PrettySize) {
-  EXPECT_EQ("1GB", PrettySize(1 * GB));
-  EXPECT_EQ("2GB", PrettySize(2 * GB));
-  if (sizeof(size_t) > sizeof(uint32_t)) {
-    EXPECT_EQ("100GB", PrettySize(100 * GB));
-  }
-  EXPECT_EQ("1024KB", PrettySize(1 * MB));
-  EXPECT_EQ("10MB", PrettySize(10 * MB));
-  EXPECT_EQ("100MB", PrettySize(100 * MB));
-  EXPECT_EQ("1024B", PrettySize(1 * KB));
-  EXPECT_EQ("10KB", PrettySize(10 * KB));
-  EXPECT_EQ("100KB", PrettySize(100 * KB));
-  EXPECT_EQ("0B", PrettySize(0));
-  EXPECT_EQ("1B", PrettySize(1));
-  EXPECT_EQ("10B", PrettySize(10));
-  EXPECT_EQ("100B", PrettySize(100));
-  EXPECT_EQ("512B", PrettySize(512));
-}
-
-TEST_F(UtilsTest, JniShortName_JniLongName) {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
-  ASSERT_TRUE(c != nullptr);
-  ArtMethod* m;
-
-  m = c->FindClassMethod("charAt", "(I)C", kRuntimePointerSize);
-  ASSERT_TRUE(m != nullptr);
-  ASSERT_FALSE(m->IsDirect());
-  EXPECT_EQ("Java_java_lang_String_charAt", m->JniShortName());
-  EXPECT_EQ("Java_java_lang_String_charAt__I", m->JniLongName());
-
-  m = c->FindClassMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize);
-  ASSERT_TRUE(m != nullptr);
-  ASSERT_FALSE(m->IsDirect());
-  EXPECT_EQ("Java_java_lang_String_indexOf", m->JniShortName());
-  EXPECT_EQ("Java_java_lang_String_indexOf__Ljava_lang_String_2I", m->JniLongName());
-
-  m = c->FindClassMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize);
-  ASSERT_TRUE(m != nullptr);
-  ASSERT_TRUE(m->IsStatic());
-  EXPECT_EQ("Java_java_lang_String_copyValueOf", m->JniShortName());
-  EXPECT_EQ("Java_java_lang_String_copyValueOf___3CII", m->JniLongName());
-}
-
-TEST_F(UtilsTest, Split) {
-  std::vector<std::string> actual;
-  std::vector<std::string> expected;
-
-  expected.clear();
-
-  actual.clear();
-  Split("", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split(":", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  expected.clear();
-  expected.push_back("foo");
-
-  actual.clear();
-  Split(":foo", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split("foo:", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split(":foo:", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  expected.push_back("bar");
-
-  actual.clear();
-  Split("foo:bar", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split(":foo:bar", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split("foo:bar:", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split(":foo:bar:", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  expected.push_back("baz");
-
-  actual.clear();
-  Split("foo:bar:baz", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split(":foo:bar:baz", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split("foo:bar:baz:", ':', &actual);
-  EXPECT_EQ(expected, actual);
-
-  actual.clear();
-  Split(":foo:bar:baz:", ':', &actual);
-  EXPECT_EQ(expected, actual);
-}
-
-TEST_F(UtilsTest, ArrayCount) {
-  int i[64];
-  EXPECT_EQ(ArrayCount(i), 64u);
-  char c[7];
-  EXPECT_EQ(ArrayCount(c), 7u);
-}
-
-TEST_F(UtilsTest, BoundsCheckedCast) {
-  char buffer[64];
-  const char* buffer_end = buffer + ArrayCount(buffer);
-  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr);
-  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end),
-            reinterpret_cast<const uint64_t*>(buffer));
-  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end),
-            reinterpret_cast<const uint64_t*>(buffer + 56));
-  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr);
-  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
-}
-
-}  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index d27f431..72f03f2 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -22,9 +22,9 @@
 
 #include "base/array_ref.h"
 #include "base/macros.h"
+#include "base/os.h"
 #include "dex/compact_offset_table.h"
 #include "mem_map.h"
-#include "os.h"
 #include "quicken_info.h"
 
 namespace art {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 52bd736..2a2afc4 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -30,6 +30,7 @@
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
+#include "base/utils.h"
 #include "class_linker.h"
 #include "compiler_callbacks.h"
 #include "dex/descriptors_names.h"
@@ -55,7 +56,6 @@
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
-#include "utils.h"
 #include "verifier_compiler_binding.h"
 #include "verifier_deps.h"
 
@@ -105,25 +105,27 @@
 PcToRegisterLineTable::~PcToRegisterLineTable() {}
 
 // Note: returns true on failure.
-ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool condition,
-                                             const char* error_msg, uint32_t work_insn_idx) {
+inline bool MethodVerifier::FailOrAbort(bool condition,
+                                        const char* error_msg,
+                                        uint32_t work_insn_idx) {
   if (kIsDebugBuild) {
     // In a debug build, abort if the error condition is wrong. Only warn if
     // we are already aborting (as this verification is likely run to print
     // lock information).
     if (LIKELY(gAborting == 0)) {
-      DCHECK(condition) << error_msg << work_insn_idx;
+      DCHECK(condition) << error_msg << work_insn_idx << " "
+                        << dex_file_->PrettyMethod(dex_method_idx_);
     } else {
       if (!condition) {
         LOG(ERROR) << error_msg << work_insn_idx;
-        verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx;
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx;
         return true;
       }
     }
   } else {
     // In a non-debug build, just fail the class.
     if (!condition) {
-      verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx;
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx;
       return true;
     }
   }
@@ -2683,7 +2685,7 @@
         while (0 != instance_of_idx && !GetInstructionFlags(instance_of_idx).IsOpcode()) {
           instance_of_idx--;
         }
-        if (FailOrAbort(this, GetInstructionFlags(instance_of_idx).IsOpcode(),
+        if (FailOrAbort(GetInstructionFlags(instance_of_idx).IsOpcode(),
                         "Unable to get previous instruction of if-eqz/if-nez for work index ",
                         work_insn_idx_)) {
           break;
@@ -2750,7 +2752,7 @@
             while (0 != move_idx && !GetInstructionFlags(move_idx).IsOpcode()) {
               move_idx--;
             }
-            if (FailOrAbort(this, GetInstructionFlags(move_idx).IsOpcode(),
+            if (FailOrAbort(GetInstructionFlags(move_idx).IsOpcode(),
                             "Unable to get previous instruction of if-eqz/if-nez for work index ",
                             work_insn_idx_)) {
               break;
@@ -3863,8 +3865,7 @@
               // odd case, but nothing to do
             } else {
               common_super = &common_super->Merge(exception, &reg_types_, this);
-              if (FailOrAbort(this,
-                              reg_types_.JavaLangThrowable(false).IsAssignableFrom(
+              if (FailOrAbort(reg_types_.JavaLangThrowable(false).IsAssignableFrom(
                                   *common_super, this),
                               "java.lang.Throwable is not assignable-from common_super at ",
                               work_insn_idx_)) {
@@ -4206,14 +4207,19 @@
   if (it.Size() < 3) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
                                       << " has too few arguments: "
-                                      << it.Size() << "< 3";
+                                      << it.Size() << " < 3";
     return false;
   }
 
   // Get and check the first argument: the method handle (index range
   // checked by the dex file verifier).
   uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
-  it.Next();
+  if (method_handle_idx > dex_file_->NumMethodHandles()) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx
+                                      << " method handle index invalid " << method_handle_idx
+                                      << " >= "  << dex_file_->NumMethodHandles();
+    return false;
+  }
 
   const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
   if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
@@ -4222,93 +4228,6 @@
                                       << mh.method_handle_type_;
     return false;
   }
-
-  // Skip the second argument, the name to resolve, as checked by the
-  // dex file verifier.
-  it.Next();
-
-  // Skip the third argument, the method type expected, as checked by
-  // the dex file verifier.
-  it.Next();
-
-  // Check the bootstrap method handle and remaining arguments.
-  const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_);
-  uint32_t length;
-  const char* shorty = dex_file_->GetMethodShorty(method_id, &length);
-
-  if (it.Size() < length - 1) {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
-                                      << " too few arguments for bootstrap method: "
-                                      << it.Size() << " < " << (length - 1);
-    return false;
-  }
-
-  // Check the return type and first 3 arguments are references
-  // (CallSite, Lookup, String, MethodType). If they are not of the
-  // expected types (or subtypes), it will trigger a
-  // WrongMethodTypeException during execution.
-  if (shorty[0] != 'L') {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
-                                      << " bootstrap return type is not a reference";
-    return false;
-  }
-
-  for (uint32_t i = 1; i < 4; ++i) {
-    if (shorty[i] != 'L') {
-      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
-                                        << " bootstrap method argument " << (i - 1)
-                                        << " is not a reference";
-      return false;
-    }
-  }
-
-  // Check the optional arguments.
-  for (uint32_t i = 4; i < length; ++i, it.Next()) {
-    bool match = false;
-    switch (it.GetValueType()) {
-      case EncodedArrayValueIterator::ValueType::kBoolean:
-      case EncodedArrayValueIterator::ValueType::kByte:
-      case EncodedArrayValueIterator::ValueType::kShort:
-      case EncodedArrayValueIterator::ValueType::kChar:
-      case EncodedArrayValueIterator::ValueType::kInt:
-        // These all fit within one register and encoders do not seem
-        // too exacting on the encoding type they use (ie using
-        // integer for all of these).
-        match = (strchr("ZBCSI", shorty[i]) != nullptr);
-        break;
-      case EncodedArrayValueIterator::ValueType::kLong:
-        match = ('J' == shorty[i]);
-        break;
-      case EncodedArrayValueIterator::ValueType::kFloat:
-        match = ('F' == shorty[i]);
-        break;
-      case EncodedArrayValueIterator::ValueType::kDouble:
-        match = ('D' == shorty[i]);
-        break;
-      case EncodedArrayValueIterator::ValueType::kMethodType:
-      case EncodedArrayValueIterator::ValueType::kMethodHandle:
-      case EncodedArrayValueIterator::ValueType::kString:
-      case EncodedArrayValueIterator::ValueType::kType:
-      case EncodedArrayValueIterator::ValueType::kNull:
-        match = ('L' == shorty[i]);
-        break;
-      case EncodedArrayValueIterator::ValueType::kField:
-      case EncodedArrayValueIterator::ValueType::kMethod:
-      case EncodedArrayValueIterator::ValueType::kEnum:
-      case EncodedArrayValueIterator::ValueType::kArray:
-      case EncodedArrayValueIterator::ValueType::kAnnotation:
-        // Unreachable based on current EncodedArrayValueIterator::Next().
-        UNREACHABLE();
-    }
-
-    if (!match) {
-      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
-                                        << " bootstrap method argument " << (i - 1)
-                                        << " expected " << shorty[i]
-                                        << " got value type: " << it.GetValueType();
-      return false;
-    }
-  }
   return true;
 }
 
@@ -4512,8 +4431,9 @@
   if (klass->IsInterface()) {
     // Derive Object.class from Class.class.getSuperclass().
     mirror::Class* object_klass = klass->GetClass()->GetSuperClass();
-    if (FailOrAbort(this, object_klass->IsObjectClass(),
-                    "Failed to find Object class in quickened invoke receiver", work_insn_idx_)) {
+    if (FailOrAbort(object_klass->IsObjectClass(),
+                    "Failed to find Object class in quickened invoke receiver",
+                    work_insn_idx_)) {
       return nullptr;
     }
     dispatch_class = object_klass;
@@ -4521,7 +4441,8 @@
     dispatch_class = klass;
   }
   if (!dispatch_class->HasVTable()) {
-    FailOrAbort(this, allow_failure, "Receiver class has no vtable for quickened invoke at ",
+    FailOrAbort(allow_failure,
+                "Receiver class has no vtable for quickened invoke at ",
                 work_insn_idx_);
     return nullptr;
   }
@@ -4529,14 +4450,15 @@
   auto* cl = Runtime::Current()->GetClassLinker();
   auto pointer_size = cl->GetImagePointerSize();
   if (static_cast<int32_t>(vtable_index) >= dispatch_class->GetVTableLength()) {
-    FailOrAbort(this, allow_failure,
+    FailOrAbort(allow_failure,
                 "Receiver class has not enough vtable slots for quickened invoke at ",
                 work_insn_idx_);
     return nullptr;
   }
   ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index, pointer_size);
   if (self_->IsExceptionPending()) {
-    FailOrAbort(this, allow_failure, "Unexpected exception pending for quickened invoke at ",
+    FailOrAbort(allow_failure,
+                "Unexpected exception pending for quickened invoke at ",
                 work_insn_idx_);
     return nullptr;
   }
@@ -4552,11 +4474,13 @@
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name();
     return nullptr;
   }
-  if (FailOrAbort(this, !res_method->IsDirect(), "Quick-invoked method is direct at ",
+  if (FailOrAbort(!res_method->IsDirect(),
+                  "Quick-invoked method is direct at ",
                   work_insn_idx_)) {
     return nullptr;
   }
-  if (FailOrAbort(this, !res_method->IsStatic(), "Quick-invoked method is static at ",
+  if (FailOrAbort(!res_method->IsStatic(),
+                  "Quick-invoked method is static at ",
                   work_insn_idx_)) {
     return nullptr;
   }
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 26c598f..9a94297 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -721,6 +721,8 @@
   const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  ALWAYS_INLINE bool FailOrAbort(bool condition, const char* error_msg, uint32_t work_insn_idx);
+
   // The thread we're verifying on.
   Thread* const self_;
 
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 97c1b62..db3f093 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -21,11 +21,11 @@
 
 #include "android-base/strings.h"
 
+#include "base/utils.h"
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "dex/dex_file-inl.h"
 #include "scoped_thread_state_change-inl.h"
-#include "utils.h"
 #include "verifier_enums.h"
 
 namespace art {
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index 7b45690..aa54018 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -23,11 +23,11 @@
 
 #include <android-base/logging.h>
 
+#include "base/os.h"
 #include "base/safe_map.h"
 #include "base/unix_file/random_access_file.h"
 #include "globals.h"
 #include "mem_map.h"
-#include "os.h"
 
 // system/core/zip_archive definitions.
 struct ZipEntry;
diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc
index 4fc7ee2..48ee94c 100644
--- a/runtime/zip_archive_test.cc
+++ b/runtime/zip_archive_test.cc
@@ -22,9 +22,9 @@
 #include <zlib.h>
 #include <memory>
 
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/sigchainlib/Android.bp b/sigchainlib/Android.bp
index 1f490cd..a151d7a 100644
--- a/sigchainlib/Android.bp
+++ b/sigchainlib/Android.bp
@@ -35,7 +35,7 @@
         },
 
         android: {
-            shared_libs: ["liblog"],
+            whole_static_libs: ["libasync_safe"],
         },
     },
     // Sigchainlib is whole-statically linked into binaries. For Android.mk-based binaries,
@@ -56,7 +56,7 @@
     srcs: ["sigchain_dummy.cc"],
     target: {
         android: {
-            shared_libs: ["liblog"],
+            whole_static_libs: ["libasync_safe"],
         },
     },
 }
diff --git a/sigchainlib/log.h b/sigchainlib/log.h
new file mode 100644
index 0000000..d689930
--- /dev/null
+++ b/sigchainlib/log.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SIGCHAINLIB_LOG_H_
+#define ART_SIGCHAINLIB_LOG_H_
+
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+#define log(...) async_safe_format_log(ANDROID_LOG_ERROR, "libsigchain", __VA_ARGS__)
+#define fatal async_safe_fatal
+
+#else
+
+#include <stdarg.h>
+#include <stdio.h>
+
+static void log(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  va_end(ap);
+
+  printf("\n");
+}
+
+#define fatal(...) log(__VA_ARGS__); abort()
+
+#endif
+
+#endif  // ART_SIGCHAINLIB_LOG_H_
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 61346b1..2e5f46c 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -14,13 +14,6 @@
  * limitations under the License.
  */
 
-#ifdef ART_TARGET_ANDROID
-#include <android/log.h>
-#else
-#include <stdarg.h>
-#include <iostream>
-#endif
-
 #include <dlfcn.h>
 #include <errno.h>
 #include <pthread.h>
@@ -35,6 +28,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "log.h"
 #include "sigchain.h"
 
 #if defined(__APPLE__)
@@ -65,22 +59,7 @@
 //               doesn't have SA_RESTART, and raise the signal to avoid restarting syscalls that are
 //               expected to be interrupted?
 
-static void log(const char* format, ...) {
-  char buf[256];
-  va_list ap;
-  va_start(ap, format);
-  vsnprintf(buf, sizeof(buf), format, ap);
-#ifdef ART_TARGET_ANDROID
-  __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
-#else
-  std::cout << buf << "\n";
-#endif
-  va_end(ap);
-}
-
-#define fatal(...) log(__VA_ARGS__); abort()
-
-#if defined(__BIONIC__) && !defined(__LP64__)
+#if defined(__BIONIC__) && !defined(__LP64__) && !defined(__mips__)
 static int sigismember(const sigset64_t* sigset, int signum) {
   return sigismember64(sigset, signum);
 }
@@ -223,7 +202,9 @@
       SigactionType result;
       result.sa_flags = action_.sa_flags;
       result.sa_handler = action_.sa_handler;
+#if defined(SA_RESTORER)
       result.sa_restorer = action_.sa_restorer;
+#endif
       memcpy(&result.sa_mask, &action_.sa_mask,
              std::min(sizeof(action_.sa_mask), sizeof(result.sa_mask)));
       return result;
@@ -237,7 +218,9 @@
     } else {
       action_.sa_flags = new_action->sa_flags;
       action_.sa_handler = new_action->sa_handler;
+#if defined(SA_RESTORER)
       action_.sa_restorer = new_action->sa_restorer;
+#endif
       sigemptyset(&action_.sa_mask);
       memcpy(&action_.sa_mask, &new_action->sa_mask,
              std::min(sizeof(action_.sa_mask), sizeof(new_action->sa_mask)));
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
index edce965..c274530 100644
--- a/sigchainlib/sigchain_dummy.cc
+++ b/sigchainlib/sigchain_dummy.cc
@@ -17,13 +17,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#ifdef ART_TARGET_ANDROID
-#include <android/log.h>
-#else
-#include <stdarg.h>
-#include <iostream>
-#endif
-
+#include "log.h"
 #include "sigchain.h"
 
 #define ATTRIBUTE_UNUSED __attribute__((__unused__))
@@ -33,19 +27,6 @@
 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
 #pragma GCC diagnostic ignored "-Wmissing-noreturn"
 
-static void log(const char* format, ...) {
-  char buf[256];
-  va_list ap;
-  va_start(ap, format);
-  vsnprintf(buf, sizeof(buf), format, ap);
-#ifdef ART_TARGET_ANDROID
-  __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
-#else
-  std::cout << buf << "\n";
-#endif
-  va_end(ap);
-}
-
 namespace art {
 
 extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED) {
diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc
index 9f338ad..1d1e54f 100644
--- a/sigchainlib/sigchain_test.cc
+++ b/sigchainlib/sigchain_test.cc
@@ -50,7 +50,7 @@
 
 static int RealSigprocmask(int how, const sigset64_t* new_sigset, sigset64_t* old_sigset) {
   // glibc's sigset_t is overly large, so sizeof(*new_sigset) doesn't work.
-  return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, 8);
+  return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, NSIG/8);
 }
 
 class SigchainTest : public ::testing::Test {
diff --git a/test/001-HelloWorld/src/Main.java b/test/001-HelloWorld/src/Main.java
index 1ef6289..401e852 100644
--- a/test/001-HelloWorld/src/Main.java
+++ b/test/001-HelloWorld/src/Main.java
@@ -14,8 +14,37 @@
  * limitations under the License.
  */
 
+import java.util.concurrent.*;
+
+interface I {
+}
+
+class A implements I {
+    static int x = (int)(10*Math.random());  // Suppress initialization.
+}
+
 public class Main {
-  public static void main(String[] args) {
-    System.out.println("Hello, world!");
+  public static void main(String[] args) throws Exception {
+
+      final CountDownLatch first = new CountDownLatch(1);
+      final CountDownLatch second = new CountDownLatch(1);
+
+      new Thread(new Runnable() {
+          public void run() {
+              try {
+                  synchronized(I.class) {
+                      first.countDown();
+                      second.await();
+                  }
+              } catch (Exception e) {
+                  e.printStackTrace();
+              }
+          }
+      }).start();
+
+      first.await();
+      new A();
+      second.countDown();
+      System.out.println("Hello, world!");
   }
 }
diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc
index 67118d5..49fe369 100644
--- a/test/004-SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -102,10 +102,14 @@
 bool compare_sigaction(const struct sigaction* lhs, const struct sigaction* rhs) {
   // bionic's definition of `struct sigaction` has internal padding bytes, so we can't just do a
   // naive memcmp of the entire struct.
+#if defined(SA_RESTORER)
+  if (lhs->sa_restorer != rhs->sa_restorer) {
+    return false;
+  }
+#endif
   return memcmp(&lhs->sa_mask, &rhs->sa_mask, sizeof(lhs->sa_mask)) == 0 &&
          lhs->sa_sigaction == rhs->sa_sigaction &&
-         lhs->sa_flags == rhs->sa_flags &&
-         lhs->sa_restorer == rhs->sa_restorer;
+         lhs->sa_flags == rhs->sa_flags;
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) {
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 3b35209..a74f763 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -229,7 +229,9 @@
   struct sigaction64 tmp2;
   sigemptyset64(&tmp2.sa_mask);
   tmp2.sa_sigaction = test_sigaction_handler;
+#if defined(SA_RESTORER)
   tmp2.sa_restorer = nullptr;
+#endif
 
   sigaction64(SIGSEGV, &tmp2, nullptr);
   sigaction64(SIGILL, &tmp2, nullptr);
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 83234f0..b91d983 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -31,11 +31,11 @@
 
 #include "base/file_utils.h"
 #include "base/macros.h"
+#include "base/utils.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
 #include "oat_file.h"
 #include "runtime.h"
-#include "utils.h"
 
 namespace art {
 
diff --git a/test/170-interface-init/expected.txt b/test/170-interface-init/expected.txt
new file mode 100644
index 0000000..619c561
--- /dev/null
+++ b/test/170-interface-init/expected.txt
@@ -0,0 +1 @@
+Done.
diff --git a/test/170-interface-init/info.txt b/test/170-interface-init/info.txt
new file mode 100644
index 0000000..28401ad
--- /dev/null
+++ b/test/170-interface-init/info.txt
@@ -0,0 +1,2 @@
+Check that holding a lock on an interface class does not block initialization of an
+implementing class.
diff --git a/test/170-interface-init/src/Main.java b/test/170-interface-init/src/Main.java
new file mode 100644
index 0000000..62f6c5c
--- /dev/null
+++ b/test/170-interface-init/src/Main.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+interface I {
+}
+
+class A implements I {
+    static int x = (int)(10*Math.random());  // Suppress compile-time initialization.
+}
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+        final CountDownLatch first = new CountDownLatch(1);
+        final CountDownLatch second = new CountDownLatch(1);
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    synchronized(I.class) {
+                        first.countDown();
+                        second.await();
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }).start();
+
+        first.await();
+        new A();  // Will initialize A.
+        second.countDown();
+
+        System.out.println("Done.");
+    }
+}
diff --git a/test/1934-jvmti-signal-thread/src/art/Test1934.java b/test/1934-jvmti-signal-thread/src/art/Test1934.java
index 2a3f8db..308f17b 100644
--- a/test/1934-jvmti-signal-thread/src/art/Test1934.java
+++ b/test/1934-jvmti-signal-thread/src/art/Test1934.java
@@ -25,6 +25,8 @@
   public static final boolean PRINT_STACK_TRACE = false;
 
   public static void run() throws Exception {
+    ensureClassesLoaded();
+
     System.out.println("Interrupt before start");
     testInterruptBeforeStart();
 
@@ -53,6 +55,22 @@
     testStopInNative();
   }
 
+  private static void ensureInitialized(Class c) {
+    try {
+      Class.forName(c.getName());
+    } catch (Exception e) {
+      throw new Error("Failed to initialize " + c, e);
+    }
+  }
+
+  private static void ensureClassesLoaded() {
+    // Depending on timing this class might (or might not) be loaded during testing of Stop. If it
+    // is and the StopThread occurs inside of it we will get a ExceptionInInitializerError which is
+    // not what we are looking for. In order to avoid this ever happening simply initialize the
+    // class that can cause it early.
+    ensureInitialized(java.util.concurrent.locks.LockSupport.class);
+  }
+
   public static void testStopBeforeStart() throws Exception {
     final Throwable[] out_err = new Throwable[] { null, };
     final Object tst = new Object();
diff --git a/test/1949-short-dex-file/expected.txt b/test/1949-short-dex-file/expected.txt
new file mode 100644
index 0000000..863339f
--- /dev/null
+++ b/test/1949-short-dex-file/expected.txt
@@ -0,0 +1 @@
+Passed
diff --git a/test/1949-short-dex-file/info.txt b/test/1949-short-dex-file/info.txt
new file mode 100644
index 0000000..e924086
--- /dev/null
+++ b/test/1949-short-dex-file/info.txt
@@ -0,0 +1,30 @@
+Tests the fix for b/74116990
+
+The JIT was reading into incorrect dex files during class redefinition if a
+native method was present.
+
+The transformed dex file is specifically crafted to have exactly 4 methodIDs in
+it. They are (in order):
+  (0) Ljava/lang/Object;-><init>()V
+  (1) Lxyz/Transform;-><init>()V
+  (2) Lxyz/Transform;->bar()V
+  (3) Lxyz/Transform;->foo()V
+
+In the transformed version of the dex file there is a new method. The new list of methodIDs is:
+  (0) Lart/Test1949;->doNothing()V
+  (1) Ljava/lang/Object;-><init>()V
+  (2) Lxyz/Transform;-><init>()V
+  (3) Lxyz/Transform;->bar()V
+  (4) Lxyz/Transform;->foo()V
+
+This test tries to get the JIT to read out-of-bounds on the initial dex file by getting it to
+read the 5th method id of the new file (Lxyz/Transform;->foo()V) from the old dex file (which
+only has 4 method ids).
+
+To do this we need to make sure that the class being transformed is near the end of the
+alphabet (package xyz, method foo). If it is further forward than the other method-ids then the
+JIT will read an incorrect (but valid) method-id from the old-dex file. This is why the error
+wasn't caught in our other tests (package art is always at the front).
+
+The final method that causes the OOB read needs to be a native method because that is the only
+method-type the jit uses dex-file information to keep track of.
diff --git a/test/1949-short-dex-file/run b/test/1949-short-dex-file/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1949-short-dex-file/run
@@ -0,0 +1,17 @@
+#!/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.
+
+./default-run "$@" --jvmti
diff --git a/runtime/base/safe_copy.h b/test/1949-short-dex-file/src/Main.java
similarity index 60%
copy from runtime/base/safe_copy.h
copy to test/1949-short-dex-file/src/Main.java
index d0f497c..dbbaa86 100644
--- a/runtime/base/safe_copy.h
+++ b/test/1949-short-dex-file/src/Main.java
@@ -14,18 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_SAFE_COPY_H_
-#define ART_RUNTIME_BASE_SAFE_COPY_H_
-
-#include <sys/types.h>
-
-namespace art {
-
-// Safely dereference a pointer.
-// Returns -1 if safe copy isn't implemented on the platform, or if the transfer is too large.
-// Returns 0 if src is unreadable.
-ssize_t SafeCopy(void *dst, const void *src, size_t len);
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_BASE_SAFE_COPY_H_
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1949.run();
+  }
+}
diff --git a/test/1949-short-dex-file/src/art/Redefinition.java b/test/1949-short-dex-file/src/art/Redefinition.java
new file mode 100644
index 0000000..56d2938
--- /dev/null
+++ b/test/1949-short-dex-file/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/1949-short-dex-file/src/art/Test1949.java b/test/1949-short-dex-file/src/art/Test1949.java
new file mode 100644
index 0000000..98fa7fc
--- /dev/null
+++ b/test/1949-short-dex-file/src/art/Test1949.java
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.*;
+import java.util.Base64;
+import java.nio.ByteBuffer;
+
+public class Test1949 {
+  private final static boolean isDalvik = System.getProperty("java.vm.name").equals("Dalvik");
+
+  // This dex file is specifically crafted to have exactly 4 methodIDs in it. They are (in order):
+  //   (0) Ljava/lang/Object;-><init>()V
+  //   (1) Lxyz/Transform;-><init>()V
+  //   (2) Lxyz/Transform;->bar()V
+  //   (3) Lxyz/Transform;->foo()V
+  //
+  // In the transformed version of the dex file there is a new method. The new list of methodIDs is:
+  //   (0) Lart/Test1949;->doNothing()V
+  //   (1) Ljava/lang/Object;-><init>()V
+  //   (2) Lxyz/Transform;-><init>()V
+  //   (3) Lxyz/Transform;->bar()V
+  //   (4) Lxyz/Transform;->foo()V
+  //
+  // This test tries to get the JIT to read out-of-bounds on the initial dex file by getting it to
+  // read the 5th method id of the new file (Lxyz/Transform;->foo()V) from the old dex file (which
+  // only has 4 method ids).
+  //
+  // To do this we need to make sure that the class being transformed is near the end of the
+  // alphabet (package xyz, method foo). If it is further forward than the other method-ids then the
+  // JIT will read an incorrect (but valid) method-id from the old-dex file. This is why the error
+  // wasn't caught in our other tests (package art is always at the front).
+  //
+  // The final method that causes the OOB read needs to be a native method because that is the only
+  // method-type the jit uses dex-file information to keep track of.
+
+  /**
+   * base64 encoded class/dex file for
+   * package xyz;
+   * public class Transform {
+   *   public native void foo();
+   *   public void bar() {}
+   * }
+   */
+  private static final byte[] CLASS_BYTES_INIT = Base64.getDecoder().decode(
+    "yv66vgAAADUADwoAAwAMBwANBwAOAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJU" +
+    "YWJsZQEAA2ZvbwEAA2JhcgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwABAAFAQANeHl6" +
+    "L1RyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QAIQACAAMAAAAAAAMAAQAEAAUAAQAGAAAAHQAB" +
+    "AAEAAAAFKrcAAbEAAAABAAcAAAAGAAEAAAACAQEACAAFAAAAAQAJAAUAAQAGAAAAGQAAAAEAAAAB" +
+    "sQAAAAEABwAAAAYAAQAAAAQAAQAKAAAAAgAL");
+  private static final byte[] DEX_BYTES_INIT = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBDUutFJpeT+okk+aXah8NQ61q2XRtkmChwAgAAcAAAAHhWNBIAAAAAAAAAANwBAAAI" +
+    "AAAAcAAAAAMAAACQAAAAAQAAAJwAAAAAAAAAAAAAAAQAAACoAAAAAQAAAMgAAACIAQAA6AAAABwB" +
+    "AAAkAQAAOAEAAEkBAABZAQAAXAEAAGEBAABmAQAAAQAAAAIAAAAEAAAABAAAAAIAAAAAAAAAAAAA" +
+    "AAAAAAABAAAAAAAAAAEAAAAFAAAAAQAAAAYAAAABAAAAAQAAAAAAAAAAAAAAAwAAAAAAAADDAQAA" +
+    "AAAAAAEAAQABAAAAEgEAAAQAAABwEAAAAAAOAAEAAQAAAAAAFgEAAAEAAAAOAAIADgAEAA4AAAAG" +
+    "PGluaXQ+ABJMamF2YS9sYW5nL09iamVjdDsAD0x4eXovVHJhbnNmb3JtOwAOVHJhbnNmb3JtLmph" +
+    "dmEAAVYAA2JhcgADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVj" +
+    "Y2FiMjMwMzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQIB" +
+    "gYAE6AECAYACAYECAAAAAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAMAAACQ" +
+    "AAAAAwAAAAEAAACcAAAABQAAAAQAAACoAAAABgAAAAEAAADIAAAAASAAAAIAAADoAAAAAyAAAAIA" +
+    "AAASAQAAAiAAAAgAAAAcAQAAACAAAAEAAADDAQAAAxAAAAEAAADYAQAAABAAAAEAAADcAQAA");
+
+  /**
+   * base64 encoded class/dex file for
+   * package xyz;
+   * public class Transform {
+   *   public native void foo();
+   *   public void bar() {
+   *     // Make sure the methodID is before any of the ones in Transform
+   *     art.Test1949.doNothing();
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES_FINAL = Base64.getDecoder().decode(
+    "yv66vgAAADUAFAoABAANCgAOAA8HABAHABEBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51" +
+    "bWJlclRhYmxlAQADZm9vAQADYmFyAQAKU291cmNlRmlsZQEADlRyYW5zZm9ybS5qYXZhDAAFAAYH" +
+    "ABIMABMABgEADXh5ei9UcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAMYXJ0L1Rlc3QxOTQ5" +
+    "AQAJZG9Ob3RoaW5nACEAAwAEAAAAAAADAAEABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAAAQAI" +
+    "AAAABgABAAAAAgEBAAkABgAAAAEACgAGAAEABwAAABwAAAABAAAABLgAArEAAAABAAgAAAAGAAEA" +
+    "AAAEAAEACwAAAAIADA==");
+  private static final byte[] DEX_BYTES_FINAL = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBHXBiw7Hso1vnmaXE1VCV41f4+0aECixOgAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAK" +
+    "AAAAcAAAAAQAAACYAAAAAQAAAKgAAAAAAAAAAAAAAAUAAAC0AAAAAQAAANwAAACkAQAA/AAAADQB" +
+    "AAA8AQAATAEAAGABAABxAQAAgQEAAIQBAACJAQAAlAEAAJkBAAABAAAAAgAAAAMAAAAFAAAABQAA" +
+    "AAMAAAAAAAAAAAAAAAcAAAABAAAAAAAAAAIAAAAAAAAAAgAAAAYAAAACAAAACAAAAAIAAAABAAAA" +
+    "AQAAAAAAAAAEAAAAAAAAAPYBAAAAAAAAAQABAAEAAAAsAQAABAAAAHAQAQAAAA4AAQABAAAAAAAw" +
+    "AQAABAAAAHEAAAAAAA4AAgAOAAQADgAGPGluaXQ+AA5MYXJ0L1Rlc3QxOTQ5OwASTGphdmEvbGFu" +
+    "Zy9PYmplY3Q7AA9MeHl6L1RyYW5zZm9ybTsADlRyYW5zZm9ybS5qYXZhAAFWAANiYXIACWRvTm90" +
+    "aGluZwADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVjY2FiMjMw" +
+    "MzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQICgYAE/AED" +
+    "AZQCAYECAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAoAAABwAAAAAgAAAAQAAACYAAAAAwAAAAEA" +
+    "AACoAAAABQAAAAUAAAC0AAAABgAAAAEAAADcAAAAASAAAAIAAAD8AAAAAyAAAAIAAAAsAQAAAiAA" +
+    "AAoAAAA0AQAAACAAAAEAAAD2AQAAAxAAAAEAAAAIAgAAABAAAAEAAAAMAgAA");
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest();
+  }
+
+  // A method with a methodID before anything in Transform.
+  public static void doNothing() {}
+
+  private static ClassLoader CreateClassLoader(byte[] clz, byte[] dex) throws Exception {
+    if (isDalvik) {
+      Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+      Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+      /* on Dalvik, this is a DexFile; otherwise, it's null */
+      return (ClassLoader)ctor.newInstance(ByteBuffer.wrap(dex), Test1949.class.getClassLoader());
+    } else {
+      return new ClassLoader() {
+        public Class<?> findClass(String name) throws ClassNotFoundException {
+          if (name.equals("xyz.Transform")) {
+            return defineClass(name, clz, 0, clz.length);
+          } else {
+            throw new ClassNotFoundException("Couldn't find class: " + name);
+          }
+        }
+      };
+    }
+  }
+
+  public static void doTest() throws Exception {
+    Class c = CreateClassLoader(CLASS_BYTES_INIT, DEX_BYTES_INIT).loadClass("xyz.Transform");
+    Redefinition.doCommonClassRedefinition(c, CLASS_BYTES_FINAL, DEX_BYTES_FINAL);
+    System.out.println("Passed");
+  }
+}
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
index bd5d9e2..517aacd 100644
--- a/test/445-checker-licm/src/Main.java
+++ b/test/445-checker-licm/src/Main.java
@@ -153,7 +153,6 @@
     return result;
   }
 
-
   /// CHECK-START: int Main.invariantBoundIntrinsic(int) instruction_simplifier (before)
   /// CHECK-DAG: InvokeStaticOrDirect loop:{{B\d+}}
   //
@@ -176,14 +175,17 @@
     return result;
   }
 
-  /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (before)
+  /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) instruction_simplifier (before)
   /// CHECK-DAG: InvokeStaticOrDirect loop:{{B\d+}}
 
-  /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (after)
-  /// CHECK-NOT: InvokeStaticOrDirect loop:{{B\d+}}
+  /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (before)
+  /// CHECK-DAG: Max loop:{{B\d+}}
 
   /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (after)
-  /// CHECK-DAG: InvokeStaticOrDirect loop:none
+  /// CHECK-NOT: Max loop:{{B\d+}}
+
+  /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (after)
+  /// CHECK-DAG: Max loop:none
 
   public static int invariantBodyIntrinsic(int x, int y) {
     int result = 0;
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 3173afd..17707e1 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -141,7 +141,7 @@
 
   /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$getBootImageString() builder (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadString load_kind:{{BootImageAddress|BootImageInternTable}}
+  /// CHECK:                LoadString load_kind:{{BootImageAddress|BootImageRelRo}}
 
   public static String $noinline$getBootImageString() {
     // Prevent inlining to avoid the string comparison being optimized away.
@@ -169,7 +169,7 @@
 
   /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.Class Main.$noinline$getStringClass() builder (after)
   // Note: load kind depends on PIC/non-PIC
-  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|BootImageRelRo}} class_name:java.lang.String
 
   public static Class<?> $noinline$getStringClass() {
     // Prevent inlining to avoid the string comparison being optimized away.
@@ -195,6 +195,32 @@
     return Other.class;
   }
 
+  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexString(int) builder (after)
+  /// CHECK:                InvokeStaticOrDirect method_load_kind:RuntimeCall
+
+  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexString(int) sharpening (after)
+  // Note: load kind depends on PIC/non-PIC
+  /// CHECK:                InvokeStaticOrDirect method_load_kind:{{BootImageRelRo|DirectAddress}}
+  public static String $noinline$toHexString(int value) {
+    return Integer.toString(value, 16);
+  }
+
+  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexStringIndirect(int) builder (after)
+  /// CHECK:                InvokeStaticOrDirect method_load_kind:RuntimeCall
+
+  /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexStringIndirect(int) sharpening (after)
+  /// CHECK:                InvokeStaticOrDirect method_load_kind:BssEntry
+
+  /// CHECK-START-X86: java.lang.String Main.$noinline$toHexStringIndirect(int) pc_relative_fixups_x86 (before)
+  /// CHECK-NOT:            X86ComputeBaseMethodAddress
+
+  /// CHECK-START-X86: java.lang.String Main.$noinline$toHexStringIndirect(int) pc_relative_fixups_x86 (after)
+  /// CHECK-DAG:            X86ComputeBaseMethodAddress
+  /// CHECK-DAG:            InvokeStaticOrDirect method_load_kind:BssEntry
+  public static String $noinline$toHexStringIndirect(int value) {
+    return $noinline$toHexString(value);
+  }
+
   public static void main(String[] args) {
     assertIntEquals(1, testSimple(1));
     assertIntEquals(1, testDiamond(false, 1));
@@ -208,6 +234,8 @@
     assertStringEquals("non-boot-image-string", $noinline$getNonBootImageString());
     assertClassEquals(String.class, $noinline$getStringClass());
     assertClassEquals(Other.class, $noinline$getOtherClass());
+    assertStringEquals("12345678", $noinline$toHexString(0x12345678));
+    assertStringEquals("76543210", $noinline$toHexStringIndirect(0x76543210));
   }
 }
 
diff --git a/test/562-checker-no-intermediate/src/Main.java b/test/562-checker-no-intermediate/src/Main.java
index 104ba8b..2b19918 100644
--- a/test/562-checker-no-intermediate/src/Main.java
+++ b/test/562-checker-no-intermediate/src/Main.java
@@ -18,7 +18,7 @@
 
   /**
    * Check that the intermediate address computation is not reordered or merged
-   * across the call to Math.abs().
+   * across a method call.
    */
 
   /// CHECK-START-ARM: void Main.main(java.lang.String[]) instruction_simplifier_arm (before)
@@ -26,7 +26,7 @@
   /// CHECK-DAG:           <<Array:l\d+>>         NullCheck
   /// CHECK-DAG:           <<Index:i\d+>>         BoundsCheck
   /// CHECK-DAG:           <<ArrayGet:i\d+>>      ArrayGet [<<Array>>,<<Index>>]
-  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs
   /// CHECK-DAG:           <<Add:i\d+>>           Add [<<ArrayGet>>,<<AbsM42>>]
   /// CHECK-DAG:                                  ArraySet [<<Array>>,<<Index>>,<<Add>>]
 
@@ -37,7 +37,7 @@
   /// CHECK-DAG:           <<Index:i\d+>>         BoundsCheck
   /// CHECK-DAG:           <<Address1:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:           <<ArrayGet:i\d+>>      ArrayGet [<<Address1>>,<<Index>>]
-  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs
   /// CHECK-DAG:           <<Add:i\d+>>           Add [<<ArrayGet>>,<<AbsM42>>]
   /// CHECK-DAG:           <<Address2:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:                                  ArraySet [<<Address2>>,<<Index>>,<<Add>>]
@@ -49,7 +49,7 @@
   /// CHECK-DAG:           <<Index:i\d+>>         BoundsCheck
   /// CHECK-DAG:           <<Address1:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:           <<ArrayGet:i\d+>>      ArrayGet [<<Address1>>,<<Index>>]
-  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs
   /// CHECK-DAG:           <<Add:i\d+>>           Add [<<ArrayGet>>,<<AbsM42>>]
   /// CHECK-DAG:           <<Address2:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:                                  ArraySet [<<Address2>>,<<Index>>,<<Add>>]
@@ -60,7 +60,7 @@
   /// CHECK-DAG:           <<Array:l\d+>>         NullCheck
   /// CHECK-DAG:           <<Index:i\d+>>         BoundsCheck
   /// CHECK-DAG:           <<ArrayGet:i\d+>>      ArrayGet [<<Array>>,<<Index>>]
-  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs
   /// CHECK-DAG:           <<Add:i\d+>>           Add [<<ArrayGet>>,<<AbsM42>>]
   /// CHECK-DAG:                                  ArraySet [<<Array>>,<<Index>>,<<Add>>]
 
@@ -71,7 +71,7 @@
   /// CHECK-DAG:           <<Index:i\d+>>         BoundsCheck
   /// CHECK-DAG:           <<Address1:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:           <<ArrayGet:i\d+>>      ArrayGet [<<Address1>>,<<Index>>]
-  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs
   /// CHECK-DAG:           <<Add:i\d+>>           Add [<<ArrayGet>>,<<AbsM42>>]
   /// CHECK-DAG:           <<Address2:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:                                  ArraySet [<<Address2>>,<<Index>>,<<Add>>]
@@ -83,13 +83,17 @@
   /// CHECK-DAG:           <<Index:i\d+>>         BoundsCheck
   /// CHECK-DAG:           <<Address1:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:           <<ArrayGet:i\d+>>      ArrayGet [<<Address1>>,<<Index>>]
-  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>] intrinsic:MathAbsInt
+  /// CHECK-DAG:           <<AbsM42:i\d+>>        InvokeStaticOrDirect [<<ConstM42>>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs
   /// CHECK-DAG:           <<Add:i\d+>>           Add [<<ArrayGet>>,<<AbsM42>>]
   /// CHECK-DAG:           <<Address2:i\d+>>      IntermediateAddress [<<Array>>,<<DataOffset>>]
   /// CHECK-DAG:                                  ArraySet [<<Address2>>,<<Index>>,<<Add>>]
 
   public static void main(String[] args) {
-    array[index] += Math.abs(-42);
+    array[index] += $noinline$abs(-42);
+  }
+
+  public static int $noinline$abs(int value) {
+    return Math.abs(value);
   }
 
   static int index = 0;
diff --git a/test/631-checker-fp-abs/src/Main.java b/test/631-checker-fp-abs/src/Main.java
index 0f85dc6..2db93b8 100644
--- a/test/631-checker-fp-abs/src/Main.java
+++ b/test/631-checker-fp-abs/src/Main.java
@@ -23,6 +23,9 @@
  */
 public class Main {
 
+  private final static boolean isDalvik =
+      System.getProperty("java.vm.name").equals("Dalvik");
+
   private static final int SPQUIET = 1 << 22;
   private static final long DPQUIET = 1L << 51;
 
@@ -73,13 +76,16 @@
 
     // A few NaN numbers.
     int[] spnans = {
-      0x7f800001,
+      0x7f800001,  // signaling
       0x7fa00000,
-      0x7fc00000,
+      0x7fbfffff,
+      0x7fc00000,  // quiet
+      0x7fc00001,
       0x7fffffff,
-      0xff800001,
+      0xff800001,  // signaling
       0xffa00000,
-      0xffc00000,
+      0xffbfffff,
+      0xffc00000,  // quiet
       0xffffffff
     };
     for (int i = 0; i < spnans.length; i++) {
@@ -142,6 +148,13 @@
   // We allow that an expected NaN result has become quiet.
   private static void expectEqualsNaN32(int expected, int result) {
     if (expected != result && (expected | SPQUIET) != result) {
+      if (!isDalvik) {
+        // If not on ART, relax the expected value more towards just
+        // "spec compliance" and allow sign bit to remain set for NaN.
+        if (expected == (result & Integer.MAX_VALUE)) {
+          return;
+        }
+      }
       throw new Error("Expected: 0x" + Integer.toHexString(expected)
           + ", found: 0x" + Integer.toHexString(result));
     }
@@ -157,6 +170,13 @@
   // We allow that an expected NaN result has become quiet.
   private static void expectEqualsNaN64(long expected, long result) {
     if (expected != result && (expected | DPQUIET) != result) {
+      if (!isDalvik) {
+        // If not on ART, relax the expected value more towards just
+        // "spec compliance" and allow sign bit to remain set for NaN.
+        if (expected == (result & Long.MAX_VALUE)) {
+          return;
+        }
+      }
       throw new Error("Expected: 0x" + Long.toHexString(expected)
           + ", found: 0x" + Long.toHexString(result));
     }
diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java
index 4c69e58..870a403 100644
--- a/test/645-checker-abs-simd/src/Main.java
+++ b/test/645-checker-abs-simd/src/Main.java
@@ -19,6 +19,9 @@
  */
 public class Main {
 
+  private final static boolean isDalvik =
+      System.getProperty("java.vm.name").equals("Dalvik");
+
   private static final int SPQUIET = 1 << 22;
   private static final long DPQUIET = 1L << 51;
 
@@ -378,6 +381,13 @@
   // We allow that an expected NaN result has become quiet.
   private static void expectEqualsNaN32(int expected, int result) {
     if (expected != result && (expected | SPQUIET) != result) {
+      if (!isDalvik) {
+        // If not on ART, relax the expected value more towards just
+        // "spec compliance" and allow sign bit to remain set for NaN.
+        if (expected == (result & Integer.MAX_VALUE)) {
+          return;
+        }
+      }
       throw new Error("Expected: 0x" + Integer.toHexString(expected)
           + ", found: 0x" + Integer.toHexString(result));
     }
@@ -386,6 +396,13 @@
   // We allow that an expected NaN result has become quiet.
   private static void expectEqualsNaN64(long expected, long result) {
     if (expected != result && (expected | DPQUIET) != result) {
+      if (!isDalvik) {
+        // If not on ART, relax the expected value more towards just
+        // "spec compliance" and allow sign bit to remain set for NaN.
+        if (expected == (result & Long.MAX_VALUE)) {
+          return;
+        }
+      }
       throw new Error("Expected: 0x" + Long.toHexString(expected)
           + ", found: 0x" + Long.toHexString(result));
     }
diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-byte-simd-minmax/src/Main.java
index 45949ae..4e667bb 100644
--- a/test/651-checker-byte-simd-minmax/src/Main.java
+++ b/test/651-checker-byte-simd-minmax/src/Main.java
@@ -23,7 +23,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -54,7 +54,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -74,7 +74,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -105,7 +105,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -125,7 +125,7 @@
   /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get:b\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get>>,<<I100>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get>>,<<I100>>]              loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-char-simd-minmax/src/Main.java
index 9b05609..520e10b 100644
--- a/test/651-checker-char-simd-minmax/src/Main.java
+++ b/test/651-checker-char-simd-minmax/src/Main.java
@@ -23,7 +23,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -43,7 +43,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -63,7 +63,7 @@
   /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get:c\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get>>,<<I100>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get>>,<<I100>>]              loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
diff --git a/test/651-checker-double-simd-minmax/src/Main.java b/test/651-checker-double-simd-minmax/src/Main.java
index 6b12e7e..2eaf907 100644
--- a/test/651-checker-double-simd-minmax/src/Main.java
+++ b/test/651-checker-double-simd-minmax/src/Main.java
@@ -23,7 +23,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:d\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinDoubleDouble loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
   //
   // TODO x86: 0.0 vs -0.0?
@@ -45,7 +45,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:d\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxDoubleDouble loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
   //
   // TODO x86: 0.0 vs -0.0?
diff --git a/test/651-checker-float-simd-minmax/src/Main.java b/test/651-checker-float-simd-minmax/src/Main.java
index 278a9c9..dc09dfc 100644
--- a/test/651-checker-float-simd-minmax/src/Main.java
+++ b/test/651-checker-float-simd-minmax/src/Main.java
@@ -23,7 +23,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:f\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinFloatFloat loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:f\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
   //
   // TODO x86: 0.0 vs -0.0?
@@ -45,7 +45,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:f\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxFloatFloat loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:f\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
   //
   // TODO x86: 0.0 vs -0.0?
diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-int-simd-minmax/src/Main.java
index cfa0ae7..82fad84 100644
--- a/test/651-checker-int-simd-minmax/src/Main.java
+++ b/test/651-checker-int-simd-minmax/src/Main.java
@@ -23,7 +23,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after)
@@ -42,7 +42,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after)
diff --git a/test/651-checker-long-simd-minmax/src/Main.java b/test/651-checker-long-simd-minmax/src/Main.java
index 458cb8b..f52686e 100644
--- a/test/651-checker-long-simd-minmax/src/Main.java
+++ b/test/651-checker-long-simd-minmax/src/Main.java
@@ -23,7 +23,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:j\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinLongLong loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:j\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none
   //
   // Not directly supported for longs.
@@ -48,7 +48,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:j\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxLongLong loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:j\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none
   //
   // Not directly supported for longs.
diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-short-simd-minmax/src/Main.java
index 5f10ada..4300ca2 100644
--- a/test/651-checker-short-simd-minmax/src/Main.java
+++ b/test/651-checker-short-simd-minmax/src/Main.java
@@ -23,7 +23,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -54,7 +54,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -74,7 +74,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -105,7 +105,7 @@
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Max:i\d+>>  InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
@@ -125,7 +125,7 @@
   /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none
   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Get:s\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: <<Min:i\d+>>  InvokeStaticOrDirect [<<Get>>,<<I100>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get>>,<<I100>>]              loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java
index 3a0a049..fcd50a6 100644
--- a/test/661-checker-simd-reduc/src/Main.java
+++ b/test/661-checker-simd-reduc/src/Main.java
@@ -378,7 +378,7 @@
   /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<ConsM>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 InvokeStaticOrDirect [<<Phi2>>,<<Get>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                 Min [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                 Return [<<Phi2>>]             loop:none
   //
@@ -438,7 +438,7 @@
   /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<ConsM>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG:                 InvokeStaticOrDirect [<<Phi2>>,<<Get>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                 Max [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                 Return [<<Phi2>>]             loop:none
   //
diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt
index 767cc7e..7e8ffa6 100644
--- a/test/952-invoke-custom/expected.txt
+++ b/test/952-invoke-custom/expected.txt
@@ -18,3 +18,67 @@
 testInstanceFieldAccessors
 testInvokeVirtual => max(77, -3) = 77
 testConstructor => class TestInvocationKinds$Widget
+TestDynamicArguments
+bsm
+0, One, 3.141592653589793
+bsm
+1, Two, 2.718281828459045
+bsm
+2, Three, 0.0
+0, One, 3.141592653589793
+1, Two, 2.718281828459045
+2, Three, 0.0
+TestBadBootstrapArguments
+bsm(class TestBadBootstrapArguments, happy, ()void, -1, very)
+happy
+invokeWrongParameterTypes => class java.lang.NoSuchMethodError
+invokeMissingParameterTypes => class java.lang.NoSuchMethodError
+invokeExtraArguments => class java.lang.BootstrapMethodError => class java.lang.invoke.WrongMethodTypeException
+invokeWrongArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+invokeWrongArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+invokeWrongArgumentsAgain => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+invokeNarrowArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+bsmDJ(..., 1.7976931348623157E308, 2147483647)
+wideningArguments
+bsmDoubleLong(..., 1.7976931348623157E308, 9223372036854775807)
+boxingArguments
+invokeWideningBoxingArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+bsm returning void value.
+invokeVoidReturnType() => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+bsm returning Object value.
+invokeObjectReturnType() => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+bsm returning Integer value.
+invokeIntegerReturnType() => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+Hello!
+bsmWithStringArray(TestVariableArityLinkerMethod, methodA, ()void, [Aachen, Aalborg, Aalto]);
+methodA
+bsmWithStringArray(TestVariableArityLinkerMethod, methodB, ()void, [barium]);
+methodB
+bsmWithStringArray(TestVariableArityLinkerMethod, methodC, ()void, []);
+methodC
+methodA
+methodB
+methodC
+bsmWithIntAndStringArray(TestVariableArityLinkerMethod, methodD, ()void, 101, [zoo, zoogene, zoogenic]);
+methodD
+bsmWithIntAndStringArray(TestVariableArityLinkerMethod, methodE, ()void, 102, [zonic]);
+methodE
+bsmWithIntAndStringArray(TestVariableArityLinkerMethod, methodF, ()void, 103, []);
+methodF
+methodD
+methodE
+methodF
+bsmWithLongAndIntArray(TestVariableArityLinkerMethod, methodG, ()void, 81985529216486895, [1, -1, 2, -2]);
+methodG
+bsmWithFloatAndLongArray(TestVariableArityLinkerMethod, methodH, ()void, -2.7182817, [999999999999, -8888888888888]);
+methodH
+bsmWithClassAndFloatArray(TestVariableArityLinkerMethod, methodI, ()void, class java.lang.Throwable, [3.4028235E38, 1.4E-45, 3.1415927, -3.1415927]);
+methodI
+bsmWithDoubleArray(TestVariableArityLinkerMethod, methodJ, ()void, [1.7976931348623157E308, 4.9E-324, 2.718281828459045, -3.141592653589793]);
+methodJ
+bsmWithClassArray(TestVariableArityLinkerMethod, methodK, ()void, [class java.lang.Integer, class java.lang.invoke.MethodHandles, class java.util.Arrays]);
+methodK
+methodO => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+methodP => class java.lang.BootstrapMethodError => class java.lang.ClassCastException
+methodQ => class java.lang.BootstrapMethodError => class java.lang.invoke.WrongMethodTypeException
+methodR => class java.lang.BootstrapMethodError => class java.lang.invoke.WrongMethodTypeException
diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java
index 0b1c1ff..d2250a9 100644
--- a/test/952-invoke-custom/src/Main.java
+++ b/test/952-invoke-custom/src/Main.java
@@ -74,18 +74,15 @@
                 TestLinkerMethodMinimalArguments.FAILURE_TYPE_NONE, 10, 13);
     }
 
-    private static void TestInvokeCustomWithConcurrentThreads() throws Throwable {
-        // This is a concurrency test that attempts to run invoke-custom on the same
-        // call site.
-        TestInvokeCustomWithConcurrentThreads.test();
-    }
-
     public static void main(String[] args) throws Throwable {
         TestUninitializedCallSite();
         TestLinkerMethodMinimalArguments();
         TestLinkerMethodMultipleArgumentTypes();
         TestLinkerUnrelatedBSM.test();
-        TestInvokeCustomWithConcurrentThreads();
+        TestInvokeCustomWithConcurrentThreads.test();
         TestInvocationKinds.test();
+        TestDynamicBootstrapArguments.test();
+        TestBadBootstrapArguments.test();
+        TestVariableArityLinkerMethod.test();
     }
 }
diff --git a/test/952-invoke-custom/src/TestBadBootstrapArguments.java b/test/952-invoke-custom/src/TestBadBootstrapArguments.java
new file mode 100644
index 0000000..25d8b59
--- /dev/null
+++ b/test/952-invoke-custom/src/TestBadBootstrapArguments.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import annotations.BootstrapMethod;
+import annotations.CalledByIndy;
+import annotations.Constant;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+
+public class TestBadBootstrapArguments extends TestBase {
+    private static CallSite bsm(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            int extraInt,
+            String extraString)
+            throws Throwable {
+        System.out.print("bsm(");
+        System.out.print(lookup.lookupClass());
+        System.out.print(", ");
+        System.out.print(methodName);
+        System.out.print(", ");
+        System.out.print(methodType);
+        System.out.print(", ");
+        System.out.print(extraInt);
+        System.out.print(", ");
+        System.out.print(extraString);
+        System.out.println(")");
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsm",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String.class
+                    }
+                ),
+        fieldOrMethodName = "happy",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(intValue = -1),
+            @Constant(stringValue = "very")
+        }
+    )
+    private static void invokeHappy() {
+        assertNotReached();
+    }
+
+    private static void happy() {
+        System.out.println("happy");
+    }
+
+    // BootstrapMethod.parameterTypes != parameterTypesOf(constantArgumentsForBootstrapMethod)
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsm",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        double.class
+                    }
+                ),
+        fieldOrMethodName = "wrongParameterTypes",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(intValue = -1),
+            @Constant(stringValue = "very")
+        }
+    )
+    private static void invokeWrongParameterTypes() throws NoSuchMethodError {
+        assertNotReached();
+    }
+
+    private static void wrongParameterTypes() {
+        System.out.println("wrongParameterTypes");
+    }
+
+    // BootstrapMethod.parameterTypes != parameterTypesOf(constantArgumentsForBootstrapMethod)
+    // (missing constantArgumentTypes))
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsm",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        double.class
+                    }
+                ),
+        fieldOrMethodName = "missingParameterTypes",
+        constantArgumentsForBootstrapMethod = {}
+    )
+    private static void invokeMissingParameterTypes() throws NoSuchMethodError {
+        assertNotReached();
+    }
+
+    private static void missingParameterTypes() {
+        System.out.println("missingParameterTypes");
+    }
+
+    // BootstrapMethod.parameterTypes != parameterTypesOf(constantArgumentsForBootstrapMethod):
+    // extra constant present
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsm",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String.class
+                    }
+                ),
+        fieldOrMethodName = "extraArguments",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(intValue = 1),
+            @Constant(stringValue = "2"),
+            @Constant(intValue = 3)
+        }
+    )
+    private static void invokeExtraArguments() {
+        assertNotReached();
+    }
+
+    private static void extraArguments() {
+        System.out.println("extraArguments");
+    }
+
+    // constantArgumentTypes do not correspond to expected parameter types
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsm",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String.class
+                    }
+                ),
+        fieldOrMethodName = "wrongArguments",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(stringValue = "1"),
+            @Constant(doubleValue = Math.PI)
+        }
+    )
+    private static void invokeWrongArguments() {
+        assertNotReached();
+    }
+
+    private static void wrongArguments() {
+        System.out.println("wrongArguments");
+    }
+
+    // constantArgumentTypes do not correspond to expected parameter types
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsm",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String.class
+                    }
+                ),
+        fieldOrMethodName = "wrongArgumentsAgain",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(doubleValue = Math.PI),
+            @Constant(stringValue = "pie")
+        }
+    )
+    private static void invokeWrongArgumentsAgain() {
+        assertNotReached();
+    }
+
+    private static void wrongArgumentsAgain() {
+        System.out.println("wrongArgumentsAgain");
+    }
+
+    // Primitive argument types not supported {Z, B, C, S}.
+    private static CallSite bsmZBCS(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            boolean extraArg0,
+            byte extraArg1,
+            char extraArg2,
+            short extraArg3)
+            throws Throwable {
+        assertNotReached();
+        return null;
+    }
+
+    // Arguments are narrower than supported.
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmZBCS",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        boolean.class,
+                        byte.class,
+                        char.class,
+                        short.class
+                    }
+                ),
+        fieldOrMethodName = "narrowArguments",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(booleanValue = true),
+            @Constant(byteValue = Byte.MAX_VALUE),
+            @Constant(charValue = 'A'),
+            @Constant(shortValue = Short.MIN_VALUE)
+        }
+    )
+    private static void invokeNarrowArguments() {
+        assertNotReached();
+    }
+
+    private static void narrowArguments() {
+        assertNotReached();
+    }
+
+    private static CallSite bsmDJ(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            double extraArg0,
+            long extraArg1)
+            throws Throwable {
+        System.out.print("bsmDJ(..., ");
+        System.out.print(extraArg0);
+        System.out.print(", ");
+        System.out.print(extraArg1);
+        System.out.println(")");
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    // Arguments need widening to parameter types.
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmDJ",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        double.class,
+                        long.class
+                    }
+                ),
+        fieldOrMethodName = "wideningArguments",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(doubleValue = Double.MAX_VALUE),
+            @Constant(intValue = Integer.MAX_VALUE)
+        }
+    )
+    private static void invokeWideningArguments() {
+        assertNotReached();
+    }
+
+    private static void wideningArguments() {
+        System.out.println("wideningArguments");
+    }
+
+    private static CallSite bsmDoubleLong(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            Double extraArg0,
+            Long extraArg1)
+            throws Throwable {
+        System.out.print("bsmDoubleLong(..., ");
+        System.out.print(extraArg0);
+        System.out.print(", ");
+        System.out.print(extraArg1);
+        System.out.println(")");
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    // Arguments need boxing to parameter types
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmDoubleLong",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        Double.class,
+                        Long.class
+                    }
+                ),
+        fieldOrMethodName = "boxingArguments",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(doubleValue = Double.MAX_VALUE),
+            @Constant(longValue = Long.MAX_VALUE)
+        }
+    )
+    private static void invokeBoxingArguments() {
+        assertNotReached();
+    }
+
+    private static void boxingArguments() {
+        System.out.println("boxingArguments");
+    }
+
+    // Arguments need widening and boxing to parameter types
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmDoubleLong",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        Double.class,
+                        Long.class
+                    }
+                ),
+        fieldOrMethodName = "wideningBoxingArguments",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(floatValue = Float.MAX_VALUE),
+            @Constant(longValue = Integer.MAX_VALUE)
+        }
+    )
+    private static void invokeWideningBoxingArguments() {
+        assertNotReached();
+    }
+
+    private static void wideningBoxingArguments() {
+        System.out.println("wideningBoxingArguments");
+    }
+
+    static void bsmReturningVoid(MethodHandles.Lookup lookup, String name, MethodType type) {
+        System.out.println("bsm returning void value.");
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmReturningVoid",
+                    parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+                    returnType = void.class
+                ),
+        fieldOrMethodName = "voidReturnType"
+    )
+    private static void invokeVoidReturnType() {
+        assertNotReached();
+    }
+
+    private static void voidReturnType() {
+        assertNotReached();
+    }
+
+    static Object bsmReturningObject(MethodHandles.Lookup lookup, String name, MethodType type) {
+        System.out.println("bsm returning Object value.");
+        return new Object();
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmReturningObject",
+                    parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+                    returnType = Object.class
+                ),
+        fieldOrMethodName = "ObjectReturnType"
+    )
+    private static void invokeObjectReturnType() {
+        assertNotReached();
+    }
+
+    private static void objectReturnType() {
+        assertNotReached();
+    }
+
+    static Integer bsmReturningInteger(MethodHandles.Lookup lookup, String name, MethodType type) {
+        System.out.println("bsm returning Integer value.");
+        return Integer.valueOf(3);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmReturningInteger",
+                    parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+                    returnType = Integer.class
+                ),
+        fieldOrMethodName = "integerReturnType"
+    )
+    private static void invokeIntegerReturnType() {
+        assertNotReached();
+    }
+
+    private static void integerReturnType() {
+        assertNotReached();
+    }
+
+    static class TestersConstantCallSite extends ConstantCallSite {
+        public TestersConstantCallSite(MethodHandle mh) {
+            super(mh);
+        }
+    }
+
+    static TestersConstantCallSite bsmReturningTestersConstantCallsite(
+            MethodHandles.Lookup lookup, String name, MethodType type) throws Throwable {
+        return new TestersConstantCallSite(lookup.findStatic(lookup.lookupClass(), name, type));
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestBadBootstrapArguments.class,
+                    name = "bsmReturningTestersConstantCallsite",
+                    parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+                    returnType = TestersConstantCallSite.class
+                ),
+        fieldOrMethodName = "sayHello"
+    )
+    private static void invokeViaCustomCallSiteClass() {
+        assertNotReached();
+    }
+
+    private static void sayHello() {
+        System.out.println("Hello!");
+    }
+
+    static void test() {
+        System.out.println("TestBadBootstrapArguments");
+        invokeHappy();
+        try {
+            invokeWrongParameterTypes();
+            assertNotReached();
+        } catch (NoSuchMethodError expected) {
+            System.out.print("invokeWrongParameterTypes => ");
+            System.out.println(expected.getClass());
+        }
+        try {
+            invokeMissingParameterTypes();
+            assertNotReached();
+        } catch (NoSuchMethodError expected) {
+            System.out.print("invokeMissingParameterTypes => ");
+            System.out.println(expected.getClass());
+        }
+        try {
+            invokeExtraArguments();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            assertEquals(WrongMethodTypeException.class, expected.getCause().getClass());
+            System.out.print("invokeExtraArguments => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            invokeWrongArguments();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            assertEquals(ClassCastException.class, expected.getCause().getClass());
+            System.out.print("invokeWrongArguments => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            invokeWrongArguments();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            assertEquals(ClassCastException.class, expected.getCause().getClass());
+            System.out.print("invokeWrongArguments => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            invokeWrongArgumentsAgain();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            assertEquals(ClassCastException.class, expected.getCause().getClass());
+            System.out.print("invokeWrongArgumentsAgain => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            invokeNarrowArguments();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            assertEquals(ClassCastException.class, expected.getCause().getClass());
+            System.out.print("invokeNarrowArguments => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        invokeWideningArguments();
+        invokeBoxingArguments();
+        try {
+            invokeWideningBoxingArguments();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("invokeWideningBoxingArguments => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            invokeVoidReturnType();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("invokeVoidReturnType() => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            invokeObjectReturnType();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("invokeObjectReturnType() => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            invokeIntegerReturnType();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("invokeIntegerReturnType() => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        invokeViaCustomCallSiteClass();
+    }
+}
diff --git a/test/952-invoke-custom/src/TestDynamicBootstrapArguments.java b/test/952-invoke-custom/src/TestDynamicBootstrapArguments.java
new file mode 100644
index 0000000..782feca
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDynamicBootstrapArguments.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import annotations.BootstrapMethod;
+import annotations.CalledByIndy;
+import annotations.Constant;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class TestDynamicBootstrapArguments extends TestBase {
+    private static int bsmCalls = 0;
+
+    static CallSite bsm(
+            MethodHandles.Lookup lookup,
+            String name,
+            MethodType methodType,
+            String otherNameComponent,
+            long nameSuffix)
+            throws Throwable {
+        bsmCalls = bsmCalls + 1;
+        Class<?> definingClass = TestDynamicBootstrapArguments.class;
+        String methodName = name + otherNameComponent + nameSuffix;
+        MethodHandle mh = lookup.findStatic(definingClass, methodName, methodType);
+        System.out.println("bsm");
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestDynamicBootstrapArguments.class,
+                    name = "bsm",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        String.class,
+                        long.class
+                    }
+                ),
+        fieldOrMethodName = "target",
+        returnType = int.class,
+        parameterTypes = {int.class, String.class, double.class},
+        constantArgumentsForBootstrapMethod = {
+            @Constant(stringValue = "A"),
+            @Constant(longValue = 100000000l)
+        }
+    )
+    private static int testDynamic(int i, String s, Double d) {
+        assertNotReached();
+        return 0;
+    }
+
+    private static int targetA100000000(int i, String s, Double d) {
+        System.out.print(i);
+        System.out.print(", ");
+        System.out.print(s);
+        System.out.print(", ");
+        System.out.println(d);
+        return i;
+    }
+
+    static void testCallSites() {
+        assertEquals(0, testDynamic(0, "One", Math.PI));
+        assertEquals(1, testDynamic(1, "Two", Math.E));
+        assertEquals(2, testDynamic(2, "Three", 0.0));
+    }
+
+    static void test() {
+        System.out.println("TestDynamicArguments");
+        testCallSites();
+        assertEquals(3, bsmCalls);
+        testCallSites();
+        assertEquals(3, bsmCalls);
+    }
+}
diff --git a/test/952-invoke-custom/src/TestInvocationKinds.java b/test/952-invoke-custom/src/TestInvocationKinds.java
index 7b88c18..f743bef 100644
--- a/test/952-invoke-custom/src/TestInvocationKinds.java
+++ b/test/952-invoke-custom/src/TestInvocationKinds.java
@@ -173,6 +173,7 @@
 
     static class Widget {
         int value;
+
         public Widget(int value) {}
     }
 
diff --git a/test/952-invoke-custom/src/TestVariableArityLinkerMethod.java b/test/952-invoke-custom/src/TestVariableArityLinkerMethod.java
new file mode 100644
index 0000000..597273c
--- /dev/null
+++ b/test/952-invoke-custom/src/TestVariableArityLinkerMethod.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import annotations.BootstrapMethod;
+import annotations.CalledByIndy;
+import annotations.Constant;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+
+public class TestVariableArityLinkerMethod extends TestBase {
+    private static void printBsmArgs(String method, Object... args) {
+        System.out.print(method);
+        System.out.print("(");
+        for (int i = 0; i < args.length; ++i) {
+            if (i != 0) {
+                System.out.print(", ");
+            }
+            if (args[i] != null && args[i].getClass().isArray()) {
+                Object array = args[i];
+                if (array.getClass() == int[].class) {
+                    System.out.print(Arrays.toString((int[]) array));
+                } else if (array.getClass() == long[].class) {
+                    System.out.print(Arrays.toString((long[]) array));
+                } else if (array.getClass() == float[].class) {
+                    System.out.print(Arrays.toString((float[]) array));
+                } else if (array.getClass() == double[].class) {
+                    System.out.print(Arrays.toString((double[]) array));
+                } else {
+                    System.out.print(Arrays.toString((Object[]) array));
+                }
+            } else {
+                System.out.print(args[i]);
+            }
+        }
+        System.out.println(");");
+    }
+
+    private static CallSite bsmWithStringArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            String... arityArgs)
+            throws Throwable {
+        printBsmArgs("bsmWithStringArray", lookup, methodName, methodType, arityArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodA",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(stringValue = "Aachen"),
+            @Constant(stringValue = "Aalborg"),
+            @Constant(stringValue = "Aalto")
+        }
+    )
+    private static void methodA() {
+        System.out.println("methodA");
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodB",
+        constantArgumentsForBootstrapMethod = {@Constant(stringValue = "barium")}
+    )
+    private static void methodB() {
+        System.out.println("methodB");
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodC"
+    )
+    private static void methodC() {
+        System.out.println("methodC");
+    }
+
+    private static CallSite bsmWithIntAndStringArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            int extraInt,
+            String... extraArityArgs)
+            throws Throwable {
+        printBsmArgs(
+                "bsmWithIntAndStringArray",
+                lookup,
+                methodName,
+                methodType,
+                extraInt,
+                extraArityArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithIntAndStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodD",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(intValue = 101),
+            @Constant(stringValue = "zoo"),
+            @Constant(stringValue = "zoogene"),
+            @Constant(stringValue = "zoogenic")
+        }
+    )
+    private static void methodD() {
+        System.out.println("methodD");
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithIntAndStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodE",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(intValue = 102),
+            @Constant(stringValue = "zonic")
+        }
+    )
+    private static void methodE() {
+        System.out.println("methodE");
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithIntAndStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodF",
+        constantArgumentsForBootstrapMethod = {@Constant(intValue = 103)}
+    )
+    private static void methodF() {
+        System.out.println("methodF");
+    }
+
+    private static CallSite bsmWithLongAndIntArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            long extraArg,
+            int... arityArgs)
+            throws Throwable {
+        printBsmArgs("bsmWithLongAndIntArray", lookup, methodName, methodType, extraArg, arityArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithLongAndIntArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        long.class,
+                        int[].class
+                    }
+                ),
+        fieldOrMethodName = "methodG",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(longValue = 0x123456789abcdefl),
+            @Constant(intValue = +1),
+            @Constant(intValue = -1),
+            @Constant(intValue = +2),
+            @Constant(intValue = -2)
+        }
+    )
+    private static void methodG() {
+        System.out.println("methodG");
+    }
+
+    private static CallSite bsmWithFloatAndLongArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            float extraArg,
+            long... arityArgs)
+            throws Throwable {
+        printBsmArgs(
+                "bsmWithFloatAndLongArray", lookup, methodName, methodType, extraArg, arityArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithFloatAndLongArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        float.class,
+                        long[].class
+                    }
+                ),
+        fieldOrMethodName = "methodH",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(floatValue = (float) -Math.E),
+            @Constant(longValue = 999999999999l),
+            @Constant(longValue = -8888888888888l)
+        }
+    )
+    private static void methodH() {
+        System.out.println("methodH");
+    }
+
+    private static CallSite bsmWithClassAndFloatArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            Class<?> extraArg,
+            float... arityArgs)
+            throws Throwable {
+        printBsmArgs(
+                "bsmWithClassAndFloatArray", lookup, methodName, methodType, extraArg, arityArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithClassAndFloatArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        Class.class,
+                        float[].class
+                    }
+                ),
+        fieldOrMethodName = "methodI",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(classValue = Throwable.class),
+            @Constant(floatValue = Float.MAX_VALUE),
+            @Constant(floatValue = Float.MIN_VALUE),
+            @Constant(floatValue = (float) Math.PI),
+            @Constant(floatValue = (float) -Math.PI)
+        }
+    )
+    private static void methodI() {
+        System.out.println("methodI");
+    }
+
+    private static CallSite bsmWithDoubleArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            double... arityArgs)
+            throws Throwable {
+        printBsmArgs("bsmWithDoubleArray", lookup, methodName, methodType, arityArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithDoubleArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        double[].class
+                    }
+                ),
+        fieldOrMethodName = "methodJ",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(doubleValue = Double.MAX_VALUE),
+            @Constant(doubleValue = Double.MIN_VALUE),
+            @Constant(doubleValue = Math.E),
+            @Constant(doubleValue = -Math.PI)
+        }
+    )
+    private static void methodJ() {
+        System.out.println("methodJ");
+    }
+
+    private static CallSite bsmWithClassArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            Class... arityArgs)
+            throws Throwable {
+        printBsmArgs("bsmWithClassArray", lookup, methodName, methodType, arityArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithClassArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        Class[].class
+                    }
+                ),
+        fieldOrMethodName = "methodK",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(classValue = Integer.class),
+            @Constant(classValue = MethodHandles.class),
+            @Constant(classValue = Arrays.class)
+        }
+    )
+    private static void methodK() {
+        System.out.println("methodK");
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithIntAndStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodO",
+        constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 104)}
+    )
+    private static void methodO() {
+        // Arguments are not compatible
+        assertNotReached();
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithIntAndStringArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        String[].class
+                    }
+                ),
+        fieldOrMethodName = "methodP",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(intValue = 103),
+            @Constant(stringValue = "A"),
+            @Constant(stringValue = "B"),
+            @Constant(intValue = 42)
+        }
+    )
+    private static void methodP() {
+        // Arguments are not compatible - specifically, the third
+        // component of potential collector array is an integer
+        // argument (42).
+        assertNotReached();
+    }
+
+    private static CallSite bsmWithWiderArray(
+            MethodHandles.Lookup lookup, String methodName, MethodType methodType, long[] extraArgs)
+            throws Throwable {
+        printBsmArgs("bsmWithWiderArray", lookup, methodName, methodType, extraArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithWiderArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        long[].class
+                    }
+                ),
+        fieldOrMethodName = "methodQ",
+        constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 42)}
+    )
+    private static void methodQ() {
+        assertNotReached();
+    }
+
+    private static CallSite bsmWithBoxedArray(
+            MethodHandles.Lookup lookup,
+            String methodName,
+            MethodType methodType,
+            Integer[] extraArgs)
+            throws Throwable {
+        printBsmArgs("bsmWithBoxedArray", lookup, methodName, methodType, extraArgs);
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType);
+        return new ConstantCallSite(mh);
+    }
+
+    @CalledByIndy(
+        bootstrapMethod =
+                @BootstrapMethod(
+                    enclosingType = TestVariableArityLinkerMethod.class,
+                    name = "bsmWithBoxedArray",
+                    parameterTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        Integer[].class
+                    }
+                ),
+        fieldOrMethodName = "methodR",
+        constantArgumentsForBootstrapMethod = {
+            @Constant(intValue = 1030),
+            @Constant(intValue = 420)
+        }
+    )
+    private static void methodR() {
+        assertNotReached();
+    }
+
+    static void test() {
+        // Happy cases
+        for (int i = 0; i < 2; ++i) {
+            methodA();
+            methodB();
+            methodC();
+        }
+        for (int i = 0; i < 2; ++i) {
+            methodD();
+            methodE();
+            methodF();
+        }
+        methodG();
+        methodH();
+        methodI();
+        methodJ();
+        methodK();
+
+        // Broken cases
+        try {
+            // bsm has incompatible static methods. Collector
+            // component type is String, the corresponding static
+            // arguments are int values.
+            methodO();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("methodO => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            // bsm has a trailing String array for the collector array.
+            // There is an int value amongst the String values.
+            methodP();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("methodP => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            // bsm has as trailing long[] element for the collector array.
+            // The corresponding static bsm arguments are of type int.
+            methodQ();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("methodQ => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+        try {
+            // bsm has as trailing Integer[] element for the collector array.
+            // The corresponding static bsm arguments are of type int.
+            methodR();
+            assertNotReached();
+        } catch (BootstrapMethodError expected) {
+            System.out.print("methodR => ");
+            System.out.print(expected.getClass());
+            System.out.print(" => ");
+            System.out.println(expected.getCause().getClass());
+        }
+    }
+}
diff --git a/test/988-method-trace/check b/test/988-method-trace/check
deleted file mode 100644
index de64a3e..0000000
--- a/test/988-method-trace/check
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 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.
-
-# Building for libcore, this uses @hide API which gives it wrong method trace in the expected.txt
-# TODO: would be nice if we could build against core_current jars in the future to avoid this.
-if [[ "$NEED_DEX" == true ]]; then
-  patch -p0 expected.txt < expected_jack.diff >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 574d5b0..7f64e23 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -107,8 +107,8 @@
 ......=> public static char[] java.util.Arrays.copyOf(char[],int)
 .......=> public static int java.lang.Math.min(int,int)
 .......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16>
-.......=> public static void java.lang.System.arraycopy(char[],int,char[],int,int)
-.......<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null>
+.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
 ......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d,  , a, r, g, u, m, e, n, t, :,  , -, 1, 9,  , <,  , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]>
 .....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
 .....=> static void java.lang.Integer.getChars(int,int,char[])
@@ -208,8 +208,8 @@
 ......=> public static char[] java.util.Arrays.copyOf(char[],int)
 .......=> public static int java.lang.Math.min(int,int)
 .......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16>
-.......=> public static void java.lang.System.arraycopy(char[],int,char[],int,int)
-.......<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null>
+.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
 ......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d,  , a, r, g, u, m, e, n, t, :,  , -, 1, 9,  , <,  , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]>
 .....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
 .....=> static void java.lang.Integer.getChars(int,int,char[])
diff --git a/test/988-method-trace/expected_jack.diff b/test/988-method-trace/expected_jack.diff
deleted file mode 100644
index 11364a0..0000000
--- a/test/988-method-trace/expected_jack.diff
+++ /dev/null
@@ -1,10 +0,0 @@
-450,453c450,453
-< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
-< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
-< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
-< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
----
-> .=> public static void java.lang.System.arraycopy(int[],int,int[],int,int)
-> .<= public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> <null: null>
-> .=> public static void java.lang.System.arraycopy(char[],int,char[],int,int)
-> .<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null>
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index b8427f4..e9127a8 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -812,6 +812,25 @@
 fi
 RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}detect_leaks=0"
 
+# For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
+# the same defaults as for prebuilt: everything when --dev, otherwise errors and above only.
+if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then
+  if [ "$DEV_MODE" = "y" ]; then
+      export ANDROID_LOG_TAGS='*:d'
+  elif [ "$USE_DEX2OAT_AND_PATCHOAT" = "n" ]; then
+      # All tests would log the error of failing dex2oat/patchoat. Be silent here and only
+      # log fatal events.
+      export ANDROID_LOG_TAGS='*:s'
+  elif [ "$HAVE_IMAGE" = "n" ]; then
+      # All tests would log the error of missing image. Be silent here and only log fatal
+      # events.
+      export ANDROID_LOG_TAGS='*:s'
+  else
+      # We are interested in LOG(ERROR) output.
+      export ANDROID_LOG_TAGS='*:e'
+  fi
+fi
+
 if [ "$HOST" = "n" ]; then
     adb root > /dev/null
     adb wait-for-device
@@ -850,7 +869,12 @@
     fi
 
     # System libraries needed by libarttestd.so
-    PUBLIC_LIBS=libart.so:libartd.so:libc++.so:libbacktrace.so:libdexfile.so:libdexfiled.so:libbase.so:libnativehelper.so
+    PUBLIC_LIBS=libc++.so:libbacktrace.so:libbase.so:libnativehelper.so
+    if [ "$TEST_IS_NDEBUG" = "y" ]; then
+      PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so
+    else
+      PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so
+    fi
 
     # Create a script with the command. The command can get longer than the longest
     # allowed adb command and there is no way to get the exit status from a adb shell
@@ -862,6 +886,7 @@
              export ANDROID_ADDITIONAL_PUBLIC_LIBRARIES=$PUBLIC_LIBS && \
              export DEX_LOCATION=$DEX_LOCATION && \
              export ANDROID_ROOT=$ANDROID_ROOT && \
+             export ANDROID_LOG_TAGS=$ANDROID_LOG_TAGS && \
              rm -rf ${DEX_LOCATION}/dalvik-cache/ && \
              mkdir -p ${mkdir_locations} && \
              export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
@@ -899,16 +924,6 @@
     # Host run.
     export ANDROID_PRINTF_LOG=brief
 
-    # By default, and for prebuild dex2oat, we are interested in errors being logged. In dev mode
-    # we want debug messages.
-    if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then
-      if [ "$DEV_MODE" = "y" ]; then
-          export ANDROID_LOG_TAGS='*:d'
-      else
-          export ANDROID_LOG_TAGS='*:e'
-      fi
-    fi
-
     export ANDROID_DATA="$DEX_LOCATION"
     export ANDROID_ROOT="${ANDROID_ROOT}"
     export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
@@ -969,25 +984,6 @@
     $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
     $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
 
-    # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
-    # the same defaults as for prebuilt: everything when --dev, otherwise errors and above only.
-    if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then
-      if [ "$DEV_MODE" = "y" ]; then
-          export ANDROID_LOG_TAGS='*:d'
-      elif [ "$USE_DEX2OAT_AND_PATCHOAT" = "n" ]; then
-          # All tests would log the error of failing dex2oat/patchoat. Be silent here and only
-          # log fatal events.
-          export ANDROID_LOG_TAGS='*:s'
-      elif [ "$HAVE_IMAGE" = "n" ]; then
-          # All tests would log the error of missing image. Be silent here and only log fatal
-          # events.
-          export ANDROID_LOG_TAGS='*:s'
-      else
-          # We are interested in LOG(ERROR) output.
-          export ANDROID_LOG_TAGS='*:e'
-      fi
-    fi
-
     if [ "$DRY_RUN" = "y" ]; then
       exit 0
     fi
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 2bf09c3..d1855dc 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -286,11 +286,6 @@
         "variant": "npictest"
     },
     {
-        "tests": "639-checker-code-sinking",
-        "variant": "pictest",
-        "bug": "http://b/65366606"
-    },
-    {
         "tests": "055-enum-performance",
         "variant": "optimizing | regalloc_gc",
         "description": ["055: Exceeds run time limits due to heap poisoning ",
@@ -504,6 +499,7 @@
     {
         "tests": [
             "031-class-attributes",
+            "715-clinit-implicit-parameter-annotations",
             "911-get-stack-trace"
         ],
         "description": [
@@ -669,12 +665,6 @@
         "description": ["Time out"]
     },
     {
-        "tests": ["130-hprof"],
-        "env_vars": {"SANITIZE_HOST": "address"},
-        "bug": "b/73060923",
-        "description": ["ASAN issue"]
-    },
-    {
         "tests": ["1941-dispose-stress", "522-checker-regression-monitor-exit"],
         "variant": "jvm",
         "bug": "b/73888836",
@@ -933,7 +923,6 @@
           "946-obsolete-throw",
           "948-change-annotations",
           "950-redefine-intrinsic",
-          "952-invoke-custom",
           "954-invoke-polymorphic-verifier",
           "955-methodhandles-smali",
           "956-methodhandles",
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 5556962..70efce5 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -17,6 +17,16 @@
 import tempfile
 import subprocess
 
+# begin import $ANDROID_BUILD_TOP/art/tools/build/var_cache.py
+_THIS_DIR = os.path.dirname(os.path.realpath(__file__))
+_TOP = os.path.join(_THIS_DIR, "../../..")
+_VAR_CACHE_DIR = os.path.join(_TOP, "art/tools/build/")
+
+import sys
+sys.path.append(_VAR_CACHE_DIR)
+import var_cache
+# end import var_cache.py
+
 _env = dict(os.environ)
 
 def _getEnvBoolean(var, default):
@@ -28,55 +38,8 @@
       return False
   return default
 
-_DUMP_MANY_VARS_LIST = ['HOST_2ND_ARCH_PREFIX',
-                        'TARGET_2ND_ARCH',
-                        'TARGET_ARCH',
-                        'HOST_PREFER_32_BIT',
-                        'HOST_OUT_EXECUTABLES',
-                        'ANDROID_JAVA_TOOLCHAIN',
-                        'ANDROID_COMPILE_WITH_JACK',
-                        'USE_D8_BY_DEFAULT']
-_DUMP_MANY_VARS = None  # To be set to a dictionary with above list being the keys,
-                        # and the build variable being the value.
-def _dump_many_vars(var_name):
-  """
-  Reach into the Android build system to dump many build vars simultaneously.
-  Since the make system is so slow, we want to avoid calling into build frequently.
-  """
-  global _DUMP_MANY_VARS
-  global _DUMP_MANY_VARS_LIST
-
-  # Look up var from cache.
-  if _DUMP_MANY_VARS:
-    return _DUMP_MANY_VARS[var_name]
-
-  all_vars=" ".join(_DUMP_MANY_VARS_LIST)
-
-  # The command is taken from build/envsetup.sh to fetch build variables.
-  command = ("build/soong/soong_ui.bash --dumpvars-mode --vars=\"%s\"") % (all_vars)
-
-  config = subprocess.Popen(command,
-                            stdout=subprocess.PIPE,
-                            universal_newlines=True,
-                            shell=True,
-                            cwd=ANDROID_BUILD_TOP).communicate()[0] # read until EOF, select stdin
-  # Prints out something like:
-  # TARGET_ARCH='arm64'
-  # HOST_ARCH='x86_64'
-  _DUMP_MANY_VARS = {}
-  for line in config.split("\n"):
-    # Split out "$key='$value'" via regex.
-    match = re.search("([^=]+)='([^']*)", line)
-    if not match:
-      continue
-    key = match.group(1)
-    value = match.group(2)
-    _DUMP_MANY_VARS[key] = value
-
-  return _DUMP_MANY_VARS[var_name]
-
 def _get_build_var(var_name):
-  return _dump_many_vars(var_name)
+  return var_cache.get_build_var(var_name)
 
 def _get_build_var_boolean(var, default):
   val = _get_build_var(var)
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index fcc5505..e0ccc3e 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2017, The Android Open Source Project
 #
@@ -45,9 +45,9 @@
 ##########
 
 if options.list:
-  print "List of all known build_target: "
-  for k in sorted(target_config.iterkeys()):
-    print " * " + k
+  print("List of all known build_target: ")
+  for k in sorted(target_config.keys()):
+    print(" * " + k)
   # TODO: would be nice if this was the same order as the target config file.
   sys.exit(1)
 
@@ -59,10 +59,10 @@
 n_threads = options.n_threads
 custom_env = target.get('env', {})
 custom_env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true'
-print custom_env
+print(custom_env)
 os.environ.update(custom_env)
 
-if target.has_key('make'):
+if 'make' in target:
   build_command = 'make'
   build_command += ' DX='
   build_command += ' -j' + str(n_threads)
@@ -75,7 +75,7 @@
   if subprocess.call(build_command.split()):
     sys.exit(1)
 
-if target.has_key('golem'):
+if 'golem' in target:
   machine_type = target.get('golem')
   # use art-opt-cc by default since it mimics the default preopt config.
   default_golem_config = 'art-opt-cc'
@@ -93,7 +93,7 @@
   if subprocess.call(cmd):
     sys.exit(1)
 
-if target.has_key('run-test'):
+if 'run-test' in target:
   run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
                                    'art/test/testrunner/testrunner.py')]
   test_flags = target.get('run-test', [])
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index d2da244..bbe7465 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -24,9 +24,9 @@
 
 #include <jni.h>
 
+#include "base/utils.h"
 #include "exec_utils.h"
 #include "jvmti.h"
-#include "utils.h"
 
 #pragma clang diagnostic push
 
diff --git a/tools/bisection_search/bisection_search.py b/tools/bisection_search/bisection_search.py
index 27bd599..a1ac72d 100755
--- a/tools/bisection_search/bisection_search.py
+++ b/tools/bisection_search/bisection_search.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
diff --git a/tools/bisection_search/bisection_test.py b/tools/bisection_search/bisection_test.py
index 9aa08fb..b6a73c0 100755
--- a/tools/bisection_search/bisection_test.py
+++ b/tools/bisection_search/bisection_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
diff --git a/tools/bootjars.sh b/tools/bootjars.sh
index f710de9..dca209d 100755
--- a/tools/bootjars.sh
+++ b/tools/bootjars.sh
@@ -21,7 +21,7 @@
 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 TOP="$DIR/../.."
 
-source "${TOP}/build/envsetup.sh" >&/dev/null # import get_build_var
+source "${TOP}/art/tools/build/var_cache.sh" >&/dev/null # import get_build_var
 
 selected_env_var=
 core_jars_only=n
diff --git a/tools/build/var_cache.py b/tools/build/var_cache.py
new file mode 100644
index 0000000..9e616fa
--- /dev/null
+++ b/tools/build/var_cache.py
@@ -0,0 +1,148 @@
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# !!! Keep up-to-date with var_cache.sh
+#
+
+#
+# Provide a soong-build variable query mechanism that is cached
+# in the current process and any other subchild process that knows
+# how to parse the exported variable:
+#
+# export ART_TOOLS_BUILD_VAR_CACHE="..."
+#
+# Of the format:
+#
+#   <key1>='<value1>'\n
+#   <key2>='<value2>'\n
+#   ...
+#   <keyN>='<valueN>'
+#
+# Note: This is intentionally the same output format as
+#     build/soong/soong_ui.bash --dumpvars-mode --vars "key1 key2 ... keyN"
+#
+# For example, this would be a valid var-cache:
+#
+# export ART_TOOLS_BUILD_VAR_CACHE="TARGET_CORE_JARS='core-oj core-libart'
+#   HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex'"
+#
+# Calling into soong repeatedly is very slow; whenever it needs to be done
+# more than once, the var_cache.py or var_cache.sh script should be used instead.
+#
+
+import os
+import subprocess
+import sys
+
+def get_build_var(name):
+  """
+  Query soong build for a variable value and return it as a string.
+
+  Var lookup is cached, subsequent var lookups in any child process
+  (that includes a var-cache is free). The var must be in 'var_list'
+  to participate in the cache.
+
+  Example:
+     host_out = var_cache.get_build_var('HOST_OUT')
+
+  Note that build vars can often have spaces in them,
+  so the caller must take care to ensure space-correctness.
+
+  Raises KeyError if the variable name is not in 'var_list'.
+  """
+  _populate()
+  _build_dict()
+
+  value = _var_cache_dict.get(name)
+  if value is None:
+    _debug(_var_cache_dict)
+    raise KeyError("The variable '%s' is not in 'var_list', can't lookup" %(name))
+
+  return value
+
+_var_cache_dict = None
+_THIS_DIR = os.path.dirname(os.path.realpath(__file__))
+_TOP = os.path.join(_THIS_DIR, "../../..")
+_VAR_LIST_PATH = os.path.join(_THIS_DIR, "var_list")
+_SOONG_UI_SCRIPT = os.path.join(_TOP, "build/soong/soong_ui.bash")
+_DEBUG = False
+
+def _populate():
+  if os.environ.get('ART_TOOLS_BUILD_VAR_CACHE'):
+    return
+
+  _debug("Varcache missing (PY)... repopulate")
+
+  interesting_vars=[]
+  with open(_VAR_LIST_PATH) as f:
+    for line in f.readlines():
+      line = line.strip()
+      if not line or line.startswith('#'):
+        continue
+
+      _debug(line)
+
+      interesting_vars.append(line)
+
+  _debug("Interesting vars: ", interesting_vars)
+
+  # Invoke soong exactly once for optimal performance.
+  var_values = subprocess.check_output([
+      _SOONG_UI_SCRIPT, '--dumpvars-mode', '-vars', " ".join(interesting_vars)],
+      cwd=_TOP)
+
+  # Export the ART_TOOLS_BUILD_VAR_CACHE in the same format as soong_ui.bash --dumpvars-mode.
+  os.environb[b'ART_TOOLS_BUILD_VAR_CACHE'] = var_values
+
+  _debug("Soong output: ", var_values)
+
+def _build_dict():
+  global _var_cache_dict
+
+  if _var_cache_dict:
+    return
+
+  _debug("_var_cache_build_dict()")
+
+  _var_cache_dict = {}
+
+  # Parse $ART_TOOLS_BUILD_VAR_CACHE, e.g.
+  #   TARGET_CORE_JARS='core-oj core-libart conscrypt okhttp bouncycastle apache-xml'
+  #   HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex ...'
+
+  for line in os.environ['ART_TOOLS_BUILD_VAR_CACHE'].splitlines():
+    _debug(line)
+    var_name, var_value = line.split("=")
+    var_value = var_value.strip("'")
+
+    _debug("Var name =", var_name)
+    _debug("Var value =", var_value)
+
+    _var_cache_dict[var_name] = var_value
+
+  _debug("Num entries in dict: ", len(_var_cache_dict))
+
+def _debug(*args):
+  if _DEBUG:
+    print(*args, file=sys.stderr)
+
+# Below definitions are for interactive use only, e.g.
+# python -c 'import var_cache; var_cache._dump_cache()'
+
+def _dump_cache():
+  _populate()
+  print(os.environ['ART_TOOLS_BUILD_VAR_CACHE'])
+
+#get_build_var("xyz")
diff --git a/tools/build/var_cache.sh b/tools/build/var_cache.sh
new file mode 100755
index 0000000..26e9770
--- /dev/null
+++ b/tools/build/var_cache.sh
@@ -0,0 +1,195 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# !!! Keep up-to-date with var_cache.py
+#
+
+#
+# Provide a soong-build variable query mechanism that is cached
+# in the current process and any other subchild process that knows
+# how to parse the exported variable:
+#
+# export ART_TOOLS_BUILD_VAR_CACHE="..."
+#
+# Of the format:
+#
+#   <key1>='<value1>'\n
+#   <key2>='<value2>'\n
+#   ...
+#   <keyN>='<valueN>'
+#
+# Note: This is intentionally the same output format as
+#     build/soong/soong_ui.bash --dumpvars-mode --vars "key1 key2 ... keyN"
+#
+# For example, this would be a valid var-cache:
+#
+# export ART_TOOLS_BUILD_VAR_CACHE="TARGET_CORE_JARS='core-oj core-libart'
+#   HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex'"
+#
+# Calling into soong repeatedly is very slow; whenever it needs to be done
+# more than once, the var_cache.py or var_cache.sh script should be used instead.
+#
+
+# -------------------------------------------------------
+
+# Echoes the result of get_build_var <var_name>.
+# Var lookup is cached, subsequent var lookups in any child process
+# (that includes a var-cache is free). The var must be in 'var_list'
+# to participate in the cache.
+#
+# Example:
+#    local host_out="$(get_build_var HOST_OUT)"
+#
+# Note that build vars can often have spaces in them,
+# so the caller must take care to ensure space-correctness.
+get_build_var() {
+  local var_name="$1"
+
+  _var_cache_populate
+  _var_cache_build_dict
+
+  if [[ ${_VAR_CACHE_DICT[$var_name]+exists} ]]; then
+    echo "${_VAR_CACHE_DICT[$var_name]}"
+    return 0
+  else
+    echo "[ERROR] get_build_var: The variable '$var_name' is not in 'var_list', can't lookup." >&2
+    return 1
+  fi
+}
+
+# The above functions are "public" and are intentionally not exported.
+# User scripts must have "source var_cache.sh" to take advantage of caching.
+
+# -------------------------------------------------------
+# Below functions are "private";
+# do not call them outside of this file.
+
+_var_cache_populate() {
+  if [[ -n $ART_TOOLS_BUILD_VAR_CACHE ]]; then
+    _var_cache_debug "ART_TOOLS_BUILD_VAR_CACHE preset to (quotes added)..."
+    _var_cache_debug \""$ART_TOOLS_BUILD_VAR_CACHE"\"
+    return 0
+  fi
+
+  _var_cache_debug "Varcache missing... repopulate"
+
+  local this_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+  local top="$this_dir/../../.."
+
+  local interesting_vars=()
+  while read -r line; do
+    if [[ -z $line ]] || [[ $line == '#'* ]]; then
+      continue;
+    fi
+    interesting_vars+=($line)
+  done < "$this_dir"/var_list
+
+  _var_cache_debug "Interesting vars: " ${interesting_vars[@]}
+
+  local flat_vars="${interesting_vars[*]}"
+
+  local var_values
+  _var_cache_show_command "$top"/build/soong/soong_ui.bash --dumpvars-mode -vars=\"${interesting_vars[*]}\"
+
+  # Invoke soong exactly once for optimal performance.
+  # soong_ui.bash must be invoked from $ANDROID_BUILD_TOP or it gets confused and breaks.
+  var_values="$(cd "$top" && "$top"/build/soong/soong_ui.bash --dumpvars-mode -vars="$flat_vars")"
+
+  # Export the ART_TOOLS_BUILD_VAR_CACHE in the same format as soong_ui.bash --dumpvars-mode.
+  export ART_TOOLS_BUILD_VAR_CACHE="$var_values"
+
+  _var_cache_debug ART_TOOLS_BUILD_VAR_CACHE=\"$var_values\"
+}
+
+_var_cache_build_dict() {
+  if [[ ${#_VAR_CACHE_DICT[@]} -ne 0 ]]; then
+    # Associative arrays cannot be exported, have
+    # a separate step to reconstruct the associative
+    # array from a flat variable.
+    return 0
+  fi
+
+  # Parse $ART_TOOLS_BUILD_VAR_CACHE, e.g.
+  #   TARGET_CORE_JARS='core-oj core-libart conscrypt okhttp bouncycastle apache-xml'
+  #   HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex ...'
+
+  local var_name
+  local var_value
+  local strip_quotes
+
+  _var_cache_debug "_var_cache_build_dict()"
+
+  declare -g -A _VAR_CACHE_DICT  # global associative array.
+  while IFS='=' read -r var_name var_value; do
+    if [[ -z $var_name ]]; then
+      # skip empty lines, e.g. blank newline at the end
+      continue
+    fi
+    _var_cache_debug "Var_name was $var_name"
+    _var_cache_debug "Var_value was $var_value"
+    strip_quotes=${var_value//\'/}
+    _VAR_CACHE_DICT["$var_name"]="$strip_quotes"
+  done < <(echo "$ART_TOOLS_BUILD_VAR_CACHE")
+
+  _var_cache_debug ${#_VAR_CACHE_DICT[@]} -eq 0
+}
+
+_var_cache_debug() {
+  if ((_var_cache_debug_enabled)); then
+    echo "[DBG]: " "$@" >&2
+  fi
+}
+
+_var_cache_show_command() {
+  if (( _var_cache_show_commands || _var_cache_debug_enabled)); then
+    echo "$@" >&2
+  fi
+}
+
+while true; do
+  case $1 in
+    --help)
+      echo "Usage: $0 [--debug] [--show-commands] [--dump-cache] [--var <name>] [--var <name2>...]"
+      echo ""
+      echo "Exposes a function 'get_build_var' which returns the result of"
+      echo "a soong build variable."
+      echo ""
+      echo "Primarily intended to be used as 'source var_cache.sh',"
+      echo "but also allows interactive command line usage for simplifying development."
+      exit 0
+      ;;
+    --var)
+      echo -ne "$2="
+      get_build_var "$2"
+      shift
+      ;;
+    --debug)
+      _var_cache_debug_enabled=1
+      ;;
+    --show-commands)
+      _var_cache_show_commands=1
+      ;;
+    --dump-cache)
+      _var_cache_populate
+      echo "ART_TOOLS_BUILD_VAR_CACHE=\"$ART_TOOLS_BUILD_VAR_CACHE\""
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
diff --git a/tools/build/var_list b/tools/build/var_list
new file mode 100644
index 0000000..3727741
--- /dev/null
+++ b/tools/build/var_list
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# This file contains a list of all the build vars that need to be eagerly cached
+# by the var_cache.sh and var_cache.py scripts.
+# Lines starting with '#' or blank lines are ignored.
+#
+
+# javac-helper.sh
+TARGET_CORE_JARS
+PRODUCT_BOOT_JARS
+TARGET_OUT_COMMON_INTERMEDIATES
+HOST_CORE_JARS
+HOST_OUT_COMMON_INTERMEDIATES
+
+# testrunner/env.py
+HOST_2ND_ARCH_PREFIX
+TARGET_2ND_ARCH
+TARGET_ARCH
+HOST_PREFER_32_BIT
+HOST_OUT_EXECUTABLES
+ANDROID_JAVA_TOOLCHAIN
+ANDROID_COMPILE_WITH_JACK
+USE_D8_BY_DEFAULT
+
diff --git a/tools/common/common.py b/tools/common/common.py
index b822dca..735bbaa 100755
--- a/tools/common/common.py
+++ b/tools/common/common.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
diff --git a/tools/external_oj_libjdwp_art_failures.txt b/tools/external_oj_libjdwp_art_failures.txt
index 6c2206f..9b6ff98 100644
--- a/tools/external_oj_libjdwp_art_failures.txt
+++ b/tools/external_oj_libjdwp_art_failures.txt
@@ -13,12 +13,6 @@
   name: "org.apache.harmony.jpda.tests.jdwp.ThreadReference.ThreadGroup002Test#testThreadGroup002"
 },
 {
-  description: "Test fails due to modifiers not including ACC_SUPER",
-  result: EXEC_FAILED,
-  bug: 66906055,
-  name: "org.apache.harmony.jpda.tests.jdwp.ReferenceType.ModifiersTest#testModifiers001"
-},
-{
   description: "Test fails due to static values not being set correctly.",
   result: EXEC_FAILED,
   bug: 66905894,
diff --git a/tools/hiddenapi/find_api_violations.pl b/tools/hiddenapi/find_api_violations.pl
new file mode 100755
index 0000000..a022999
--- /dev/null
+++ b/tools/hiddenapi/find_api_violations.pl
@@ -0,0 +1,124 @@
+#!/usr/bin/perl
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use Getopt::Long;
+use Pod::Usage;
+
+=pod
+
+=head1 DESCRIPTION
+
+This script parses API violations from C<adb logcat>. Output is in CSV format
+with columns C<package>, C<symbol>, C<list>.
+
+The package name is mapped from a PID, parsed from the same log. To ensure you
+get all packages names, you should process the logcat from device boot time.
+
+=head1 SYNOPSIS
+
+  adb logcat | perl find_api_violations.pl > violations.csv
+  cat bugreport.txt | perl find_api_violations.pl --bugreport > violations.csv
+
+=head1 OPTIONS
+
+=over
+
+=item --[no]lightgrey
+
+(Don't) show light grey list accesses (default true)
+
+=item --[no]darkgrey
+
+(Don't) show dark grey list accesses (default true)
+
+=item --[no]black
+
+(Don't) show black list accesses (default true)
+
+=item --bugreport|-b
+
+Process a bugreport, rather than raw logcat
+
+=item --help
+
+=back
+
+=cut
+
+my $lightgrey = 1;
+my $darkgrey = 1;
+my $black = 1;
+my $bugreport = 0;
+my $help = 0;
+
+GetOptions("lightgrey!"  => \$lightgrey,
+           "darkgrey!"   => \$darkgrey,
+           "black!"      => \$black,
+           "bugreport|b" => \$bugreport,
+           "help"        => \$help)
+  or pod2usage(q(-verbose) => 1);
+
+pod2usage(q(-verbose) => 2) if ($help);
+
+my $in;
+
+if ($bugreport) {
+  my $found_main = 0;
+  while (my $line = <>) {
+    chomp $line;
+    if ($line =~ m/^------ SYSTEM LOG /) {
+      $found_main = 1;
+      last;
+    }
+  }
+  if (!$found_main) {
+    die "Couldn't find main log in bugreport\n";
+  }
+}
+
+my $procmap = {};
+print "package,symbol,list\n";
+while (my $line = <>) {
+  chomp $line;
+  last if $bugreport and $line =~ m/^------ \d+\.\d+s was the duration of 'SYSTEM LOG' ------/;
+  next if $line =~ m/^--------- beginning of/;
+  unless ($line =~ m/^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}\s+(?:\w+\s+)?(\d+)\s+(\d+)\s+([VWIDE])\s+(.*?): (.*)$/) {
+    die "Cannot match line: $line\n";
+    next;
+  }
+  my ($pid, $tid, $class, $tag, $msg) = ($1, $2, $3, $4, $5);
+  if ($tag eq "ActivityManager" && $msg =~ m/^Start proc (\d+):(.*?) for /) {
+    my ($new_pid, $proc_name) = ($1, $2);
+    my $package;
+    if ($proc_name =~ m/^(.*?)(:.*?)?\/(.*)$/) {
+      $package = $1;
+    } else {
+      $package = $proc_name;
+    }
+    $procmap->{$new_pid} = $package;
+  }
+  if ($tag eq "zygote" || $tag eq "zygote64") {
+    if ($msg =~ m/Accessing hidden (\w+) (L.*?) \((.*list), (.*?)\)/) {
+      my ($member_type, $symbol, $list, $access_type) = ($1, $2, $3, $4);
+      my $package = $procmap->{$pid} || "unknown($pid)";
+      print "$package,$symbol,$list\n"
+        if (($list =~ m/light/ && $lightgrey)
+          || ($list =~ m/dark/ && $darkgrey)
+          || ($list =~ m/black/ && $black));
+    }
+  }
+}
+
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index d986cf8..d22998a 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -21,12 +21,12 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
+#include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file-inl.h"
 #include "dex/hidden_api_access_flags.h"
 #include "mem_map.h"
-#include "os.h"
 
 namespace art {
 
diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py
index 203e03d..47fe072 100755
--- a/tools/jfuzz/run_dex_fuzz_test.py
+++ b/tools/jfuzz/run_dex_fuzz_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index 4a54a3a..3ff9f45 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
diff --git a/tools/jfuzz/run_jfuzz_test_nightly.py b/tools/jfuzz/run_jfuzz_test_nightly.py
index e6c216d..fecf116 100755
--- a/tools/jfuzz/run_jfuzz_test_nightly.py
+++ b/tools/jfuzz/run_jfuzz_test_nightly.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
 #
 # Copyright (C) 2016 The Android Open Source Project
 #