am 227dfb0a: Fix false alarm on thread suspend timeout

* commit '227dfb0a25d020f0571eb194b98eca350bd6d036':
  Fix false alarm on thread suspend timeout
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 0dcefea..6ce3877 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -173,6 +173,14 @@
   -DNVALGRIND \
   -Wno-unused-value
 
+
+ifdef ART_IMT_SIZE
+  art_cflags += -DIMT_SIZE=$(ART_IMT_SIZE)
+else
+  # Default is 64
+  art_cflags += -DIMT_SIZE=64
+endif
+
 ifeq ($(ART_SMALL_MODE),true)
   art_cflags += -DART_SMALL_MODE=1
 endif
@@ -192,7 +200,7 @@
 
 ifeq ($(HOST_OS),linux)
   # Larger frame-size for host clang builds today
-  art_host_non_debug_cflags += -Wframe-larger-than=2600
+  art_host_non_debug_cflags += -Wframe-larger-than=3000
   art_target_non_debug_cflags += -Wframe-larger-than=1728
 endif
 
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index d887acd..412f2dd 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -57,6 +57,7 @@
   LOCAL_SRC_FILES := $$(art_source)
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes)
   LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries)
+  LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
 
   ifeq ($$(art_ndebug_or_debug),ndebug)
     LOCAL_MODULE := $$(art_executable)
@@ -65,9 +66,15 @@
   endif
 
   LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS)
+  # Mac OS linker doesn't understand --export-dynamic/--version-script.
+  ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host)
+    LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
+  endif
+
   ifeq ($$(art_target_or_host),target)
   	$(call set-target-local-clang-vars)
   	$(call set-target-local-cflags-vars,$(6))
+    LOCAL_SHARED_LIBRARIES += libdl
   else # host
     LOCAL_CLANG := $(ART_HOST_CLANG)
     LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
@@ -76,7 +83,7 @@
     else
       LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
     endif
-    LOCAL_LDLIBS += -lpthread
+    LOCAL_LDLIBS += -lpthread -ldl
   endif
 
   ifeq ($$(art_ndebug_or_debug),ndebug)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a7d852b..71d504c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -72,6 +72,7 @@
   runtime/barrier_test.cc \
   runtime/base/bit_field_test.cc \
   runtime/base/bit_vector_test.cc \
+  runtime/base/hash_set_test.cc \
   runtime/base/hex_dump_test.cc \
   runtime/base/histogram_test.cc \
   runtime/base/mutex_test.cc \
@@ -152,6 +153,7 @@
   compiler/output_stream_test.cc \
   compiler/utils/arena_allocator_test.cc \
   compiler/utils/dedupe_set_test.cc \
+  compiler/utils/swap_space_test.cc \
   compiler/utils/arm/managed_register_arm_test.cc \
   compiler/utils/arm64/managed_register_arm64_test.cc \
   compiler/utils/x86/managed_register_x86_test.cc \
@@ -278,7 +280,7 @@
 
 .PHONY: $$(gtest_rule)
 $$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) $$(gtest_deps)
-	$(hide) ($$(call ART_TEST_SKIP,$$@) && LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) $$< && $$(call ART_TEST_PASSED,$$@)) \
+	$(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \
 	  || $$(call ART_TEST_FAILED,$$@)
 
   ART_TEST_HOST_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule)
@@ -329,6 +331,7 @@
   LOCAL_SRC_FILES := $$(art_gtest_filename)
   LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes)
   LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest
+  LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
 
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 69f9387..b0ec8fb 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -114,6 +114,7 @@
 	utils/x86_64/assembler_x86_64.cc \
 	utils/x86_64/managed_register_x86_64.cc \
 	utils/scoped_arena_allocator.cc \
+	utils/swap_space.cc \
 	buffered_output_stream.cc \
 	compilers.cc \
 	compiler.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 5e4623d..7617247 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -142,22 +142,12 @@
 CommonCompilerTest::CommonCompilerTest() {}
 CommonCompilerTest::~CommonCompilerTest() {}
 
-OatFile::OatMethod CommonCompilerTest::CreateOatMethod(const void* code, const uint8_t* gc_map) {
+OatFile::OatMethod CommonCompilerTest::CreateOatMethod(const void* code) {
   CHECK(code != nullptr);
-  const byte* base;
-  uint32_t code_offset, gc_map_offset;
-  if (gc_map == nullptr) {
-    base = reinterpret_cast<const byte*>(code);  // Base of data points at code.
-    base -= kPointerSize;  // Move backward so that code_offset != 0.
-    code_offset = kPointerSize;
-    gc_map_offset = 0;
-  } else {
-    // TODO: 64bit support.
-    base = nullptr;  // Base of data in oat file, ie 0.
-    code_offset = PointerToLowMemUInt32(code);
-    gc_map_offset = PointerToLowMemUInt32(gc_map);
-  }
-  return OatFile::OatMethod(base, code_offset, gc_map_offset);
+  const byte* base = reinterpret_cast<const byte*>(code);  // Base of data points at code.
+  base -= kPointerSize;  // Move backward so that code_offset != 0.
+  uint32_t code_offset = kPointerSize;
+  return OatFile::OatMethod(base, code_offset);
 }
 
 void CommonCompilerTest::MakeExecutable(mirror::ArtMethod* method) {
@@ -172,25 +162,29 @@
                                                             method->GetDexMethodIndex()));
   }
   if (compiled_method != nullptr) {
-    const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
+    const SwapVector<uint8_t>* code = compiled_method->GetQuickCode();
     const void* code_ptr;
     if (code != nullptr) {
       uint32_t code_size = code->size();
       CHECK_NE(0u, code_size);
-      const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+      const SwapVector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
       uint32_t vmap_table_offset = vmap_table.empty() ? 0u
           : sizeof(OatQuickMethodHeader) + vmap_table.size();
-      const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
+      const SwapVector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
       uint32_t mapping_table_offset = mapping_table.empty() ? 0u
           : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size();
-      OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset,
+      const SwapVector<uint8_t>& gc_map = compiled_method->GetGcMap();
+      uint32_t gc_map_offset = gc_map.empty() ? 0u
+          : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size() + gc_map.size();
+      OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
                                          compiled_method->GetFrameSizeInBytes(),
                                          compiled_method->GetCoreSpillMask(),
                                          compiled_method->GetFpSpillMask(), code_size);
 
       header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
       std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
-      size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size();
+      size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size() +
+          gc_map.size();
       size_t code_offset = compiled_method->AlignCode(size - code_size);
       size_t padding = code_offset - (size - code_size);
       chunk->reserve(padding + size);
@@ -198,6 +192,7 @@
       memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
       chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
       chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
+      chunk->insert(chunk->begin(), gc_map.begin(), gc_map.end());
       chunk->insert(chunk->begin(), padding, 0);
       chunk->insert(chunk->end(), code->begin(), code->end());
       CHECK_EQ(padding + size, chunk->size());
@@ -210,7 +205,7 @@
     const void* method_code = CompiledMethod::CodePointer(code_ptr,
                                                           compiled_method->GetInstructionSet());
     LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
-    OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
+    OatFile::OatMethod oat_method = CreateOatMethod(method_code);
     oat_method.LinkMethod(method);
     method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
   } else {
@@ -222,13 +217,13 @@
 #else
       const void* method_code = GetQuickToInterpreterBridge();
 #endif
-      OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
+      OatFile::OatMethod oat_method = CreateOatMethod(method_code);
       oat_method.LinkMethod(method);
       method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
     } else {
       const void* method_code = reinterpret_cast<void*>(art_quick_generic_jni_trampoline);
 
-      OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
+      OatFile::OatMethod oat_method = CreateOatMethod(method_code);
       oat_method.LinkMethod(method);
       method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
     }
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index df06b71..dd25dff 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -42,7 +42,7 @@
   ~CommonCompilerTest();
 
   // Create an OatMethod based on pointers (for unit tests).
-  OatFile::OatMethod CreateOatMethod(const void* code, const uint8_t* gc_map);
+  OatFile::OatMethod CreateOatMethod(const void* code);
 
   void MakeExecutable(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index f098a34..40ef513 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -20,7 +20,7 @@
 namespace art {
 
 CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-                           const std::vector<uint8_t>& quick_code)
+                           const ArrayRef<const uint8_t>& quick_code)
     : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
       portable_code_(nullptr), quick_code_(nullptr) {
   SetCode(&quick_code, nullptr);
@@ -29,22 +29,20 @@
 CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
                            const std::string& elf_object, const std::string& symbol)
     : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
-      portable_code_(nullptr), quick_code_(nullptr), symbol_(symbol) {
+      portable_code_(compiler_driver_->DeduplicateCode(
+          ArrayRef<const uint8_t>(reinterpret_cast<const uint8_t*>(elf_object.data()),
+                                  elf_object.size()))),
+      quick_code_(nullptr), symbol_(symbol) {
   CHECK_NE(elf_object.size(), 0U);
   CHECK_NE(symbol.size(), 0U);
-  std::vector<uint8_t> temp_code(elf_object.size());
-  for (size_t i = 0; i < elf_object.size(); ++i) {
-    temp_code[i] = elf_object[i];
-  }
   // TODO: we shouldn't just shove ELF objects in as "code" but
   // change to have different kinds of compiled methods.  This is
   // being deferred until we work on hybrid execution or at least
   // until we work on batch compilation.
-  SetCode(nullptr, &temp_code);
 }
 
-void CompiledCode::SetCode(const std::vector<uint8_t>* quick_code,
-                           const std::vector<uint8_t>* portable_code) {
+void CompiledCode::SetCode(const ArrayRef<const uint8_t>* quick_code,
+                           const ArrayRef<const uint8_t>* portable_code) {
   if (portable_code != nullptr) {
     CHECK(!portable_code->empty());
     portable_code_ = compiler_driver_->DeduplicateCode(*portable_code);
@@ -144,56 +142,93 @@
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver,
                                InstructionSet instruction_set,
-                               const std::vector<uint8_t>& quick_code,
+                               const ArrayRef<const uint8_t>& quick_code,
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
                                const uint32_t fp_spill_mask,
-                               const std::vector<uint8_t>& mapping_table,
-                               const std::vector<uint8_t>& vmap_table,
-                               const std::vector<uint8_t>& native_gc_map,
-                               const std::vector<uint8_t>* cfi_info)
+                               const ArrayRef<const uint8_t>& mapping_table,
+                               const ArrayRef<const uint8_t>& vmap_table,
+                               const ArrayRef<const uint8_t>& native_gc_map,
+                               const ArrayRef<const uint8_t>& cfi_info)
     : CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes),
       core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
-  mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
-  vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
-  gc_map_(driver->DeduplicateGCMap(native_gc_map)),
-  cfi_info_(driver->DeduplicateCFIInfo(cfi_info)) {
+      mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+      vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
+      gc_map_(driver->DeduplicateGCMap(native_gc_map)),
+      cfi_info_(cfi_info.data() == nullptr ? nullptr : driver->DeduplicateCFIInfo(cfi_info)) {
 }
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver,
                                InstructionSet instruction_set,
-                               const std::vector<uint8_t>& code,
+                               const ArrayRef<const uint8_t>& code,
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
                                const uint32_t fp_spill_mask)
     : CompiledCode(driver, instruction_set, code),
       frame_size_in_bytes_(frame_size_in_bytes),
       core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
-      mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
-      vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
-      gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
       cfi_info_(nullptr) {
+  mapping_table_ = driver->DeduplicateMappingTable(ArrayRef<const uint8_t>());
+  vmap_table_ = driver->DeduplicateVMapTable(ArrayRef<const uint8_t>());
+  gc_map_ = driver->DeduplicateGCMap(ArrayRef<const uint8_t>());
 }
 
 // Constructs a CompiledMethod for the Portable compiler.
 CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
-                               const std::string& code, const std::vector<uint8_t>& gc_map,
+                               const std::string& code, const ArrayRef<const uint8_t>& gc_map,
                                const std::string& symbol)
     : CompiledCode(driver, instruction_set, code, symbol),
       frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0), gc_map_(driver->DeduplicateGCMap(gc_map)) {
-  mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
-  vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
+      fp_spill_mask_(0), gc_map_(driver->DeduplicateGCMap(gc_map)),
+      cfi_info_(nullptr) {
+  mapping_table_ = driver->DeduplicateMappingTable(ArrayRef<const uint8_t>());
+  vmap_table_ = driver->DeduplicateVMapTable(ArrayRef<const uint8_t>());
 }
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
                                const std::string& code, const std::string& symbol)
     : CompiledCode(driver, instruction_set, code, symbol),
       frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0) {
-  mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
-  vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
-  gc_map_ = driver->DeduplicateGCMap(std::vector<uint8_t>());
+      fp_spill_mask_(0), cfi_info_(nullptr) {
+  mapping_table_ = driver->DeduplicateMappingTable(ArrayRef<const uint8_t>());
+  vmap_table_ = driver->DeduplicateVMapTable(ArrayRef<const uint8_t>());
+  gc_map_ = driver->DeduplicateGCMap(ArrayRef<const uint8_t>());
+}
+
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(CompilerDriver* driver,
+    InstructionSet instruction_set,
+    const ArrayRef<const uint8_t>& quick_code,
+    const size_t frame_size_in_bytes,
+    const uint32_t core_spill_mask,
+    const uint32_t fp_spill_mask,
+    const ArrayRef<const uint8_t>& mapping_table,
+    const ArrayRef<const uint8_t>& vmap_table,
+    const ArrayRef<const uint8_t>& native_gc_map,
+    const ArrayRef<const uint8_t>& cfi_info) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  CompiledMethod* ret = alloc.allocate(1);
+  alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+                  fp_spill_mask, mapping_table, vmap_table, native_gc_map, cfi_info);
+  return ret;
+}
+
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(CompilerDriver* driver,
+                                                        InstructionSet instruction_set,
+                                                        const ArrayRef<const uint8_t>& quick_code,
+                                                        const size_t frame_size_in_bytes,
+                                                        const uint32_t core_spill_mask,
+                                                        const uint32_t fp_spill_mask) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  CompiledMethod* ret = alloc.allocate(1);
+  alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+                  fp_spill_mask);
+  return ret;
+}
+
+void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  alloc.destroy(m);
+  alloc.deallocate(m, 1);
 }
 
 }  // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index b8cd851..2d95851 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -23,6 +23,8 @@
 
 #include "instruction_set.h"
 #include "utils.h"
+#include "utils/array_ref.h"
+#include "utils/swap_space.h"
 
 namespace llvm {
   class Function;
@@ -36,7 +38,7 @@
  public:
   // For Quick to supply an code blob
   CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-               const std::vector<uint8_t>& quick_code);
+               const ArrayRef<const uint8_t>& quick_code);
 
   // For Portable to supply an ELF object
   CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
@@ -46,15 +48,16 @@
     return instruction_set_;
   }
 
-  const std::vector<uint8_t>* GetPortableCode() const {
+  const SwapVector<uint8_t>* GetPortableCode() const {
     return portable_code_;
   }
 
-  const std::vector<uint8_t>* GetQuickCode() const {
+  const SwapVector<uint8_t>* GetQuickCode() const {
     return quick_code_;
   }
 
-  void SetCode(const std::vector<uint8_t>* quick_code, const std::vector<uint8_t>* portable_code);
+  void SetCode(const ArrayRef<const uint8_t>* quick_code,
+               const ArrayRef<const uint8_t>* portable_code);
 
   bool operator==(const CompiledCode& rhs) const;
 
@@ -85,10 +88,10 @@
   const InstructionSet instruction_set_;
 
   // The ELF image for portable.
-  std::vector<uint8_t>* portable_code_;
+  SwapVector<uint8_t>* portable_code_;
 
   // Used to store the PIC code for Quick.
-  std::vector<uint8_t>* quick_code_;
+  SwapVector<uint8_t>* quick_code_;
 
   // Used for the Portable ELF symbol name.
   const std::string symbol_;
@@ -105,26 +108,26 @@
   // Constructs a CompiledMethod for the non-LLVM compilers.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
-                 const std::vector<uint8_t>& quick_code,
+                 const ArrayRef<const uint8_t>& quick_code,
                  const size_t frame_size_in_bytes,
                  const uint32_t core_spill_mask,
                  const uint32_t fp_spill_mask,
-                 const std::vector<uint8_t>& mapping_table,
-                 const std::vector<uint8_t>& vmap_table,
-                 const std::vector<uint8_t>& native_gc_map,
-                 const std::vector<uint8_t>* cfi_info);
+                 const ArrayRef<const uint8_t>& mapping_table,
+                 const ArrayRef<const uint8_t>& vmap_table,
+                 const ArrayRef<const uint8_t>& native_gc_map,
+                 const ArrayRef<const uint8_t>& cfi_info);
 
   // Constructs a CompiledMethod for the QuickJniCompiler.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
-                 const std::vector<uint8_t>& quick_code,
+                 const ArrayRef<const uint8_t>& quick_code,
                  const size_t frame_size_in_bytes,
                  const uint32_t core_spill_mask,
                  const uint32_t fp_spill_mask);
 
   // Constructs a CompiledMethod for the Portable compiler.
   CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
-                 const std::vector<uint8_t>& gc_map, const std::string& symbol);
+                 const ArrayRef<const uint8_t>& gc_map, const std::string& symbol);
 
   // Constructs a CompiledMethod for the Portable JniCompiler.
   CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
@@ -132,6 +135,26 @@
 
   ~CompiledMethod() {}
 
+  static CompiledMethod* SwapAllocCompiledMethod(CompilerDriver* driver,
+                                                 InstructionSet instruction_set,
+                                                 const ArrayRef<const uint8_t>& quick_code,
+                                                 const size_t frame_size_in_bytes,
+                                                 const uint32_t core_spill_mask,
+                                                 const uint32_t fp_spill_mask,
+                                                 const ArrayRef<const uint8_t>& mapping_table,
+                                                 const ArrayRef<const uint8_t>& vmap_table,
+                                                 const ArrayRef<const uint8_t>& native_gc_map,
+                                                 const ArrayRef<const uint8_t>& cfi_info);
+
+  static CompiledMethod* SwapAllocCompiledMethod(CompilerDriver* driver,
+                                                 InstructionSet instruction_set,
+                                                 const ArrayRef<const uint8_t>& quick_code,
+                                                 const size_t frame_size_in_bytes,
+                                                 const uint32_t core_spill_mask,
+                                                 const uint32_t fp_spill_mask);
+
+  static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m);
+
   size_t GetFrameSizeInBytes() const {
     return frame_size_in_bytes_;
   }
@@ -144,22 +167,22 @@
     return fp_spill_mask_;
   }
 
-  const std::vector<uint8_t>& GetMappingTable() const {
+  const SwapVector<uint8_t>& GetMappingTable() const {
     DCHECK(mapping_table_ != nullptr);
     return *mapping_table_;
   }
 
-  const std::vector<uint8_t>& GetVmapTable() const {
+  const SwapVector<uint8_t>& GetVmapTable() const {
     DCHECK(vmap_table_ != nullptr);
     return *vmap_table_;
   }
 
-  const std::vector<uint8_t>& GetGcMap() const {
+  const SwapVector<uint8_t>& GetGcMap() const {
     DCHECK(gc_map_ != nullptr);
     return *gc_map_;
   }
 
-  const std::vector<uint8_t>* GetCFIInfo() const {
+  const SwapVector<uint8_t>* GetCFIInfo() const {
     return cfi_info_;
   }
 
@@ -172,14 +195,14 @@
   const uint32_t fp_spill_mask_;
   // For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to
   // native PC offset. Size prefixed.
-  std::vector<uint8_t>* mapping_table_;
+  SwapVector<uint8_t>* mapping_table_;
   // For quick code, a uleb128 encoded map from GPR/FPR register to dex register. Size prefixed.
-  std::vector<uint8_t>* vmap_table_;
+  SwapVector<uint8_t>* vmap_table_;
   // For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers
   // are live. For portable code, the key is a dalvik PC.
-  std::vector<uint8_t>* gc_map_;
+  SwapVector<uint8_t>* gc_map_;
   // For quick code, a FDE entry for the debug_frame section.
-  std::vector<uint8_t>* cfi_info_;
+  SwapVector<uint8_t>* cfi_info_;
 };
 
 }  // namespace art
diff --git a/compiler/compilers.cc b/compiler/compilers.cc
index 250924a..2481128 100644
--- a/compiler/compilers.cc
+++ b/compiler/compilers.cc
@@ -84,7 +84,9 @@
 }
 
 uintptr_t QuickCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
-  return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode());
+  size_t pointer_size = InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet());
+  return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize(
+      pointer_size));
 }
 
 bool QuickCompiler::WriteElf(art::File* file,
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index 7395324..2769b4b 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -33,16 +33,16 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->DoCacheFieldLoweringInfo();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->DoCacheFieldLoweringInfo();
   }
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->HasFieldAccess();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->HasFieldAccess();
   }
 };
 
@@ -57,16 +57,16 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->DoCacheMethodLoweringInfo();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->DoCacheMethodLoweringInfo();
   }
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->HasInvokes();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->HasInvokes();
   }
 };
 
@@ -83,35 +83,35 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->InlineSpecialMethodsGate();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->InlineSpecialMethodsGate();
   }
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->InlineSpecialMethodsStart();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->InlineSpecialMethodsStart();
   }
 
   bool Worker(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
     const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    cUnit->mir_graph->InlineSpecialMethods(bb);
+    c_unit->mir_graph->InlineSpecialMethods(bb);
     // No need of repeating, so just return false.
     return false;
   }
 
   void End(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->InlineSpecialMethodsEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->InlineSpecialMethodsEnd();
   }
 };
 
@@ -126,9 +126,10 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->VerifyDataflow();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->VerifyDataflow();
+    c_unit->mir_graph->ClearAllVisitedFlags();
   }
 
   bool Worker(const PassDataHolder* data) const;
@@ -146,26 +147,26 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->EliminateNullChecksAndInferTypesStart();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->EliminateNullChecksAndInferTypesStart();
   }
 
   bool Worker(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
     const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    return cUnit->mir_graph->EliminateNullChecksAndInferTypes(bb);
+    return c_unit->mir_graph->EliminateNullChecksAndInferTypes(bb);
   }
 
   void End(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->EliminateNullChecksAndInferTypesEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->EliminateNullChecksAndInferTypesEnd();
   }
 };
 
@@ -177,26 +178,26 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->EliminateClassInitChecksGate();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->EliminateClassInitChecksGate();
   }
 
   bool Worker(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
     const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    return cUnit->mir_graph->EliminateClassInitChecks(bb);
+    return c_unit->mir_graph->EliminateClassInitChecks(bb);
   }
 
   void End(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->EliminateClassInitChecksEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->EliminateClassInitChecksEnd();
   }
 };
 
@@ -212,26 +213,26 @@
 
   bool Gate(const PassDataHolder* data) const OVERRIDE {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->ApplyGlobalValueNumberingGate();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->ApplyGlobalValueNumberingGate();
   }
 
   bool Worker(const PassDataHolder* data) const OVERRIDE {
     DCHECK(data != nullptr);
     const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    return cUnit->mir_graph->ApplyGlobalValueNumbering(bb);
+    return c_unit->mir_graph->ApplyGlobalValueNumbering(bb);
   }
 
   void End(PassDataHolder* data) const OVERRIDE {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->ApplyGlobalValueNumberingEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->ApplyGlobalValueNumberingEnd();
   }
 };
 
@@ -246,9 +247,9 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return ((cUnit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
   }
 
   bool Worker(const PassDataHolder* data) const;
@@ -265,9 +266,9 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return ((cUnit->disable_opt & (1 << kBBOpt)) == 0);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return ((c_unit->disable_opt & (1 << kBBOpt)) == 0);
   }
 
   void Start(PassDataHolder* data) const;
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 6aee563..2aafb74 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -762,8 +762,10 @@
       } else {
         DCHECK(cur_block->fall_through == NullBasicBlockId);
         DCHECK(cur_block->taken == NullBasicBlockId);
-        // Unreachable instruction, mark for no continuation.
+        // Unreachable instruction, mark for no continuation and end basic block.
         flags &= ~Instruction::kContinue;
+        FindBlock(current_offset_ + width, /* split */ false, /* create */ true,
+                  /* immed_pred_block_p */ NULL);
       }
     } else {
       cur_block->AppendMIR(insn);
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 23ceb56..a322fa9 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -596,26 +596,40 @@
   }
 }
 
-/* Try to make common case the fallthrough path */
+/* Try to make common case the fallthrough path. */
 bool MIRGraph::LayoutBlocks(BasicBlock* bb) {
-  // TODO: For now, just looking for direct throws.  Consider generalizing for profile feedback
+  // TODO: For now, just looking for direct throws.  Consider generalizing for profile feedback.
   if (!bb->explicit_throw) {
     return false;
   }
+
+  // If we visited it, we are done.
+  if (bb->visited) {
+    return false;
+  }
+  bb->visited = true;
+
   BasicBlock* walker = bb;
   while (true) {
-    // Check termination conditions
+    // Check termination conditions.
     if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) {
       break;
     }
     BasicBlock* prev = GetBasicBlock(walker->predecessors->Get(0));
+
+    // If we visited the predecessor, we are done.
+    if (prev->visited) {
+      return false;
+    }
+    prev->visited = true;
+
     if (prev->conditional_branch) {
       if (GetBasicBlock(prev->fall_through) == walker) {
-        // Already done - return
+        // Already done - return.
         break;
       }
       DCHECK_EQ(walker, GetBasicBlock(prev->taken));
-      // Got one.  Flip it and exit
+      // Got one.  Flip it and exit.
       Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode;
       switch (opcode) {
         case Instruction::IF_EQ: opcode = Instruction::IF_NE; break;
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 0de2a44..b299d9b 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -550,6 +550,14 @@
 
 // Try to convert *lit to 1 RegRegRegShift/RegRegShift form.
 bool ArmMir2Lir::GetEasyMultiplyOp(int lit, ArmMir2Lir::EasyMultiplyOp* op) {
+  if (lit == 0) {
+    // Special case for *divide-by-zero*. The ops won't actually be used to generate code, as
+    // GenArithOpIntLit will directly generate exception-throwing code, and multiply-by-zero will
+    // have been optimized away earlier.
+    op->op = kOpInvalid;
+    return true;
+  }
+
   if (IsPowerOfTwo(lit)) {
     op->op = kOpLsl;
     op->shift = LowestSetBit(lit);
@@ -1142,6 +1150,7 @@
   // Check for destructive overlap
   if (rl_result.reg.GetLowReg() == rl_src.reg.GetHighReg()) {
     RegStorage t_reg = AllocTemp();
+    OpRegCopy(t_reg, rl_result.reg.GetLow());
     OpRegRegReg(kOpSub, rl_result.reg.GetLow(), z_reg, rl_src.reg.GetLow());
     OpRegRegReg(kOpSbc, rl_result.reg.GetHigh(), z_reg, t_reg);
     FreeTemp(t_reg);
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 0509ad3..c853c49 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -718,6 +718,7 @@
   FreeTemp(rs_r1);
   FreeTemp(rs_r2);
   FreeTemp(rs_r3);
+  FreeTemp(TargetReg(kHiddenArg));
 }
 
 RegStorage ArmMir2Lir::LoadHelper(QuickEntrypointEnum trampoline) {
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index bba1a8c..a3b099c 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -978,9 +978,8 @@
     // Use LDREXD for the atomic load. (Expect displacement > 0, don't optimize for == 0.)
     RegStorage r_ptr = AllocTemp();
     OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
-    LIR* lir = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
+    load = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
     FreeTemp(r_ptr);
-    return lir;
   } else {
     load = LoadBaseDispBody(r_base, displacement, r_dest, size);
   }
@@ -1092,7 +1091,7 @@
     GenMemBarrier(kAnyStore);
   }
 
-  LIR* store;
+  LIR* null_ck_insn;
   if (UNLIKELY(is_volatile == kVolatile &&
                (size == k64 || size == kDouble) &&
                !cu_->compiler_driver->GetInstructionSetFeatures().HasLpae())) {
@@ -1109,17 +1108,16 @@
     RegStorage r_temp = AllocTemp();
     RegStorage r_temp_high = AllocTemp(false);  // We may not have another temp.
     if (r_temp_high.Valid()) {
-      NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
+      null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
       FreeTemp(r_temp_high);
       FreeTemp(r_temp);
     } else {
       // If we don't have another temp, clobber r_ptr in LDREXD and reload it.
-      NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
+      null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
       FreeTemp(r_temp);  // May need the temp for kOpAdd.
       OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
     }
-    store = NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(),
-                    r_ptr.GetReg());
+    NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(), r_ptr.GetReg());
     OpCmpImmBranch(kCondNe, r_temp, 0, fail_target);
     FreeTemp(r_ptr);
   } else {
@@ -1128,7 +1126,7 @@
       size = k32;
     }
 
-    store = StoreBaseDispBody(r_base, displacement, r_src, size);
+    null_ck_insn = StoreBaseDispBody(r_base, displacement, r_src, size);
   }
 
   if (UNLIKELY(is_volatile == kVolatile)) {
@@ -1137,7 +1135,7 @@
     GenMemBarrier(kAnyAny);
   }
 
-  return store;
+  return null_ck_insn;
 }
 
 LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 9b4546a..16a1078 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -754,6 +754,7 @@
   FreeTemp(rs_f5);
   FreeTemp(rs_f6);
   FreeTemp(rs_f7);
+  FreeTemp(TargetReg(kHiddenArg));
 }
 
 RegStorage Arm64Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline) {
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index ee1c467..cb5fbb3 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -509,6 +509,20 @@
     PushPointer(code_buffer_, &target_method_id, cu_->target64);
     data_lir = NEXT_LIR(data_lir);
   }
+  // Push the string literals.
+  data_lir = string_literal_list_;
+  while (data_lir != nullptr) {
+    uint32_t string_idx = data_lir->operands[0];
+    cu_->compiler_driver->AddStringPatch(cu_->dex_file,
+                                         cu_->class_def_idx,
+                                         cu_->method_idx,
+                                         string_idx,
+                                         code_buffer_.size());
+    const auto& target_string_id = cu_->dex_file->GetStringId(string_idx);
+    // unique value based on target to ensure code deduplication works
+    PushPointer(code_buffer_, &target_string_id, cu_->target64);
+    data_lir = NEXT_LIR(data_lir);
+  }
 }
 
 /* Write the switch tables to the output stream */
@@ -759,6 +773,10 @@
         ": " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
     native_gc_map_builder.AddEntry(native_offset, references);
   }
+
+  // Maybe not necessary, but this could help prevent errors where we access the verified method
+  // after it has been deleted.
+  mir_graph_->GetCurrentDexCompilationUnit()->ClearVerifiedMethod();
 }
 
 /* Determine the offset of each literal field */
@@ -768,6 +786,7 @@
   offset = AssignLiteralPointerOffsetCommon(code_literal_list_, offset, ptr_size);
   offset = AssignLiteralPointerOffsetCommon(method_literal_list_, offset, ptr_size);
   offset = AssignLiteralPointerOffsetCommon(class_literal_list_, offset, ptr_size);
+  offset = AssignLiteralPointerOffsetCommon(string_literal_list_, offset, ptr_size);
   return offset;
 }
 
@@ -973,6 +992,7 @@
       literal_list_(NULL),
       method_literal_list_(NULL),
       class_literal_list_(NULL),
+      string_literal_list_(NULL),
       code_literal_list_(NULL),
       first_fixup_(NULL),
       cu_(cu),
@@ -1078,11 +1098,18 @@
   }
 
   std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnCallFrameInformation());
-  CompiledMethod* result =
-      new CompiledMethod(cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
-                         core_spill_mask_, fp_spill_mask_, encoded_mapping_table_,
-                         vmap_encoder.GetData(), native_gc_map_, cfi_info.get());
-  return result;
+  ArrayRef<const uint8_t> cfi_ref;
+  if (cfi_info.get() != nullptr) {
+    cfi_ref = ArrayRef<const uint8_t>(*cfi_info);
+  }
+  return CompiledMethod::SwapAllocCompiledMethod(
+      cu_->compiler_driver, cu_->instruction_set,
+      ArrayRef<const uint8_t>(code_buffer_),
+      frame_size_, core_spill_mask_, fp_spill_mask_,
+      ArrayRef<const uint8_t>(encoded_mapping_table_),
+      ArrayRef<const uint8_t>(vmap_encoder.GetData()),
+      ArrayRef<const uint8_t>(native_gc_map_),
+      cfi_ref);
 }
 
 size_t Mir2Lir::GetMaxPossibleCompilerTemps() const {
@@ -1240,6 +1267,17 @@
   AppendLIR(load_pc_rel);
 }
 
+void Mir2Lir::LoadString(uint32_t string_idx, RegStorage target_reg) {
+  // Use the literal pool and a PC-relative load from a data word.
+  LIR* data_target = ScanLiteralPool(string_literal_list_, string_idx, 0);
+  if (data_target == nullptr) {
+    data_target = AddWordData(&string_literal_list_, string_idx);
+  }
+  // Loads a Class pointer, which is a reference as it lives in the heap.
+  LIR* load_pc_rel = OpPcRelLoad(target_reg, data_target);
+  AppendLIR(load_pc_rel);
+}
+
 std::vector<uint8_t>* Mir2Lir::ReturnCallFrameInformation() {
   // Default case is to do nothing.
   return nullptr;
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index f6c77fc..d451401 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -160,6 +160,10 @@
   if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
     return GenExplicitNullCheck(m_reg, opt_flags);
   }
+  // If null check has not been eliminated, reset redundant store tracking.
+  if ((opt_flags & MIR_IGNORE_NULL_CHECK) == 0) {
+    ResetDefTracking();
+  }
   return nullptr;
 }
 
@@ -781,15 +785,15 @@
     }
     GenNullCheck(rl_obj.reg, opt_flags);
     int field_offset = field_info.FieldOffset().Int32Value();
-    LIR* store;
+    LIR* null_ck_insn;
     if (is_object) {
-      store = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
+      null_ck_insn = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
           kVolatile : kNotVolatile);
     } else {
-      store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, store_size,
-                            field_info.IsVolatile() ? kVolatile : kNotVolatile);
+      null_ck_insn = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, store_size,
+                                   field_info.IsVolatile() ? kVolatile : kNotVolatile);
     }
-    MarkPossibleNullPointerExceptionAfter(opt_flags, store);
+    MarkPossibleNullPointerExceptionAfter(opt_flags, null_ck_insn);
     if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) {
       MarkGCCard(rl_src.reg, rl_obj.reg);
     }
@@ -877,8 +881,8 @@
 
 void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
   /* NOTE: Most strings should be available at compile time */
-  int32_t offset_of_string = mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx).
-                                                                                      Int32Value();
+  const int32_t offset_of_string =
+      mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx).Int32Value();
   if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
       *cu_->dex_file, string_idx) || SLOW_STRING_PATH) {
     // slow path, resolve string if not in dex cache
@@ -896,7 +900,11 @@
       r_method = TargetReg(kArg2, kRef);
       LoadCurrMethodDirect(r_method);
     }
-    LoadRefDisp(r_method, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(),
+    // Method to declaring class.
+    LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+                TargetReg(kArg0, kRef), kNotVolatile);
+    // Declaring class to dex cache strings.
+    LoadRefDisp(TargetReg(kArg0, kRef), mirror::Class::DexCacheStringsOffset().Int32Value(),
                 TargetReg(kArg0, kRef), kNotVolatile);
 
     // Might call out to helper, which will return resolved string in kRet0
@@ -930,13 +938,38 @@
     GenBarrier();
     StoreValue(rl_dest, GetReturn(kRefReg));
   } else {
-    RegLocation rl_method = LoadCurrMethod();
-    RegStorage res_reg = AllocTempRef();
-    RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
-    LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), res_reg,
-                kNotVolatile);
-    LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
-    StoreValue(rl_dest, rl_result);
+    // Try to see if we can embed a direct pointer.
+    bool use_direct_ptr = false;
+    size_t direct_ptr = 0;
+    bool embed_string = false;
+    // TODO: Implement for X86.
+    if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) {
+      embed_string = cu_->compiler_driver->CanEmbedStringInCode(*cu_->dex_file, string_idx,
+                                                                &use_direct_ptr, &direct_ptr);
+    }
+    if (embed_string) {
+      RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
+      if (!use_direct_ptr) {
+        LoadString(string_idx, rl_result.reg);
+      } else {
+        LoadConstant(rl_result.reg, static_cast<int32_t>(direct_ptr));
+      }
+      StoreValue(rl_dest, rl_result);
+    } else {
+      RegLocation rl_method = LoadCurrMethod();
+      RegStorage res_reg = AllocTempRef();
+      RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
+
+      // Method to declaring class.
+      LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+                  res_reg, kNotVolatile);
+      // Declaring class to dex cache strings.
+      LoadRefDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
+                  kNotVolatile);
+
+      LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
+      StoreValue(rl_dest, rl_result);
+    }
   }
 }
 
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 7958886..b30ed9e 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -477,9 +477,10 @@
                                                        const RegStorage* alt_from,
                                                        const CompilationUnit* cu, Mir2Lir* cg) {
   if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+    int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+        InstructionSetPointerSize(cu->instruction_set)).Int32Value();
     // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt]
-    cg->LoadWordDisp(alt_from == nullptr ? cg->TargetReg(kArg0, kRef) : *alt_from,
-                     mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
+    cg->LoadWordDisp(alt_from == nullptr ? cg->TargetReg(kArg0, kRef) : *alt_from, offset,
                      cg->TargetPtrReg(kInvokeTgt));
     return true;
   }
@@ -1802,8 +1803,9 @@
         call_inst =
           reinterpret_cast<X86Mir2Lir*>(this)->CallWithLinkerFixup(target_method, info->type);
       } else {
-        call_inst = OpMem(kOpBlx, TargetReg(kArg0, kRef),
-                          mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+        int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+            InstructionSetPointerSize(cu_->instruction_set)).Int32Value();
+        call_inst = OpMem(kOpBlx, TargetReg(kArg0, kRef), offset);
       }
     } else {
       call_inst = GenInvokeNoInlineCall(this, info->type);
@@ -1812,7 +1814,7 @@
   EndInvoke(info);
   MarkSafepointPC(call_inst);
 
-  ClobberCallerSave();
+  FreeCallTemps();
   if (info->result.location != kLocInvalid) {
     // We have a following MOVE_RESULT - do it now.
     if (info->result.wide) {
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index bc91fbcd..94adab9 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -417,6 +417,7 @@
   FreeTemp(rs_rMIPS_ARG1);
   FreeTemp(rs_rMIPS_ARG2);
   FreeTemp(rs_rMIPS_ARG3);
+  FreeTemp(TargetReg(kHiddenArg));
 }
 
 bool MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index e519011..6e0fe02 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -200,6 +200,16 @@
     RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
     RegStorage reg_arg_high = GetArgMappingToPhysicalReg(in_position + 1);
 
+    if (cu_->instruction_set == kX86) {
+      // Can't handle double split between reg & memory.  Flush reg half to memory.
+      if (rl_dest.reg.IsDouble() && (reg_arg_low.Valid() != reg_arg_high.Valid())) {
+        DCHECK(reg_arg_low.Valid());
+        DCHECK(!reg_arg_high.Valid());
+        Store32Disp(TargetPtrReg(kSp), offset, reg_arg_low);
+        reg_arg_low = RegStorage::InvalidReg();
+      }
+    }
+
     if (reg_arg_low.Valid() && reg_arg_high.Valid()) {
       OpRegCopyWide(rl_dest.reg, RegStorage::MakeRegPair(reg_arg_low, reg_arg_high));
     } else if (reg_arg_low.Valid() && !reg_arg_high.Valid()) {
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 3dc111f..bfd7860 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1119,6 +1119,9 @@
      */
     virtual void LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg);
 
+    // Load a string
+    virtual void LoadString(uint32_t string_idx, RegStorage target_reg);
+
     // Routines that work for the generic case, but may be overriden by target.
     /*
      * @brief Compare memory to immediate, and branch if condition true.
@@ -1677,6 +1680,7 @@
     LIR* literal_list_;                        // Constants.
     LIR* method_literal_list_;                 // Method literals requiring patching.
     LIR* class_literal_list_;                  // Class literals requiring patching.
+    LIR* string_literal_list_;                 // String literals requiring patching.
     LIR* code_literal_list_;                   // Code literals requiring patching.
     LIR* first_fixup_;                         // Doubly-linked list of LIR nodes requiring fixups.
 
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 00a2621..baa72a0 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -1661,7 +1661,16 @@
     AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2,
                             false /* is_load */, true /* is64bit */);
   }
-  FreeTemp(rl_src.reg);
+
+  int v_src_reg = mir_graph_->SRegToVReg(rl_src.s_reg_low);
+  int v_dst_reg = mir_graph_->SRegToVReg(rl_dest.s_reg_low);
+
+  // If the left operand is in memory and the right operand is in a register
+  // and both belong to the same dalvik register then we should clobber the
+  // right one because it doesn't hold valid data anymore.
+  if (v_src_reg == v_dst_reg) {
+    Clobber(rl_src.reg);
+  }
 }
 
 void X86Mir2Lir::GenLongArith(RegLocation rl_dest, RegLocation rl_src1,
@@ -2003,13 +2012,6 @@
     OpRegReg(kOpNeg, rl_result.reg, rl_src.reg);
   } else {
     rl_result = ForceTempWide(rl_src);
-    if (((rl_dest.location == kLocPhysReg) && (rl_src.location == kLocPhysReg)) &&
-        ((rl_dest.reg.GetLowReg() == rl_src.reg.GetHighReg()))) {
-      // The registers are the same, so we would clobber it before the use.
-      RegStorage temp_reg = AllocTemp();
-      OpRegCopy(temp_reg, rl_result.reg);
-      rl_result.reg.SetHighReg(temp_reg.GetReg());
-    }
     OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_result.reg.GetLow());    // rLow = -rLow
     OpRegImm(kOpAdc, rl_result.reg.GetHigh(), 0);                   // rHigh = rHigh + CF
     OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_result.reg.GetHigh());  // rHigh = -rHigh
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index f7cb820..873db7e 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -539,6 +539,7 @@
   FreeTemp(rs_rX86_ARG1);
   FreeTemp(rs_rX86_ARG2);
   FreeTemp(rs_rX86_ARG3);
+  FreeTemp(TargetReg32(kHiddenArg));
   if (cu_->target64) {
     FreeTemp(rs_rX86_ARG4);
     FreeTemp(rs_rX86_ARG5);
@@ -2853,7 +2854,7 @@
     if (rl_idx.is_const) {
       LIR* comparison;
       range_check_branch = OpCmpMemImmBranch(
-          kCondUlt, RegStorage::InvalidReg(), rl_obj.reg, count_offset,
+          kCondLs, RegStorage::InvalidReg(), rl_obj.reg, count_offset,
           mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr, &comparison);
       MarkPossibleNullPointerExceptionAfter(0, comparison);
     } else {
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index a8e6b3c..2db2be7 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -57,8 +57,8 @@
 
   const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
   if (verified_method == nullptr) {
-    DCHECK(method_verifier->HasFailures());
-    return false;
+    // Do not report an error to the verifier. We'll just punt this later.
+    return true;
   }
 
   WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
@@ -84,6 +84,15 @@
   return (it != verified_methods_.end()) ? it->second : nullptr;
 }
 
+void VerificationResults::RemoveVerifiedMethod(MethodReference ref) {
+  WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+  auto it = verified_methods_.find(ref);
+  if (it != verified_methods_.end()) {
+    delete it->second;
+    verified_methods_.erase(it);
+  }
+}
+
 void VerificationResults::AddRejectedClass(ClassReference ref) {
   {
     WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 0e7923f..7fc2a23 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -48,6 +48,7 @@
 
     const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
         LOCKS_EXCLUDED(verified_methods_lock_);
+    void RemoveVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_);
 
     void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
     bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 1b3f2a1..e7160d9 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -49,7 +49,6 @@
   if (compile) {
     /* Generate a register map. */
     if (!verified_method->GenerateGcMap(method_verifier)) {
-      CHECK(method_verifier->HasFailures());
       return nullptr;  // Not a real failure, but a failure to encode.
     }
     if (kIsDebugBuild) {
@@ -83,17 +82,17 @@
   ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits);
   // There's a single byte to encode the size of each bitmap.
   if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
-    // TODO: either a better GC map format or per method failures
-    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers";
+    LOG(WARNING) << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers: "
+                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+                                 *method_verifier->GetMethodReference().dex_file);
     return false;
   }
   size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
   // There are 2 bytes to encode the number of entries.
   if (num_entries >= 65536) {
-    // TODO: Either a better GC map format or per method failures.
-    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Cannot encode GC map for method with " << num_entries << " entries";
+    LOG(WARNING) << "Cannot encode GC map for method with " << num_entries << " entries: "
+                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+                                 *method_verifier->GetMethodReference().dex_file);
     return false;
   }
   size_t pc_bytes;
@@ -105,10 +104,10 @@
     format = verifier::kRegMapFormatCompact16;
     pc_bytes = 2;
   } else {
-    // TODO: Either a better GC map format or per method failures.
-    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Cannot encode GC map for method with "
-        << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
+    LOG(WARNING) << "Cannot encode GC map for method with "
+                 << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2): "
+                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+                                 *method_verifier->GetMethodReference().dex_file);
     return false;
   }
   size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
@@ -161,7 +160,7 @@
         }
       }
     } else {
-      DCHECK(reg_bitmap == NULL);
+      DCHECK(i >= 65536 || reg_bitmap == NULL);
     }
   }
 }
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index d278884..4e51b0e 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -232,7 +232,8 @@
   bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) &&
       (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) &&
       resolved_method->GetMethodIndex() < methods_class->GetVTableLength() &&
-      (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method);
+      (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method) &&
+      !resolved_method->IsAbstract();
 
   if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) {
     // Sharpen a virtual call into a direct call. The method_idx is into referrer's
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 62d70e9..b99aca1 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -22,6 +22,10 @@
 #include <vector>
 #include <unistd.h>
 
+#ifndef __APPLE__
+#include <malloc.h>  // For mallinfo
+#endif
+
 #include "base/stl_util.h"
 #include "base/timing_logger.h"
 #include "class_linker.h"
@@ -56,6 +60,7 @@
 #include "thread_pool.h"
 #include "trampolines/trampoline_compiler.h"
 #include "transaction.h"
+#include "utils/swap_space.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 
@@ -333,8 +338,10 @@
                                bool image, std::set<std::string>* image_classes,
                                std::set<std::string>* compiled_classes, size_t thread_count,
                                bool dump_stats, bool dump_passes, CumulativeLogger* timer,
-                               std::string profile_file)
-    : profile_present_(false), compiler_options_(compiler_options),
+                               int swap_fd, std::string profile_file)
+    : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
+      swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())),
+      profile_present_(false), compiler_options_(compiler_options),
       verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
       compiler_(Compiler::Create(this, compiler_kind)),
@@ -343,6 +350,7 @@
       freezing_constructor_lock_("freezing constructor lock"),
       compiled_classes_lock_("compiled classes lock"),
       compiled_methods_lock_("compiled method lock"),
+      compiled_methods_(MethodTable::key_compare()),
       image_(image),
       image_classes_(image_classes),
       classes_to_compile_(compiled_classes),
@@ -358,11 +366,12 @@
       compiler_get_method_code_addr_(nullptr),
       support_boot_image_fixup_(instruction_set != kMips),
       cfi_info_(nullptr),
-      dedupe_code_("dedupe code"),
-      dedupe_mapping_table_("dedupe mapping table"),
-      dedupe_vmap_table_("dedupe vmap table"),
-      dedupe_gc_map_("dedupe gc map"),
-      dedupe_cfi_info_("dedupe cfi info") {
+      // Use actual deduping only if we don't use swap.
+      dedupe_code_("dedupe code", *swap_space_allocator_),
+      dedupe_mapping_table_("dedupe mapping table", *swap_space_allocator_),
+      dedupe_vmap_table_("dedupe vmap table", *swap_space_allocator_),
+      dedupe_gc_map_("dedupe gc map", *swap_space_allocator_),
+      dedupe_cfi_info_("dedupe cfi info", *swap_space_allocator_) {
   DCHECK(compiler_options_ != nullptr);
   DCHECK(verification_results_ != nullptr);
   DCHECK(method_inliner_map_ != nullptr);
@@ -396,27 +405,24 @@
   }
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCode(const ArrayRef<const uint8_t>& code) {
   return dedupe_code_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const ArrayRef<const uint8_t>& code) {
   return dedupe_mapping_table_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const ArrayRef<const uint8_t>& code) {
   return dedupe_vmap_table_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateGCMap(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateGCMap(const ArrayRef<const uint8_t>& code) {
   return dedupe_gc_map_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info) {
-  if (cfi_info == nullptr) {
-    return nullptr;
-  }
-  return dedupe_cfi_info_.Add(Thread::Current(), *cfi_info);
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info) {
+  return dedupe_cfi_info_.Add(Thread::Current(), cfi_info);
 }
 
 CompilerDriver::~CompilerDriver() {
@@ -424,22 +430,14 @@
   {
     MutexLock mu(self, compiled_classes_lock_);
     STLDeleteValues(&compiled_classes_);
-  }
-  {
-    MutexLock mu(self, compiled_methods_lock_);
-    STLDeleteValues(&compiled_methods_);
-  }
-  {
-    MutexLock mu(self, compiled_methods_lock_);
     STLDeleteElements(&code_to_patch_);
-  }
-  {
-    MutexLock mu(self, compiled_methods_lock_);
     STLDeleteElements(&methods_to_patch_);
-  }
-  {
-    MutexLock mu(self, compiled_methods_lock_);
     STLDeleteElements(&classes_to_patch_);
+    STLDeleteElements(&strings_to_patch_);
+
+    for (auto& pair : compiled_methods_) {
+      CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, pair.second);
+    }
   }
   CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key");
   compiler_->UnInit();
@@ -510,6 +508,7 @@
                                 TimingLogger* timings) {
   DCHECK(!Runtime::Current()->IsStarted());
   std::unique_ptr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
+  VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
   PreCompile(class_loader, dex_files, thread_pool.get(), timings);
   Compile(class_loader, dex_files, thread_pool.get(), timings);
   if (dump_stats_) {
@@ -606,20 +605,25 @@
 void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                                 ThreadPool* thread_pool, TimingLogger* timings) {
   LoadImageClasses(timings);
+  VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
   Resolve(class_loader, dex_files, thread_pool, timings);
+  VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
 
   if (!compiler_options_->IsVerificationEnabled()) {
-    LOG(INFO) << "Verify none mode specified, skipping verification.";
+    VLOG(compiler) << "Verify none mode specified, skipping verification.";
     SetVerified(class_loader, dex_files, thread_pool, timings);
     return;
   }
 
   Verify(class_loader, dex_files, thread_pool, timings);
+  VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
 
   InitializeClasses(class_loader, dex_files, thread_pool, timings);
+  VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
 
   UpdateImageClasses(timings);
+  VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
 }
 
 bool CompilerDriver::IsImageClass(const char* descriptor) const {
@@ -941,16 +945,16 @@
 bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx,
                                         bool* is_type_initialized, bool* use_direct_type_ptr,
                                         uintptr_t* direct_type_ptr, bool* out_is_finalizable) {
+  if (GetCompilerOptions().GetCompilePic()) {
+    // Do not allow a direct class pointer to be used when compiling for position-independent
+    return false;
+  }
   ScopedObjectAccess soa(Thread::Current());
   mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
   mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == nullptr) {
     return false;
   }
-  if (GetCompilerOptions().GetCompilePic()) {
-    // Do not allow a direct class pointer to be used when compiling for position-independent
-    return false;
-  }
   *out_is_finalizable = resolved_class->IsFinalizable();
   const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
   const bool support_boot_image_fixup = GetSupportBootImageFixup();
@@ -989,6 +993,51 @@
   }
 }
 
+bool CompilerDriver::CanEmbedStringInCode(const DexFile& dex_file, uint32_t string_idx,
+                                          bool* use_direct_type_ptr, uintptr_t* direct_type_ptr) {
+  if (GetCompilerOptions().GetCompilePic()) {
+    // Do not allow a direct class pointer to be used when compiling for position-independent
+    return false;
+  }
+  ScopedObjectAccess soa(Thread::Current());
+  mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
+  mirror::String* resolved_string = dex_cache->GetResolvedString(string_idx);
+  if (resolved_string == nullptr) {
+    return false;
+  }
+  const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
+  const bool support_boot_image_fixup = GetSupportBootImageFixup();
+  if (compiling_boot) {
+    // boot -> boot class pointers.
+    // True if the class is in the image at boot compiling time.
+    const bool is_image_string = IsImage();
+    // True if pc relative load works.
+    if (is_image_string && support_boot_image_fixup) {
+      *use_direct_type_ptr = false;
+      *direct_type_ptr = 0;
+      return true;
+    }
+    return false;
+  } else {
+    // True if the class is in the image at app compiling time.
+    const bool obj_in_image =
+        false && Runtime::Current()->GetHeap()->FindSpaceFromObject(resolved_string, false)->IsImageSpace();
+    if (obj_in_image && support_boot_image_fixup) {
+      // boot -> app class pointers.
+      // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
+      *use_direct_type_ptr = !GetCompilerOptions().GetIncludePatchInformation();
+      *direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_string);
+      return true;
+    }
+
+    // app -> app class pointers.
+    // Give up because app does not have an image and class
+    // isn't created at compile time.  TODO: implement this
+    // if/when each app gets an image.
+    return false;
+  }
+}
+
 void CompilerDriver::ProcessedInstanceField(bool resolved) {
   if (!resolved) {
     stats_->UnresolvedInstanceField();
@@ -1135,14 +1184,15 @@
     // TODO: support patching on all architectures.
     use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
   }
-  bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
+  mirror::Class* declaring_class = method->GetDeclaringClass();
+  bool method_code_in_boot = (declaring_class->GetClassLoader() == nullptr);
   if (!use_dex_cache) {
     if (!method_code_in_boot) {
       use_dex_cache = true;
     } else {
       bool has_clinit_trampoline =
-          method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
-      if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
+          method->IsStatic() && !declaring_class->IsInitialized();
+      if (has_clinit_trampoline && (declaring_class != referrer_class)) {
         // Ensure we run the clinit trampoline unless we are invoking a static method in the same
         // class.
         use_dex_cache = true;
@@ -1153,7 +1203,15 @@
     *stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
   }
   if (!use_dex_cache && force_relocations) {
-    if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
+    bool is_in_image;
+    if (IsImage()) {
+      is_in_image = IsImageClass(method->GetDeclaringClassDescriptor());
+    } else {
+      is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 &&
+                    Runtime::Current()->GetHeap()->FindSpaceFromObject(declaring_class,
+                                                                       false)->IsImageSpace();
+    }
+    if (!is_in_image) {
       // We can only branch directly to Methods that are resolved in the DexCache.
       // Otherwise we won't invoke the resolution trampoline.
       use_dex_cache = true;
@@ -1162,7 +1220,7 @@
   // The method is defined not within this dex file. We need a dex cache slot within the current
   // dex file or direct pointers.
   bool must_use_direct_pointers = false;
-  if (target_method->dex_file == method->GetDeclaringClass()->GetDexCache()->GetDexFile()) {
+  if (target_method->dex_file == declaring_class->GetDexCache()->GetDexFile()) {
     target_method->dex_method_index = method->GetDexMethodIndex();
   } else {
     if (no_guarantee_of_dex_cache_entry) {
@@ -1176,7 +1234,7 @@
       } else {
         if (force_relocations && !use_dex_cache) {
           target_method->dex_method_index = method->GetDexMethodIndex();
-          target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+          target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
         }
         must_use_direct_pointers = true;
       }
@@ -1199,14 +1257,14 @@
       *type = sharp_type;
       *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
       *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
-      target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+      target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
       target_method->dex_method_index = method->GetDexMethodIndex();
     } else if (!must_use_direct_pointers) {
       // Set the code and rely on the dex cache for the method.
       *type = sharp_type;
       if (force_relocations) {
         *direct_code = -1;
-        target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+        target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
         target_method->dex_method_index = method->GetDexMethodIndex();
       } else {
         *direct_code = compiler_->GetEntryPointOf(method);
@@ -1372,6 +1430,18 @@
                                                        target_type_idx,
                                                        literal_offset));
 }
+void CompilerDriver::AddStringPatch(const DexFile* dex_file,
+                                    uint16_t referrer_class_def_idx,
+                                    uint32_t referrer_method_idx,
+                                    uint32_t string_idx,
+                                    size_t literal_offset) {
+  MutexLock mu(Thread::Current(), compiled_methods_lock_);
+  strings_to_patch_.push_back(new StringPatchInformation(dex_file,
+                                                         referrer_class_def_idx,
+                                                         referrer_method_idx,
+                                                         string_idx,
+                                                         literal_offset));
+}
 
 class ParallelCompilationManager {
  public:
@@ -1915,6 +1985,7 @@
     CHECK(dex_file != nullptr);
     CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
   }
+  VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
 }
 
 void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) {
@@ -2027,6 +2098,7 @@
                                    bool compilation_enabled) {
   CompiledMethod* compiled_method = nullptr;
   uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
+  MethodReference method_ref(&dex_file, method_idx);
 
   if ((access_flags & kAccNative) != 0) {
     // Are we interpreting only and have support for generic JNI down calls?
@@ -2039,9 +2111,12 @@
     }
   } else if ((access_flags & kAccAbstract) != 0) {
   } else {
-    MethodReference method_ref(&dex_file, method_idx);
+    bool has_verified_method = verification_results_->GetVerifiedMethod(method_ref) != nullptr;
     bool compile = compilation_enabled &&
-                   verification_results_->IsCandidateForCompilation(method_ref, access_flags);
+                   // Basic checks, e.g., not <clinit>.
+                   verification_results_->IsCandidateForCompilation(method_ref, access_flags) &&
+                   // Did not fail to create VerifiedMethod metadata.
+                   has_verified_method;
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return nullptr.
       compiled_method = compiler_->Compile(code_item, access_flags, invoke_type, class_def_idx,
@@ -2049,10 +2124,12 @@
     }
     if (compiled_method == nullptr && dex_to_dex_compilation_level != kDontDexToDexCompile) {
       // TODO: add a command-line option to disable DEX-to-DEX compilation ?
+      // Do not optimize if a VerifiedMethod is missing. SafeCast elision, for example, relies on
+      // it.
       (*dex_to_dex_compiler_)(*this, code_item, access_flags,
                               invoke_type, class_def_idx,
                               method_idx, class_loader, dex_file,
-                              dex_to_dex_compilation_level);
+                              has_verified_method ? dex_to_dex_compilation_level : kRequired);
     }
   }
   if (kTimeCompileMethod) {
@@ -2065,15 +2142,17 @@
 
   Thread* self = Thread::Current();
   if (compiled_method != nullptr) {
-    MethodReference ref(&dex_file, method_idx);
-    DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file);
+    DCHECK(GetCompiledMethod(method_ref) == nullptr) << PrettyMethod(method_idx, dex_file);
     {
       MutexLock mu(self, compiled_methods_lock_);
-      compiled_methods_.Put(ref, compiled_method);
+      compiled_methods_.Put(method_ref, compiled_method);
     }
-    DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file);
+    DCHECK(GetCompiledMethod(method_ref) != nullptr) << PrettyMethod(method_idx, dex_file);
   }
 
+  // Done compiling, delete the verified method to reduce native memory usage.
+  verification_results_->RemoveVerifiedMethod(method_ref);
+
   if (self->IsExceptionPending()) {
     ScopedObjectAccess soa(self);
     LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n"
@@ -2218,4 +2297,31 @@
   }
   return !compile;
 }
+
+std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
+  std::ostringstream oss;
+  const ArenaPool* arena_pool = GetArenaPool();
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated());
+  oss << " java alloc=" << PrettySize(heap->GetBytesAllocated());
+#ifdef HAVE_MALLOC_H
+  struct mallinfo info = mallinfo();
+  const size_t allocated_space = static_cast<size_t>(info.uordblks);
+  const size_t free_space = static_cast<size_t>(info.fordblks);
+  oss << " native alloc=" << PrettySize(allocated_space) << " free="
+      << PrettySize(free_space);
+#endif
+  if (swap_space_.get() != nullptr) {
+    oss << " swap=" << PrettySize(swap_space_->GetSize());
+  }
+  if (extended) {
+    oss << "\nCode dedupe: " << dedupe_code_.DumpStats();
+    oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats();
+    oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats();
+    oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats();
+    oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats();
+  }
+  return oss.str();
+}
+
 }  // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index c487e42..db07a61 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -39,6 +39,8 @@
 #include "thread_pool.h"
 #include "utils/arena_allocator.h"
 #include "utils/dedupe_set.h"
+#include "utils/swap_space.h"
+#include "dex/verified_method.h"
 
 namespace art {
 
@@ -90,6 +92,8 @@
     void* llvm_info_;
 };
 
+static constexpr bool kUseMurmur3Hash = true;
+
 class CompilerDriver {
  public:
   // Create a compiler targeting the requested "instruction_set".
@@ -106,7 +110,8 @@
                           bool image, std::set<std::string>* image_classes,
                           std::set<std::string>* compiled_classes,
                           size_t thread_count, bool dump_stats, bool dump_passes,
-                          CumulativeLogger* timer, std::string profile_file = "");
+                          CumulativeLogger* timer, int swap_fd = -1,
+                          std::string profile_file = "");
 
   ~CompilerDriver();
 
@@ -212,6 +217,9 @@
                           bool* is_type_initialized, bool* use_direct_type_ptr,
                           uintptr_t* direct_type_ptr, bool* out_is_finalizable);
 
+  bool CanEmbedStringInCode(const DexFile& dex_file, uint32_t string_idx,
+                            bool* use_direct_type_ptr, uintptr_t* direct_type_ptr);
+
   // Get the DexCache for the
   mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -357,6 +365,12 @@
                      uint32_t target_method_idx,
                      size_t literal_offset)
       LOCKS_EXCLUDED(compiled_methods_lock_);
+  void AddStringPatch(const DexFile* dex_file,
+                      uint16_t referrer_class_def_idx,
+                      uint32_t referrer_method_idx,
+                      uint32_t string_idx,
+                      size_t literal_offset)
+      LOCKS_EXCLUDED(compiled_methods_lock_);
 
   bool GetSupportBootImageFixup() const {
     return support_boot_image_fixup_;
@@ -369,6 +383,12 @@
   ArenaPool* GetArenaPool() {
     return &arena_pool_;
   }
+  const ArenaPool* GetArenaPool() const {
+    return &arena_pool_;
+  }
+  SwapAllocator<void>& GetSwapSpaceAllocator() {
+    return *swap_space_allocator_.get();
+  }
 
   bool WriteElf(const std::string& android_root,
                 bool is_host,
@@ -576,6 +596,35 @@
     DISALLOW_COPY_AND_ASSIGN(TypePatchInformation);
   };
 
+  class StringPatchInformation : public PatchInformation {
+   public:
+    uint32_t GetStringIdx() const {
+      return string_idx_;
+    }
+
+    bool IsType() const {
+      return false;
+    }
+    const TypePatchInformation* AsType() const {
+      return nullptr;
+    }
+
+   private:
+    StringPatchInformation(const DexFile* dex_file,
+                           uint16_t referrer_class_def_idx,
+                           uint32_t referrer_method_idx,
+                           uint32_t string_idx,
+                           size_t literal_offset)
+        : PatchInformation(dex_file, referrer_class_def_idx, referrer_method_idx, literal_offset),
+          string_idx_(string_idx) {
+    }
+
+    const uint32_t string_idx_;
+
+    friend class CompilerDriver;
+    DISALLOW_COPY_AND_ASSIGN(StringPatchInformation);
+  };
+
   const std::vector<const CallPatchInformation*>& GetCodeToPatch() const {
     return code_to_patch_;
   }
@@ -585,6 +634,9 @@
   const std::vector<const TypePatchInformation*>& GetClassesToPatch() const {
     return classes_to_patch_;
   }
+  const std::vector<const StringPatchInformation*>& GetStringsToPatch() const {
+    return strings_to_patch_;
+  }
 
   // Checks if class specified by type_idx is one of the image_classes_
   bool IsImageClass(const char* descriptor) const;
@@ -595,11 +647,11 @@
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       LOCKS_EXCLUDED(compiled_classes_lock_);
 
-  std::vector<uint8_t>* DeduplicateCode(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info);
+  SwapVector<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
 
   /*
    * @brief return the pointer to the Call Frame Information.
@@ -609,12 +661,12 @@
     return cfi_info_.get();
   }
 
-  ProfileFile profile_file_;
-  bool profile_present_;
-
   // Should the compiler run on this method given profile information?
   bool SkipCompilation(const std::string& method_name);
 
+  // Get memory usage during compilation.
+  std::string GetMemoryUsageString(bool extended) const;
+
  private:
   // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics.
   // The only external contract is that unresolved method has flags 0 and resolved non-0.
@@ -707,9 +759,18 @@
   static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
+  // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
+  // as other fields rely on this.
+  std::unique_ptr<SwapSpace> swap_space_;
+  std::unique_ptr<SwapAllocator<void> > swap_space_allocator_;
+
+  ProfileFile profile_file_;
+  bool profile_present_;
+
   std::vector<const CallPatchInformation*> code_to_patch_;
   std::vector<const CallPatchInformation*> methods_to_patch_;
   std::vector<const TypePatchInformation*> classes_to_patch_;
+  std::vector<const StringPatchInformation*> strings_to_patch_;
 
   const CompilerOptions* const compiler_options_;
   VerificationResults* const verification_results_;
@@ -792,42 +853,81 @@
   // DeDuplication data structures, these own the corresponding byte arrays.
   class DedupeHashFunc {
    public:
-    size_t operator()(const std::vector<uint8_t>& array) const {
-      // For small arrays compute a hash using every byte.
-      static const size_t kSmallArrayThreshold = 16;
-      size_t hash = 0x811c9dc5;
-      if (array.size() <= kSmallArrayThreshold) {
+    size_t operator()(const ArrayRef<const uint8_t>& array) const {
+      if (kUseMurmur3Hash) {
+        static constexpr uint32_t c1 = 0xcc9e2d51;
+        static constexpr uint32_t c2 = 0x1b873593;
+        static constexpr uint32_t r1 = 15;
+        static constexpr uint32_t r2 = 13;
+        static constexpr uint32_t m = 5;
+        static constexpr uint32_t n = 0xe6546b64;
+
+        uint32_t hash = 0;
+        uint32_t len = static_cast<uint32_t>(array.size());
+
+        const int nblocks = len / 4;
+        typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+        const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(array.data());
+        int i;
+        for (i = 0; i < nblocks; i++) {
+          uint32_t k = blocks[i];
+          k *= c1;
+          k = (k << r1) | (k >> (32 - r1));
+          k *= c2;
+
+          hash ^= k;
+          hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
+        }
+
+        const uint8_t *tail = reinterpret_cast<const uint8_t*>(array.data() + nblocks * 4);
+        uint32_t k1 = 0;
+
+        switch (len & 3) {
+          case 3:
+            k1 ^= tail[2] << 16;
+          case 2:
+            k1 ^= tail[1] << 8;
+          case 1:
+            k1 ^= tail[0];
+
+            k1 *= c1;
+            k1 = (k1 << r1) | (k1 >> (32 - r1));
+            k1 *= c2;
+            hash ^= k1;
+        }
+
+        hash ^= len;
+        hash ^= (hash >> 16);
+        hash *= 0x85ebca6b;
+        hash ^= (hash >> 13);
+        hash *= 0xc2b2ae35;
+        hash ^= (hash >> 16);
+
+        return hash;
+      } else {
+        size_t hash = 0x811c9dc5;
         for (uint8_t b : array) {
           hash = (hash * 16777619) ^ b;
         }
-      } else {
-        // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
-        // instruction field for quick generated code on ARM) and then select a number of other
-        // values at random.
-        static const size_t kRandomHashCount = 16;
-        for (size_t i = 0; i < 2; ++i) {
-          uint8_t b = array[i + 6];
-          hash = (hash * 16777619) ^ b;
-        }
-        for (size_t i = 2; i < kRandomHashCount; ++i) {
-          size_t r = i * 1103515245 + 12345;
-          uint8_t b = array[r % array.size()];
-          hash = (hash * 16777619) ^ b;
-        }
+        hash += hash << 13;
+        hash ^= hash >> 7;
+        hash += hash << 3;
+        hash ^= hash >> 17;
+        hash += hash << 5;
+        return hash;
       }
-      hash += hash << 13;
-      hash ^= hash >> 7;
-      hash += hash << 3;
-      hash ^= hash >> 17;
-      hash += hash << 5;
-      return hash;
     }
   };
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_code_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_code_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_;
 
   DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
 };
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 84f5799..03ae489 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -102,6 +102,10 @@
     return verified_method_;
   }
 
+  void ClearVerifiedMethod() {
+    verified_method_ = nullptr;
+  }
+
   const std::string& GetSymbol();
 
  private:
@@ -117,7 +121,7 @@
   const uint16_t class_def_idx_;
   const uint32_t dex_method_idx_;
   const uint32_t access_flags_;
-  const VerifiedMethod* const verified_method_;
+  const VerifiedMethod* verified_method_;
 
   std::string symbol_;
 };
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
index e8ccd67..1577166 100644
--- a/compiler/elf_patcher.cc
+++ b/compiler/elf_patcher.cc
@@ -96,6 +96,16 @@
   return method;
 }
 
+mirror::String* ElfPatcher::GetTargetString(const CompilerDriver::StringPatchInformation* patch) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  StackHandleScope<1> hs(Thread::Current());
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
+  mirror::String* string = class_linker->ResolveString(patch->GetDexFile(), patch->GetStringIdx(),
+                                                       dex_cache);
+  CHECK(string != nullptr) << patch->GetDexFile().GetLocation() << " " << patch->GetStringIdx();
+  return string;
+}
+
 mirror::Class* ElfPatcher::GetTargetType(const CompilerDriver::TypePatchInformation* patch) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   StackHandleScope<2> hs(Thread::Current());
@@ -183,7 +193,8 @@
   if (write_patches_) {
     patches_.reserve(compiler_driver_->GetCodeToPatch().size() +
                      compiler_driver_->GetMethodsToPatch().size() +
-                     compiler_driver_->GetClassesToPatch().size());
+                     compiler_driver_->GetClassesToPatch().size() +
+                     compiler_driver_->GetStringsToPatch().size());
   }
   Thread* self = Thread::Current();
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -248,13 +259,15 @@
     SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
   }
 
-  const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
-      compiler_driver_->GetClassesToPatch();
-  for (size_t i = 0; i < classes_to_patch.size(); i++) {
-    const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
+  for (const CompilerDriver::TypePatchInformation* patch : compiler_driver_->GetClassesToPatch()) {
     mirror::Class* target = GetTargetType(patch);
     SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
   }
+  for (const CompilerDriver::StringPatchInformation* patch :
+      compiler_driver_->GetStringsToPatch()) {
+    mirror::String* target = GetTargetString(patch);
+    SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
+  }
 
   self->EndAssertNoThreadSuspension(old_cause);
 
diff --git a/compiler/elf_patcher.h b/compiler/elf_patcher.h
index 0a9f0a01..8bd5c45 100644
--- a/compiler/elf_patcher.h
+++ b/compiler/elf_patcher.h
@@ -83,6 +83,8 @@
 
   mirror::Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::String* GetTargetString(const CompilerDriver::StringPatchInformation* patch)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void AddPatch(uintptr_t off);
 
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 703e63f..f0eaec2 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -891,10 +891,11 @@
 // Add patch information to this section. Each patch is a Elf32_Word that
 // identifies an offset from the start of the text section
 void ElfWriterQuick::ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug) {
-  size_t size =
+  const size_t size =
       compiler_driver_->GetCodeToPatch().size() +
       compiler_driver_->GetMethodsToPatch().size() +
-      compiler_driver_->GetClassesToPatch().size();
+      compiler_driver_->GetClassesToPatch().size() +
+      compiler_driver_->GetStringsToPatch().size();
   if (size == 0) {
     if (debug) {
       LOG(INFO) << "No patches to record";
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 0aef512..3c28bc2 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -102,13 +102,16 @@
     ASSERT_TRUE(success_image);
     bool success_fixup = ElfFixup::Fixup(dup_oat.get(), writer.GetOatDataBegin());
     ASSERT_TRUE(success_fixup);
+
+    ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
+                                               << oat_file.GetFilename();
   }
 
   {
     std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
     ASSERT_TRUE(file.get() != NULL);
     ImageHeader image_header;
-    file->ReadFully(&image_header, sizeof(image_header));
+    ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true);
     ASSERT_TRUE(image_header.IsValid());
     ASSERT_GE(image_header.GetImageBitmapOffset(), sizeof(image_header));
     ASSERT_NE(0U, image_header.GetImageBitmapSize());
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 3d0384e..1b7ac06 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -55,7 +55,8 @@
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
 #include "handle_scope-inl.h"
-#include "utils.h"
+
+#include <numeric>
 
 using ::art::mirror::ArtField;
 using ::art::mirror::ArtMethod;
@@ -68,6 +69,9 @@
 
 namespace art {
 
+// Separate objects into multiple bins to optimize dirty memory use.
+static constexpr bool kBinObjects = true;
+
 bool ImageWriter::Write(const std::string& image_filename,
                         uintptr_t image_begin,
                         const std::string& oat_filename,
@@ -81,6 +85,7 @@
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
+  target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet());
   std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
   if (oat_file.get() == NULL) {
     LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
@@ -121,7 +126,7 @@
     Thread::Current()->TransitionFromSuspendedToRunnable();
     PruneNonImageClasses();  // Remove junk
     ComputeLazyFieldsForImageClasses();  // Add useful information
-    ComputeEagerResolvedStrings();
+    ProcessStrings();
     Thread::Current()->TransitionFromRunnableToSuspended(kNative);
   }
   gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -144,8 +149,15 @@
   CopyAndFixupObjects();
 
   PatchOatCodeAndMethods(oat_file.get());
+
+  // Before flushing, which might fail, release the mutator lock.
   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
 
+  if (oat_file->FlushCloseOrErase() != 0) {
+    LOG(ERROR) << "Failed to flush and close oat file " << oat_filename << " for " << oat_location;
+    return false;
+  }
+
   std::unique_ptr<File> image_file(OS::CreateEmptyFile(image_filename.c_str()));
   ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
   if (image_file.get() == NULL) {
@@ -154,6 +166,7 @@
   }
   if (fchmod(image_file->Fd(), 0644) != 0) {
     PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
+    image_file->Erase();
     return EXIT_FAILURE;
   }
 
@@ -161,6 +174,7 @@
   CHECK_EQ(image_end_, image_header->GetImageSize());
   if (!image_file->WriteFully(image_->Begin(), image_end_)) {
     PLOG(ERROR) << "Failed to write image file " << image_filename;
+    image_file->Erase();
     return false;
   }
 
@@ -170,53 +184,54 @@
                          image_header->GetImageBitmapSize(),
                          image_header->GetImageBitmapOffset())) {
     PLOG(ERROR) << "Failed to write image file " << image_filename;
+    image_file->Erase();
     return false;
   }
 
+  if (image_file->FlushCloseOrErase() != 0) {
+    PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
+    return false;
+  }
   return true;
 }
 
-void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) {
+void ImageWriter::SetImageOffset(mirror::Object* object,
+                                 ImageWriter::BinSlot bin_slot,
+                                 size_t offset) {
   DCHECK(object != nullptr);
   DCHECK_NE(offset, 0U);
-  DCHECK(!IsImageOffsetAssigned(object));
   mirror::Object* obj = reinterpret_cast<mirror::Object*>(image_->Begin() + offset);
   DCHECK_ALIGNED(obj, kObjectAlignment);
-  image_bitmap_->Set(obj);
-  // Before we stomp over the lock word, save the hash code for later.
-  Monitor::Deflate(Thread::Current(), object);;
-  LockWord lw(object->GetLockWord(false));
-  switch (lw.GetState()) {
-    case LockWord::kFatLocked: {
-      LOG(FATAL) << "Fat locked object " << obj << " found during object copy";
-      break;
+
+  image_bitmap_->Set(obj);  // Mark the obj as mutated, since we will end up changing it.
+  {
+    // Remember the object-inside-of-the-image's hash code so we can restore it after the copy.
+    auto hash_it = saved_hashes_map_.find(bin_slot);
+    if (hash_it != saved_hashes_map_.end()) {
+      std::pair<BinSlot, uint32_t> slot_hash = *hash_it;
+      saved_hashes_.push_back(std::make_pair(obj, slot_hash.second));
+      saved_hashes_map_.erase(hash_it);
     }
-    case LockWord::kThinLocked: {
-      LOG(FATAL) << "Thin locked object " << obj << " found during object copy";
-      break;
-    }
-    case LockWord::kUnlocked:
-      // No hash, don't need to save it.
-      break;
-    case LockWord::kHashCode:
-      saved_hashes_.push_back(std::make_pair(obj, lw.GetHashCode()));
-      break;
-    default:
-      LOG(FATAL) << "Unreachable.";
-      break;
   }
+  // The object is already deflated from when we set the bin slot. Just overwrite the lock word.
   object->SetLockWord(LockWord::FromForwardingAddress(offset), false);
   DCHECK(IsImageOffsetAssigned(object));
 }
 
-void ImageWriter::AssignImageOffset(mirror::Object* object) {
+void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot bin_slot) {
   DCHECK(object != nullptr);
-  SetImageOffset(object, image_end_);
-  image_end_ += RoundUp(object->SizeOf(), 8);  // 64-bit alignment
-  DCHECK_LT(image_end_, image_->Size());
+  DCHECK_NE(image_objects_offset_begin_, 0u);
+
+  size_t previous_bin_sizes = GetBinSizeSum(bin_slot.GetBin());  // sum sizes in [0..bin#)
+  size_t new_offset = image_objects_offset_begin_ + previous_bin_sizes + bin_slot.GetIndex();
+  DCHECK_ALIGNED(new_offset, kObjectAlignment);
+
+  SetImageOffset(object, bin_slot, new_offset);
+  DCHECK_LT(new_offset, image_end_);
 }
 
 bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const {
+  // Will also return true if the bin slot was assigned since we are reusing the lock word.
   DCHECK(object != nullptr);
   return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress;
 }
@@ -230,6 +245,178 @@
   return offset;
 }
 
+void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) {
+  DCHECK(object != nullptr);
+  DCHECK(!IsImageOffsetAssigned(object));
+  DCHECK(!IsImageBinSlotAssigned(object));
+
+  // Before we stomp over the lock word, save the hash code for later.
+  Monitor::Deflate(Thread::Current(), object);;
+  LockWord lw(object->GetLockWord(false));
+  switch (lw.GetState()) {
+    case LockWord::kFatLocked: {
+      LOG(FATAL) << "Fat locked object " << object << " found during object copy";
+      break;
+    }
+    case LockWord::kThinLocked: {
+      LOG(FATAL) << "Thin locked object " << object << " found during object copy";
+      break;
+    }
+    case LockWord::kUnlocked:
+      // No hash, don't need to save it.
+      break;
+    case LockWord::kHashCode:
+      saved_hashes_map_[bin_slot] = lw.GetHashCode();
+      break;
+    default:
+      LOG(FATAL) << "Unreachable.";
+      break;
+  }
+  object->SetLockWord(LockWord::FromForwardingAddress(static_cast<uint32_t>(bin_slot)),
+                      false);
+  DCHECK(IsImageBinSlotAssigned(object));
+}
+
+void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
+  DCHECK(object != nullptr);
+  size_t object_size;
+  if (object->IsArtMethod()) {
+    // Methods are sized based on the target pointer size.
+    object_size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
+  } else {
+    object_size = object->SizeOf();
+  }
+
+  // The magic happens here. We segregate objects into different bins based
+  // on how likely they are to get dirty at runtime.
+  //
+  // Likely-to-dirty objects get packed together into the same bin so that
+  // at runtime their page dirtiness ratio (how many dirty objects a page has) is
+  // maximized.
+  //
+  // This means more pages will stay either clean or shared dirty (with zygote) and
+  // the app will use less of its own (private) memory.
+  Bin bin = kBinRegular;
+
+  if (kBinObjects) {
+    //
+    // Changing the bin of an object is purely a memory-use tuning.
+    // It has no change on runtime correctness.
+    //
+    // Memory analysis has determined that the following types of objects get dirtied
+    // the most:
+    //
+    // * Class'es which are verified [their clinit runs only at runtime]
+    //   - classes in general [because their static fields get overwritten]
+    //   - initialized classes with all-final statics are unlikely to be ever dirty,
+    //     so bin them separately
+    // * Art Methods that are:
+    //   - native [their native entry point is not looked up until runtime]
+    //   - have declaring classes that aren't initialized
+    //            [their interpreter/quick entry points are trampolines until the class
+    //             becomes initialized]
+    //
+    // We also assume the following objects get dirtied either never or extremely rarely:
+    //  * Strings (they are immutable)
+    //  * Art methods that aren't native and have initialized declared classes
+    //
+    // We assume that "regular" bin objects are highly unlikely to become dirtied,
+    // so packing them together will not result in a noticeably tighter dirty-to-clean ratio.
+    //
+    if (object->IsClass()) {
+      bin = kBinClassVerified;
+      mirror::Class* klass = object->AsClass();
+
+      if (klass->GetStatus() == Class::kStatusInitialized) {
+        bin = kBinClassInitialized;
+
+        // If the class's static fields are all final, put it into a separate bin
+        // since it's very likely it will stay clean.
+        uint32_t num_static_fields = klass->NumStaticFields();
+        if (num_static_fields == 0) {
+          bin = kBinClassInitializedFinalStatics;
+        } else {
+          // Maybe all the statics are final?
+          bool all_final = true;
+          for (uint32_t i = 0; i < num_static_fields; ++i) {
+            ArtField* field = klass->GetStaticField(i);
+            if (!field->IsFinal()) {
+              all_final = false;
+              break;
+            }
+          }
+
+          if (all_final) {
+            bin = kBinClassInitializedFinalStatics;
+          }
+        }
+      }
+    } else if (object->IsArtMethod<kVerifyNone>()) {
+      mirror::ArtMethod* art_method = down_cast<ArtMethod*>(object);
+      if (art_method->IsNative()) {
+        bin = kBinArtMethodNative;
+      } else {
+        mirror::Class* declaring_class = art_method->GetDeclaringClass();
+        if (declaring_class->GetStatus() != Class::kStatusInitialized) {
+          bin = kBinArtMethodNotInitialized;
+        } else {
+          // This is highly unlikely to dirty since there's no entry points to mutate.
+          bin = kBinArtMethodsManagedInitialized;
+        }
+      }
+    } else if (object->GetClass<kVerifyNone>()->IsStringClass()) {
+      bin = kBinString;  // Strings are almost always immutable (except for object header).
+    }  // else bin = kBinRegular
+  }
+
+  size_t current_offset = bin_slot_sizes_[bin];  // How many bytes the current bin is at (aligned).
+  // Move the current bin size up to accomodate the object we just assigned a bin slot.
+  size_t offset_delta = RoundUp(object_size, kObjectAlignment);  // 64-bit alignment
+  bin_slot_sizes_[bin] += offset_delta;
+
+  BinSlot new_bin_slot(bin, current_offset);
+  SetImageBinSlot(object, new_bin_slot);
+
+  ++bin_slot_count_[bin];
+
+  DCHECK_LT(GetBinSizeSum(), image_->Size());
+
+  // Grow the image closer to the end by the object we just assigned.
+  image_end_ += offset_delta;
+  DCHECK_LT(image_end_, image_->Size());
+}
+
+bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const {
+  DCHECK(object != nullptr);
+
+  // We always stash the bin slot into a lockword, in the 'forwarding address' state.
+  // If it's in some other state, then we haven't yet assigned an image bin slot.
+  if (object->GetLockWord(false).GetState() != LockWord::kForwardingAddress) {
+    return false;
+  } else if (kIsDebugBuild) {
+    LockWord lock_word = object->GetLockWord(false);
+    size_t offset = lock_word.ForwardingAddress();
+    BinSlot bin_slot(offset);
+    DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()])
+      << "bin slot offset should not exceed the size of that bin";
+  }
+  return true;
+}
+
+ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const {
+  DCHECK(object != nullptr);
+  DCHECK(IsImageBinSlotAssigned(object));
+
+  LockWord lock_word = object->GetLockWord(false);
+  size_t offset = lock_word.ForwardingAddress();  // TODO: ForwardingAddress should be uint32_t
+  DCHECK_LE(offset, std::numeric_limits<uint32_t>::max());
+
+  BinSlot bin_slot(static_cast<uint32_t>(offset));
+  DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()]);
+
+  return bin_slot;
+}
+
 bool ImageWriter::AllocMemory() {
   size_t length = RoundUp(Runtime::Current()->GetHeap()->GetTotalMemory(), kPageSize);
   std::string error_msg;
@@ -262,6 +449,156 @@
   return true;
 }
 
+// Count the number of strings in the heap and put the result in arg as a size_t pointer.
+static void CountStringsCallback(Object* obj, void* arg)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (obj->GetClass()->IsStringClass()) {
+    ++*reinterpret_cast<size_t*>(arg);
+  }
+}
+
+// Collect all the java.lang.String in the heap and put them in the output strings_ array.
+class StringCollector {
+ public:
+  StringCollector(Handle<mirror::ObjectArray<mirror::String>> strings, size_t index)
+      : strings_(strings), index_(index) {
+  }
+  static void Callback(Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    auto* collector = reinterpret_cast<StringCollector*>(arg);
+    if (obj->GetClass()->IsStringClass()) {
+      collector->strings_->SetWithoutChecks<false>(collector->index_++, obj->AsString());
+    }
+  }
+  size_t GetIndex() const {
+    return index_;
+  }
+
+ private:
+  Handle<mirror::ObjectArray<mirror::String>> strings_;
+  size_t index_;
+};
+
+// Compare strings based on length, used for sorting strings by length / reverse length.
+class StringLengthComparator {
+ public:
+  explicit StringLengthComparator(Handle<mirror::ObjectArray<mirror::String>> strings)
+      : strings_(strings) {
+  }
+  bool operator()(size_t a, size_t b) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return strings_->GetWithoutChecks(a)->GetLength() < strings_->GetWithoutChecks(b)->GetLength();
+  }
+
+ private:
+  Handle<mirror::ObjectArray<mirror::String>> strings_;
+};
+
+// Normal string < comparison through the chars_ array.
+class SubstringComparator {
+ public:
+  explicit SubstringComparator(const std::vector<uint16_t>* const chars) : chars_(chars) {
+  }
+  bool operator()(const std::pair<size_t, size_t>& a, const std::pair<size_t, size_t>& b) {
+    return std::lexicographical_compare(chars_->begin() + a.first,
+                                        chars_->begin() + a.first + a.second,
+                                        chars_->begin() + b.first,
+                                        chars_->begin() + b.first + b.second);
+  }
+
+ private:
+  const std::vector<uint16_t>* const chars_;
+};
+
+void ImageWriter::ProcessStrings() {
+  size_t total_strings = 0;
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  ClassLinker* cl = Runtime::Current()->GetClassLinker();
+  {
+    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+    heap->VisitObjects(CountStringsCallback, &total_strings);  // Count the strings.
+  }
+  Thread* self = Thread::Current();
+  StackHandleScope<1> hs(self);
+  auto strings = hs.NewHandle(cl->AllocStringArray(self, total_strings));
+  StringCollector string_collector(strings, 0U);
+  {
+    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+    // Read strings into the array.
+    heap->VisitObjects(StringCollector::Callback, &string_collector);
+  }
+  // Some strings could have gotten freed if AllocStringArray caused a GC.
+  CHECK_LE(string_collector.GetIndex(), total_strings);
+  total_strings = string_collector.GetIndex();
+  size_t total_length = 0;
+  std::vector<size_t> reverse_sorted_strings;
+  for (size_t i = 0; i < total_strings; ++i) {
+    mirror::String* s = strings->GetWithoutChecks(i);
+    // Look up the string in the array.
+    total_length += s->GetLength();
+    reverse_sorted_strings.push_back(i);
+  }
+  // Sort by reverse length.
+  StringLengthComparator comparator(strings);
+  std::sort(reverse_sorted_strings.rbegin(), reverse_sorted_strings.rend(), comparator);
+  // Deduplicate prefixes and add strings to the char array.
+  std::vector<uint16_t> combined_chars(total_length, 0U);
+  size_t num_chars = 0;
+  // Characters of strings which are non equal prefix of another string (not the same string).
+  // We don't count the savings from equal strings since these would get interned later anyways.
+  size_t prefix_saved_chars = 0;
+  std::set<std::pair<size_t, size_t>, SubstringComparator> existing_strings((
+      SubstringComparator(&combined_chars)));
+  for (size_t i = 0; i < total_strings; ++i) {
+    mirror::String* s = strings->GetWithoutChecks(reverse_sorted_strings[i]);
+    // Add the string to the end of the char array.
+    size_t length = s->GetLength();
+    for (size_t j = 0; j < length; ++j) {
+      combined_chars[num_chars++] = s->CharAt(j);
+    }
+    // Try to see if the string exists as a prefix of an existing string.
+    size_t new_offset = 0;
+    std::pair<size_t, size_t> new_string(num_chars - length, length);
+    auto it = existing_strings.lower_bound(new_string);
+    bool is_prefix = true;
+    if (it == existing_strings.end()) {
+      is_prefix = false;
+    } else {
+      CHECK_LE(length, it->second);
+      for (size_t j = 0; j < length; ++j) {
+        if (combined_chars[it->first + j] != s->CharAt(j)) {
+          is_prefix = false;
+          break;
+        }
+      }
+    }
+    if (is_prefix) {
+      // Shares a prefix, set the offset to where the new offset will be.
+      new_offset = it->first;
+      // Remove the added chars.
+      num_chars -= length;
+      if (it->second != length) {
+        prefix_saved_chars += length;
+      }
+    } else {
+      new_offset = new_string.first;
+      existing_strings.insert(new_string);
+    }
+    s->SetOffset(new_offset);
+  }
+  // Allocate and update the char arrays.
+  auto* array = mirror::CharArray::Alloc(self, num_chars);
+  for (size_t i = 0; i < num_chars; ++i) {
+    array->SetWithoutChecks<false>(i, combined_chars[i]);
+  }
+  for (size_t i = 0; i < total_strings; ++i) {
+    strings->GetWithoutChecks(i)->SetArray(array);
+  }
+  if (kIsDebugBuild || VLOG_IS_ON(compiler)) {
+    LOG(INFO) << "Total # image strings=" << total_strings << " combined length="
+        << total_length << " prefix saved chars=" << prefix_saved_chars;
+  }
+  ComputeEagerResolvedStrings();
+}
+
 void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) {
   if (!obj->GetClass()->IsStringClass()) {
     return;
@@ -290,7 +627,7 @@
   }
 }
 
-void ImageWriter::ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ImageWriter::ComputeEagerResolvedStrings() {
   ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
   Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this);
 }
@@ -321,7 +658,8 @@
 
   // Remove the undesired classes from the class roots.
   for (const std::string& it : non_image_classes) {
-    class_linker->RemoveClass(it.c_str(), NULL);
+    bool result = class_linker->RemoveClass(it.c_str(), NULL);
+    DCHECK(result);
   }
 
   // Clear references to removed classes from the DexCaches.
@@ -360,8 +698,7 @@
   return true;
 }
 
-void ImageWriter::CheckNonImageClassesRemoved()
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ImageWriter::CheckNonImageClassesRemoved() {
   if (compiler_driver_.GetImageClasses() != nullptr) {
     gc::Heap* heap = Runtime::Current()->GetHeap();
     ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
@@ -390,29 +727,29 @@
   }
 }
 
-void ImageWriter::CalculateObjectOffsets(Object* obj) {
+void ImageWriter::CalculateObjectBinSlots(Object* obj) {
   DCHECK(obj != NULL);
   // if it is a string, we want to intern it if its not interned.
   if (obj->GetClass()->IsStringClass()) {
     // we must be an interned string that was forward referenced and already assigned
-    if (IsImageOffsetAssigned(obj)) {
+    if (IsImageBinSlotAssigned(obj)) {
       DCHECK_EQ(obj, obj->AsString()->Intern());
       return;
     }
     mirror::String* const interned = obj->AsString()->Intern();
     if (obj != interned) {
-      if (!IsImageOffsetAssigned(interned)) {
+      if (!IsImageBinSlotAssigned(interned)) {
         // interned obj is after us, allocate its location early
-        AssignImageOffset(interned);
+        AssignImageBinSlot(interned);
       }
       // point those looking for this object to the interned version.
-      SetImageOffset(obj, GetImageOffset(interned));
+      SetImageBinSlot(obj, GetImageBinSlot(interned));
       return;
     }
     // else (obj == interned), nothing to do but fall through to the normal case
   }
 
-  AssignImageOffset(obj);
+  AssignImageBinSlot(obj);
 }
 
 ObjectArray<Object>* ImageWriter::CreateImageRoots() const {
@@ -451,6 +788,8 @@
       ObjectArray<Object>::Alloc(self, object_array_class.Get(), ImageHeader::kImageRootsMax)));
   image_roots->Set<false>(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod());
   image_roots->Set<false>(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod());
+  image_roots->Set<false>(ImageHeader::kImtUnimplementedMethod,
+                          runtime->GetImtUnimplementedMethod());
   image_roots->Set<false>(ImageHeader::kDefaultImt, runtime->GetDefaultImt());
   image_roots->Set<false>(ImageHeader::kCalleeSaveMethod,
                           runtime->GetCalleeSaveMethod(Runtime::kSaveAll));
@@ -478,36 +817,40 @@
   }
   //
   size_t num_reference_fields = h_class->NumReferenceInstanceFields();
+  MemberOffset field_offset = h_class->GetFirstReferenceInstanceFieldOffset();
   for (size_t i = 0; i < num_reference_fields; ++i) {
-    mirror::ArtField* field = h_class->GetInstanceField(i);
-    MemberOffset field_offset = field->GetOffset();
     mirror::Object* value = obj->GetFieldObject<mirror::Object>(field_offset);
     if (value != nullptr) {
       WalkFieldsInOrder(value);
     }
+    field_offset = MemberOffset(field_offset.Uint32Value() +
+                                sizeof(mirror::HeapReference<mirror::Object>));
   }
 }
 
 // For an unvisited object, visit it then all its children found via fields.
 void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
-  if (!IsImageOffsetAssigned(obj)) {
+  // Use our own visitor routine (instead of GC visitor) to get better locality between
+  // an object and its fields
+  if (!IsImageBinSlotAssigned(obj)) {
     // Walk instance fields of all objects
     StackHandleScope<2> hs(Thread::Current());
     Handle<mirror::Object> h_obj(hs.NewHandle(obj));
     Handle<mirror::Class> klass(hs.NewHandle(obj->GetClass()));
     // visit the object itself.
-    CalculateObjectOffsets(h_obj.Get());
+    CalculateObjectBinSlots(h_obj.Get());
     WalkInstanceFields(h_obj.Get(), klass.Get());
     // Walk static fields of a Class.
     if (h_obj->IsClass()) {
       size_t num_static_fields = klass->NumReferenceStaticFields();
+      MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset();
       for (size_t i = 0; i < num_static_fields; ++i) {
-        mirror::ArtField* field = klass->GetStaticField(i);
-        MemberOffset field_offset = field->GetOffset();
         mirror::Object* value = h_obj->GetFieldObject<mirror::Object>(field_offset);
         if (value != nullptr) {
           WalkFieldsInOrder(value);
         }
+        field_offset = MemberOffset(field_offset.Uint32Value() +
+                                    sizeof(mirror::HeapReference<mirror::Object>));
       }
     } else if (h_obj->IsObjectArray()) {
       // Walk elements of an object array.
@@ -529,6 +872,24 @@
   writer->WalkFieldsInOrder(obj);
 }
 
+void ImageWriter::UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) {
+  ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg);
+  DCHECK(writer != nullptr);
+  writer->UnbinObjectsIntoOffset(obj);
+}
+
+void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) {
+  CHECK(obj != nullptr);
+
+  // We know the bin slot, and the total bin sizes for all objects by now,
+  // so calculate the object's final image offset.
+
+  DCHECK(IsImageBinSlotAssigned(obj));
+  BinSlot bin_slot = GetImageBinSlot(obj);
+  // Change the lockword from a bin slot into an offset
+  AssignImageOffset(obj, bin_slot);
+}
+
 void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) {
   CHECK_NE(0U, oat_loaded_size);
   Thread* self = Thread::Current();
@@ -540,18 +901,32 @@
 
   // Leave space for the header, but do not write it yet, we need to
   // know where image_roots is going to end up
-  image_end_ += RoundUp(sizeof(ImageHeader), 8);  // 64-bit-alignment
+  image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment);  // 64-bit-alignment
 
   {
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
     // TODO: Image spaces only?
     const char* old = self->StartAssertNoThreadSuspension("ImageWriter");
     DCHECK_LT(image_end_, image_->Size());
-    // Clear any pre-existing monitors which may have been in the monitor words.
+    image_objects_offset_begin_ = image_end_;
+    // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
     heap->VisitObjects(WalkFieldsCallback, this);
+    // Transform each object's bin slot into an offset which will be used to do the final copy.
+    heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
+    DCHECK(saved_hashes_map_.empty());  // All binslot hashes should've been put into vector by now.
     self->EndAssertNoThreadSuspension(old);
   }
 
+  DCHECK_GT(image_end_, GetBinSizeSum());
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Bin summary (total size: " << GetBinSizeSum() << "): ";
+    for (size_t bin = 0; bin < kBinSize; ++bin) {
+      LOG(INFO) << "  bin# " << bin << ", number objects: " << bin_slot_count_[bin] << ", "
+                << " total byte size: " << bin_slot_sizes_[bin];
+    }
+  }
+
   const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize);
   const byte* oat_file_end = oat_file_begin + oat_loaded_size;
   oat_data_begin_ = oat_file_begin + oat_data_offset;
@@ -578,8 +953,7 @@
   // Note that image_end_ is left at end of used space
 }
 
-void ImageWriter::CopyAndFixupObjects()
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ImageWriter::CopyAndFixupObjects() {
   Thread* self = Thread::Current();
   const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
   gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -604,7 +978,14 @@
   size_t offset = image_writer->GetImageOffset(obj);
   byte* dst = image_writer->image_->Begin() + offset;
   const byte* src = reinterpret_cast<const byte*>(obj);
-  size_t n = obj->SizeOf();
+  size_t n;
+  if (obj->IsArtMethod()) {
+    // Size without pointer fields since we don't want to overrun the buffer if target art method
+    // is 32 bits but source is 64 bits.
+    n = mirror::ArtMethod::SizeWithoutPointerFields(sizeof(void*));
+  } else {
+    n = obj->SizeOf();
+  }
   DCHECK_LT(offset + n, image_writer->image_->Size());
   memcpy(dst, src, n);
   Object* copy = reinterpret_cast<Object*>(dst);
@@ -614,6 +995,7 @@
   image_writer->FixupObject(obj, copy);
 }
 
+// Rewrite all the references in the copied object to point to their image address equivalent
 class FixupVisitor {
  public:
   FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) {
@@ -649,8 +1031,9 @@
   void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     DCHECK(obj->IsClass());
-    FixupVisitor::operator()(obj, offset, false);
+    FixupVisitor::operator()(obj, offset, /*is_static*/false);
 
+    // TODO: Remove dead code
     if (offset.Uint32Value() < mirror::Class::EmbeddedVTableOffset().Uint32Value()) {
       return;
     }
@@ -683,12 +1066,16 @@
   }
   if (orig->IsArtMethod<kVerifyNone>()) {
     FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy));
+  } else if (orig->IsClass() && orig->AsClass()->IsArtMethodClass()) {
+    // Set the right size for the target.
+    size_t size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
+    down_cast<mirror::Class*>(copy)->SetObjectSizeWithoutChecks(size);
   }
 }
 
 const byte* ImageWriter::GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted) {
   DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() &&
-         !method->IsAbstract()) << PrettyMethod(method);
+         !method->IsImtUnimplementedMethod() && !method->IsAbstract()) << PrettyMethod(method);
 
   // Use original code if it exists. Otherwise, set the code pointer to the resolution
   // trampoline.
@@ -719,9 +1106,11 @@
 const byte* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) {
   // Calculate the quick entry point following the same logic as FixupMethod() below.
   // The resolution method has a special trampoline to call.
-  if (UNLIKELY(method == Runtime::Current()->GetResolutionMethod())) {
+  Runtime* runtime = Runtime::Current();
+  if (UNLIKELY(method == runtime->GetResolutionMethod())) {
     return GetOatAddress(quick_resolution_trampoline_offset_);
-  } else if (UNLIKELY(method == Runtime::Current()->GetImtConflictMethod())) {
+  } else if (UNLIKELY(method == runtime->GetImtConflictMethod() ||
+                      method == runtime->GetImtUnimplementedMethod())) {
     return GetOatAddress(quick_imt_conflict_trampoline_offset_);
   } else {
     // We assume all methods have code. If they don't currently then we set them to the use the
@@ -739,40 +1128,62 @@
 void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
   // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
   // oat_begin_
+  // For 64 bit targets we need to repack the current runtime pointer sized fields to the right
+  // locations.
+  // Copy all of the fields from the runtime methods to the target methods first since we did a
+  // bytewise copy earlier.
+#if defined(ART_USE_PORTABLE_COMPILER)
+  copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+      orig->GetEntryPointFromPortableCompiledCode(), target_ptr_size_);
+#endif
+  copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(orig->GetEntryPointFromInterpreter(),
+                                                         target_ptr_size_);
+  copy->SetEntryPointFromJniPtrSize<kVerifyNone>(orig->GetEntryPointFromJni(), target_ptr_size_);
+  copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+      orig->GetEntryPointFromQuickCompiledCode(), target_ptr_size_);
 
   // The resolution method has a special trampoline to call.
-  if (UNLIKELY(orig == Runtime::Current()->GetResolutionMethod())) {
+  Runtime* runtime = Runtime::Current();
+  if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
 #if defined(ART_USE_PORTABLE_COMPILER)
-    copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
+    copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+        GetOatAddress(portable_resolution_trampoline_offset_), target_ptr_size_);
 #endif
-    copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
-  } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
+    copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+        GetOatAddress(quick_resolution_trampoline_offset_), target_ptr_size_);
+  } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
+                      orig == runtime->GetImtUnimplementedMethod())) {
 #if defined(ART_USE_PORTABLE_COMPILER)
-    copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
+    copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(
+        GetOatAddress(portable_imt_conflict_trampoline_offset_), target_ptr_size_);
 #endif
-    copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_imt_conflict_trampoline_offset_));
+    copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+        GetOatAddress(quick_imt_conflict_trampoline_offset_), target_ptr_size_);
   } else {
     // We assume all methods have code. If they don't currently then we set them to the use the
     // resolution trampoline. Abstract methods never have code and so we need to make sure their
     // use results in an AbstractMethodError. We use the interpreter to achieve this.
     if (UNLIKELY(orig->IsAbstract())) {
 #if defined(ART_USE_PORTABLE_COMPILER)
-      copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_to_interpreter_bridge_offset_));
+      copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(
+          GetOatAddress(portable_to_interpreter_bridge_offset_), target_ptr_size_);
 #endif
-      copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_to_interpreter_bridge_offset_));
-      copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
-          (const_cast<byte*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
+      copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+          GetOatAddress(quick_to_interpreter_bridge_offset_), target_ptr_size_);
+      copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
+          reinterpret_cast<EntryPointFromInterpreter*>(const_cast<byte*>(
+              GetOatAddress(interpreter_to_interpreter_bridge_offset_))), target_ptr_size_);
     } else {
       bool quick_is_interpreted;
       const byte* quick_code = GetQuickCode(orig, &quick_is_interpreted);
-      copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
+      copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(quick_code, target_ptr_size_);
 
       // Portable entrypoint:
       bool portable_is_interpreted = false;
 #if defined(ART_USE_PORTABLE_COMPILER)
       const byte* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
-      if (portable_code != nullptr &&
-          (!orig->IsStatic() || orig->IsConstructor() || orig->GetDeclaringClass()->IsInitialized())) {
+      if (portable_code != nullptr && (!orig->IsStatic() || orig->IsConstructor() ||
+          orig->GetDeclaringClass()->IsInitialized())) {
         // We have code for a non-static or initialized method, just use the code.
       } else if (portable_code == nullptr && orig->IsNative() &&
           (!orig->IsStatic() || orig->GetDeclaringClass()->IsInitialized())) {
@@ -789,18 +1200,15 @@
         // initialization.
         portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
       }
-      copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(portable_code);
+      copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+          portable_code, target_ptr_size_);
 #endif
       // JNI entrypoint:
       if (orig->IsNative()) {
         // The native method's pointer is set to a stub to lookup via dlsym.
         // Note this is not the code_ pointer, that is handled above.
-        copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_));
-      } else {
-        // Normal (non-abstract non-native) methods have various tables to relocate.
-        uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
-        const byte* native_gc_map = GetOatAddress(native_gc_map_offset);
-        copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
+        copy->SetEntryPointFromJniPtrSize<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_),
+                                                       target_ptr_size_);
       }
 
       // Interpreter entrypoint:
@@ -808,9 +1216,11 @@
       uint32_t interpreter_code = (quick_is_interpreted && portable_is_interpreted)
           ? interpreter_to_interpreter_bridge_offset_
           : interpreter_to_compiled_code_bridge_offset_;
-      copy->SetEntryPointFromInterpreter<kVerifyNone>(
+      EntryPointFromInterpreter* interpreter_entrypoint =
           reinterpret_cast<EntryPointFromInterpreter*>(
-              const_cast<byte*>(GetOatAddress(interpreter_code))));
+              const_cast<byte*>(GetOatAddress(interpreter_code)));
+      copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
+          interpreter_entrypoint, target_ptr_size_);
     }
   }
 }
@@ -846,4 +1256,32 @@
   image_header->SetOatChecksum(oat_header->GetChecksum());
 }
 
+size_t ImageWriter::GetBinSizeSum(ImageWriter::Bin up_to) const {
+  DCHECK_LE(up_to, kBinSize);
+  return std::accumulate(&bin_slot_sizes_[0], &bin_slot_sizes_[up_to], /*init*/0);
+}
+
+ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) {
+  // These values may need to get updated if more bins are added to the enum Bin
+  static_assert(kBinBits == 3, "wrong number of bin bits");
+  static_assert(kBinShift == 29, "wrong number of shift");
+  static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes");
+
+  DCHECK_LT(GetBin(), kBinSize);
+  DCHECK_ALIGNED(GetIndex(), kObjectAlignment);
+}
+
+ImageWriter::BinSlot::BinSlot(Bin bin, uint32_t index)
+    : BinSlot(index | (static_cast<uint32_t>(bin) << kBinShift)) {
+  DCHECK_EQ(index, GetIndex());
+}
+
+ImageWriter::Bin ImageWriter::BinSlot::GetBin() const {
+  return static_cast<Bin>((lockword_ & kBinMask) >> kBinShift);
+}
+
+uint32_t ImageWriter::BinSlot::GetIndex() const {
+  return lockword_ & ~kBinMask;
+}
+
 }  // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 61365fe..efcfc5e 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -26,12 +26,14 @@
 
 #include "base/macros.h"
 #include "driver/compiler_driver.h"
+#include "gc/space/space.h"
 #include "mem_map.h"
 #include "oat_file.h"
 #include "mirror/dex_cache.h"
 #include "os.h"
 #include "safe_map.h"
 #include "gc/space/space.h"
+#include "utils.h"
 
 namespace art {
 
@@ -39,12 +41,13 @@
 class ImageWriter FINAL {
  public:
   explicit ImageWriter(const CompilerDriver& compiler_driver)
-      : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
+      : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0),
+        image_objects_offset_begin_(0), image_begin_(NULL),
         oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
         interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
         portable_resolution_trampoline_offset_(0), quick_generic_jni_trampoline_offset_(0),
         quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
-        compile_pic_(false) {}
+        compile_pic_(false), target_ptr_size_(0), bin_slot_sizes_(), bin_slot_count_() {}
 
   ~ImageWriter() {}
 
@@ -65,14 +68,69 @@
   // Mark the objects defined in this space in the given live bitmap.
   void RecordImageAllocations() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Classify different kinds of bins that objects end up getting packed into during image writing.
+  enum Bin {
+    // Likely-clean:
+    kBinString,                        // [String] Almost always immutable (except for obj header).
+    kBinArtMethodsManagedInitialized,  // [ArtMethod] Not-native, and initialized. Unlikely to dirty
+    // Unknown mix of clean/dirty:
+    kBinRegular,
+    // Likely-dirty:
+    // All classes get their own bins since their fields often dirty
+    kBinClassInitializedFinalStatics,  // Class initializers have been run, no non-final statics
+    kBinClassInitialized,         // Class initializers have been run
+    kBinClassVerified,            // Class verified, but initializers haven't been run
+    kBinArtMethodNative,          // Art method that is actually native
+    kBinArtMethodNotInitialized,  // Art method with a declaring class that wasn't initialized
+    // Don't care about other art methods since they don't dirty
+    // Add more bins here if we add more segregation code.
+    kBinSize,
+  };
+
+  static constexpr size_t kBinBits = MinimumBitsToStore(kBinSize - 1);
+  // uint32 = typeof(lockword_)
+  static constexpr size_t kBinShift = BitSizeOf<uint32_t>() - kBinBits;
+  // 111000.....0
+  static constexpr size_t kBinMask = ((static_cast<size_t>(1) << kBinBits) - 1) << kBinShift;
+
+  // We use the lock word to store the bin # and bin index of the object in the image.
+  //
+  // The struct size must be exactly sizeof(LockWord), currently 32-bits, since this will end up
+  // stored in the lock word bit-for-bit when object forwarding addresses are being calculated.
+  struct BinSlot {
+    explicit BinSlot(uint32_t lockword);
+    BinSlot(Bin bin, uint32_t index);
+
+    // The bin an object belongs to, i.e. regular, class/verified, class/initialized, etc.
+    Bin GetBin() const;
+    // The offset in bytes from the beginning of the bin. Aligned to object size.
+    uint32_t GetIndex() const;
+    // Pack into a single uint32_t, for storing into a lock word.
+    explicit operator uint32_t() const { return lockword_; }
+    // Comparison operator for map support
+    bool operator<(const BinSlot& other) const  { return lockword_ < other.lockword_; }
+
+  private:
+    // Must be the same size as LockWord, any larger and we would truncate the data.
+    const uint32_t lockword_;
+  };
+
   // We use the lock word to store the offset of the object in the image.
-  void AssignImageOffset(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetImageOffset(mirror::Object* object, size_t offset)
+  void AssignImageOffset(mirror::Object* object, BinSlot bin_slot)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetImageOffset(mirror::Object* object, BinSlot bin_slot, size_t offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool IsImageOffsetAssigned(mirror::Object* object) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void AssignImageBinSlot(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsImageBinSlotAssigned(mirror::Object* object) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
@@ -123,13 +181,16 @@
   static void ComputeEagerResolvedStringsCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Combine string char arrays.
+  void ProcessStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Remove unwanted classes from various roots.
   void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static bool NonImageClassesVisitor(mirror::Class* c, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Verify unwanted classes removed.
-  void CheckNonImageClassesRemoved();
+  void CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void CheckNonImageClassesRemovedCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -138,7 +199,9 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void CalculateObjectOffsets(mirror::Object* obj)
+  void CalculateObjectBinSlots(mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void UnbinObjectsIntoOffset(mirror::Object* obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void WalkInstanceFields(mirror::Object* obj, mirror::Class* klass)
@@ -147,9 +210,11 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void WalkFieldsCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static void UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Creates the contiguous image in memory and adjusts pointers.
-  void CopyAndFixupObjects();
+  void CopyAndFixupObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void FixupMethod(mirror::ArtMethod* orig, mirror::ArtMethod* copy)
@@ -168,6 +233,9 @@
   void PatchOatCodeAndMethods(File* elf_file)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
+  size_t GetBinSizeSum(Bin up_to = kBinSize) const;
+
   const CompilerDriver& compiler_driver_;
 
   // oat file with code for this image
@@ -179,12 +247,18 @@
   // Offset to the free space in image_.
   size_t image_end_;
 
+  // Offset from image_begin_ to where the first object is in image_.
+  size_t image_objects_offset_begin_;
+
   // Beginning target image address for the output image.
   byte* image_begin_;
 
   // Saved hashes (objects are inside of the image so that they don't move).
   std::vector<std::pair<mirror::Object*, uint32_t>> saved_hashes_;
 
+  // Saved hashes (objects are bin slots to inside of the image, not yet allocated an address).
+  std::map<BinSlot, uint32_t> saved_hashes_map_;
+
   // Beginning target oat address for the pointers from the output image to its oat file.
   const byte* oat_data_begin_;
 
@@ -204,6 +278,13 @@
   uint32_t quick_to_interpreter_bridge_offset_;
   bool compile_pic_;
 
+  // Size of pointers on the target architecture.
+  size_t target_ptr_size_;
+
+  // Bin slot tracking for dirty object packing
+  size_t bin_slot_sizes_[kBinSize];  // Number of bytes in a bin
+  size_t bin_slot_count_[kBinSize];  // Number of objects in a bin
+
   friend class FixupVisitor;
   friend class FixupClassVisitor;
   DISALLOW_COPY_AND_ASSIGN(ImageWriter);
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index c38cfaf..71ef239 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -308,7 +308,9 @@
   }
 
   // 9. Plant call to native code associated with method.
-  __ Call(main_jni_conv->MethodStackOffset(), mirror::ArtMethod::NativeMethodOffset(),
+  MemberOffset jni_entrypoint_offset = mirror::ArtMethod::EntryPointFromJniOffset(
+      InstructionSetPointerSize(instruction_set));
+  __ Call(main_jni_conv->MethodStackOffset(), jni_entrypoint_offset,
           mr_conv->InterproceduralScratchRegister());
 
   // 10. Fix differences in result widths.
@@ -432,12 +434,12 @@
   std::vector<uint8_t> managed_code(cs);
   MemoryRegion code(&managed_code[0], managed_code.size());
   __ FinalizeInstructions(code);
-  return new CompiledMethod(driver,
-                            instruction_set,
-                            managed_code,
-                            frame_size,
-                            main_jni_conv->CoreSpillMask(),
-                            main_jni_conv->FpSpillMask());
+  return CompiledMethod::SwapAllocCompiledMethod(driver,
+                                                 instruction_set,
+                                                 ArrayRef<const uint8_t>(managed_code),
+                                                 frame_size,
+                                                 main_jni_conv->CoreSpillMask(),
+                                                 main_jni_conv->FpSpillMask());
 }
 
 // Copy a single parameter from the managed to the JNI calling convention
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index c94fc30..4694e0c 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -59,7 +59,7 @@
         EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
         uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
         quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
-        const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+        const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
         EXPECT_TRUE(quick_code != nullptr);
         size_t code_size = quick_code->size() * sizeof(quick_code[0]);
         EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
@@ -73,7 +73,7 @@
         EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
         uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(portable_oat_code), 2);
         portable_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
-        const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
+        const SwapVector<uint8_t>* portable_code = compiled_method->GetPortableCode();
         EXPECT_TRUE(portable_code != nullptr);
         size_t code_size = portable_code->size() * sizeof(portable_code[0]);
         EXPECT_EQ(0, memcmp(quick_oat_code, &portable_code[0], code_size))
@@ -185,8 +185,8 @@
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
   EXPECT_EQ(84U, sizeof(OatHeader));
-  EXPECT_EQ(8U, sizeof(OatMethodOffsets));
-  EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
+  EXPECT_EQ(4U, sizeof(OatMethodOffsets));
+  EXPECT_EQ(28U, sizeof(OatQuickMethodHeader));
   EXPECT_EQ(79 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
 }
 
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 5bf19ed..cab0573 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -138,26 +138,29 @@
 }
 
 struct OatWriter::GcMapDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return &compiled_method->GetGcMap();
   }
 
   static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
-    return oat_class->method_offsets_[method_offsets_index].gc_map_offset_;
+    uint32_t offset = oat_class->method_headers_[method_offsets_index].gc_map_offset_;
+    return offset == 0u ? 0u :
+        (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
   }
 
   static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
       ALWAYS_INLINE {
-    oat_class->method_offsets_[method_offsets_index].gc_map_offset_ = offset;
+    oat_class->method_headers_[method_offsets_index].gc_map_offset_ =
+        (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
   }
 
-  static const char* Name() ALWAYS_INLINE {
+  static const char* Name() {
     return "GC map";
   }
 };
 
 struct OatWriter::MappingTableDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return &compiled_method->GetMappingTable();
   }
 
@@ -173,13 +176,13 @@
         (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
   }
 
-  static const char* Name() ALWAYS_INLINE {
+  static const char* Name() {
     return "mapping table";
   }
 };
 
 struct OatWriter::VmapTableDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return &compiled_method->GetVmapTable();
   }
 
@@ -195,7 +198,7 @@
         (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
   }
 
-  static const char* Name() ALWAYS_INLINE {
+  static const char* Name() {
     return "vmap table";
   }
 };
@@ -339,8 +342,8 @@
       // Derived from CompiledMethod.
       uint32_t quick_code_offset = 0;
 
-      const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
-      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      const SwapVector<uint8_t>* portable_code = compiled_method->GetPortableCode();
+      const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       if (portable_code != nullptr) {
         CHECK(quick_code == nullptr);
         size_t oat_method_offsets_offset =
@@ -374,6 +377,7 @@
         OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
         uint32_t mapping_table_offset = method_header->mapping_table_offset_;
         uint32_t vmap_table_offset = method_header->vmap_table_offset_;
+        uint32_t gc_map_offset = method_header->gc_map_offset_;
         // The code offset was 0 when the mapping/vmap table offset was set, so it's set
         // to 0-offset and we need to adjust it by code_offset.
         uint32_t code_offset = quick_code_offset - thumb_offset;
@@ -385,12 +389,16 @@
           vmap_table_offset += code_offset;
           DCHECK_LT(vmap_table_offset, code_offset);
         }
+        if (gc_map_offset != 0u) {
+          gc_map_offset += code_offset;
+          DCHECK_LT(gc_map_offset, code_offset);
+        }
         uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
         uint32_t core_spill_mask = compiled_method->GetCoreSpillMask();
         uint32_t fp_spill_mask = compiled_method->GetFpSpillMask();
         *method_header = OatQuickMethodHeader(mapping_table_offset, vmap_table_offset,
-                                              frame_size_in_bytes, core_spill_mask, fp_spill_mask,
-                                              code_size);
+                                              gc_map_offset, frame_size_in_bytes, core_spill_mask,
+                                              fp_spill_mask, code_size);
 
         // Update checksum if this wasn't a duplicate.
         if (!deduped) {
@@ -404,7 +412,7 @@
         std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
         if (cfi_info != nullptr) {
           // Copy in the FDE, if present
-          const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
+          const SwapVector<uint8_t>* fde = compiled_method->GetCFIInfo();
           if (fde != nullptr) {
             // Copy the information into cfi_info and then fix the address in the new copy.
             int cur_offset = cfi_info->size();
@@ -457,7 +465,7 @@
         } else {
           status = mirror::Class::kStatusNotReady;
         }
-        const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
+        const SwapVector<uint8_t>& gc_map = compiled_method->GetGcMap();
         size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
         bool is_native = it.MemberIsNative();
         CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
@@ -497,7 +505,7 @@
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
       DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
 
-      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
       uint32_t map_size = map->size() * sizeof((*map)[0]);
       if (map_size != 0u) {
         auto lb = dedupe_map_.lower_bound(map);
@@ -519,7 +527,7 @@
  private:
   // Deduplication is already done on a pointer basis by the compiler driver,
   // so we can simply compare the pointers to find out if things are duplicated.
-  SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+  SafeMap<const SwapVector<uint8_t>*, uint32_t> dedupe_map_;
 };
 
 class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
@@ -533,7 +541,7 @@
     OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
-    OatMethodOffsets offsets(0u, 0u);
+    OatMethodOffsets offsets(0u);
     if (compiled_method != nullptr) {
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
       offsets = oat_class->method_offsets_[method_offsets_index_];
@@ -544,7 +552,7 @@
     InvokeType invoke_type = it.GetMethodInvokeType(dex_file_->GetClassDef(class_def_index_));
     // Unchecked as we hold mutator_lock_ on entry.
     ScopedObjectAccessUnchecked soa(Thread::Current());
-    StackHandleScope<2> hs(soa.Self());
+    StackHandleScope<1> hs(soa.Self());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache(*dex_file_)));
     mirror::ArtMethod* method = linker->ResolveMethod(*dex_file_, it.GetMemberIndex(), dex_cache,
                                                       NullHandle<mirror::ClassLoader>(),
@@ -553,7 +561,6 @@
     CHECK(method != NULL) << PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
     // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
     method->SetQuickOatCodeOffset(offsets.code_offset_);
-    method->SetOatNativeGcMapOffset(offsets.gc_map_offset_);
 
     return true;
   }
@@ -576,7 +583,7 @@
       size_t file_offset = file_offset_;
       OutputStream* out = out_;
 
-      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       if (quick_code != nullptr) {
         CHECK(compiled_method->GetPortableCode() == nullptr);
         uint32_t aligned_offset = compiled_method->AlignCode(offset_);
@@ -605,7 +612,8 @@
                    offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
             << PrettyMethod(it.GetMemberIndex(), *dex_file_);
         if (method_offsets.code_offset_ >= offset_) {
-          const OatQuickMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_];
+          const OatQuickMethodHeader& method_header =
+              oat_class->method_headers_[method_offsets_index_];
           if (!out->WriteFully(&method_header, sizeof(method_header))) {
             ReportWriteFailure("method header", it);
             return false;
@@ -642,7 +650,7 @@
 class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor {
  public:
   WriteMapMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
-                          size_t relative_offset)
+                        size_t relative_offset)
     : OatDexMethodVisitor(writer, relative_offset),
       out_(out),
       file_offset_(file_offset) {
@@ -660,11 +668,12 @@
       ++method_offsets_index_;
 
       // Write deduplicated map.
-      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
       size_t map_size = map->size() * sizeof((*map)[0]);
       DCHECK((map_size == 0u && map_offset == 0u) ||
             (map_size != 0u && map_offset != 0u && map_offset <= offset_))
-          << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+          << map_size << " " << map_offset << " " << offset_ << " "
+          << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " for " << DataAccess::Name();
       if (map_size != 0u && map_offset == offset_) {
         if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) {
           ReportWriteFailure(it);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 945048e..bd7bbc6 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -317,6 +317,9 @@
       if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
         return &lhs->GetVmapTable() < &rhs->GetVmapTable();
       }
+      if (UNLIKELY(&lhs->GetGcMap() != &rhs->GetGcMap())) {
+        return &lhs->GetGcMap() < &rhs->GetGcMap();
+      }
       return false;
     }
   };
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2c954a0..7822ee5 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -854,8 +854,8 @@
   // temp = temp[index_in_cache]
   __ ldr(temp, Address(temp, index_in_cache));
   // LR = temp[offset_of_quick_compiled_code]
-  __ ldr(LR, Address(temp,
-                     mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+  __ ldr(LR, Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+      kArmPointerSize).Int32Value()));
   // LR()
   __ blx(LR);
 
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f544d47..1b6fb6b 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -796,7 +796,8 @@
   // temp = temp[index_in_cache]
   __ movl(temp, Address(temp, index_in_cache));
   // (temp + offset_of_quick_compiled_code)()
-  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+                  kX86PointerSize).Int32Value()));
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(invoke->GetDexPc());
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e1807dc..1ee8271 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -738,7 +738,8 @@
   // temp = temp[index_in_cache]
   __ movl(temp, Address(temp, index_in_cache));
   // (temp + offset_of_quick_compiled_code)()
-  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+                  kX86_64PointerSize).SizeValue()));
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(invoke->GetDexPc());
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8a5077b..b9f7e8b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -167,16 +167,16 @@
   std::vector<uint8_t> gc_map;
   codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
 
-  return new CompiledMethod(GetCompilerDriver(),
-                            instruction_set,
-                            allocator.GetMemory(),
-                            codegen->GetFrameSize(),
-                            codegen->GetCoreSpillMask(),
-                            0, /* FPR spill mask, unused */
-                            mapping_table,
-                            vmap_table,
-                            gc_map,
-                            nullptr);
+  return CompiledMethod::SwapAllocCompiledMethod(GetCompilerDriver(),
+                                                 instruction_set,
+                                                 ArrayRef<const uint8_t>(allocator.GetMemory()),
+                                                 codegen->GetFrameSize(),
+                                                 codegen->GetCoreSpillMask(),
+                                                 0, /* FPR spill mask, unused */
+                                                 ArrayRef<const uint8_t>(mapping_table),
+                                                 ArrayRef<const uint8_t>(vmap_table),
+                                                 ArrayRef<const uint8_t>(gc_map),
+                                                 ArrayRef<const uint8_t>());
 }
 
 }  // namespace art
diff --git a/compiler/utils/arena_allocator.cc b/compiler/utils/arena_allocator.cc
index da49524..df3434e 100644
--- a/compiler/utils/arena_allocator.cc
+++ b/compiler/utils/arena_allocator.cc
@@ -177,6 +177,15 @@
   return ret;
 }
 
+size_t ArenaPool::GetBytesAllocated() const {
+  size_t total = 0;
+  MutexLock lock(Thread::Current(), lock_);
+  for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+    total += arena->GetBytesAllocated();
+  }
+  return total;
+}
+
 void ArenaPool::FreeArenaChain(Arena* first) {
   if (UNLIKELY(RUNNING_ON_VALGRIND > 0)) {
     for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
diff --git a/compiler/utils/arena_allocator.h b/compiler/utils/arena_allocator.h
index 7bfbb6f..d211eb3 100644
--- a/compiler/utils/arena_allocator.h
+++ b/compiler/utils/arena_allocator.h
@@ -124,6 +124,10 @@
     return Size() - bytes_allocated_;
   }
 
+  size_t GetBytesAllocated() const {
+    return bytes_allocated_;
+  }
+
  private:
   size_t bytes_allocated_;
   uint8_t* memory_;
@@ -142,11 +146,12 @@
  public:
   ArenaPool();
   ~ArenaPool();
-  Arena* AllocArena(size_t size);
-  void FreeArenaChain(Arena* first);
+  Arena* AllocArena(size_t size) LOCKS_EXCLUDED(lock_);
+  void FreeArenaChain(Arena* first) LOCKS_EXCLUDED(lock_);
+  size_t GetBytesAllocated() const LOCKS_EXCLUDED(lock_);
 
  private:
-  Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   Arena* free_arenas_ GUARDED_BY(lock_);
   DISALLOW_COPY_AND_ASSIGN(ArenaPool);
 };
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index 2d70b7d..0ab5434 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -88,7 +88,7 @@
 
   template <typename U>
   ArrayRef(const std::vector<U>& v,
-           typename std::enable_if<std::is_same<T, const U>::value, tag>::tag t = tag())
+           typename std::enable_if<std::is_same<T, const U>::value, tag>::type t = tag())
       : array_(v.data()), size_(v.size()) {
   }
 
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index 4c52174..b062a2a 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -17,50 +17,89 @@
 #ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_
 #define ART_COMPILER_UTILS_DEDUPE_SET_H_
 
+#include <algorithm>
+#include <inttypes.h>
+#include <memory>
 #include <set>
 #include <string>
 
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
+#include "utils/swap_space.h"
 
 namespace art {
 
 // A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the
 // Add method. The data-structure is thread-safe through the use of internal locks, it also
 // supports the lock being sharded.
-template <typename Key, typename HashType, typename HashFunc, HashType kShard = 1>
+template <typename InKey, typename StoreKey, typename HashType, typename HashFunc,
+          HashType kShard = 1>
 class DedupeSet {
-  typedef std::pair<HashType, Key*> HashedKey;
+  typedef std::pair<HashType, const InKey*> HashedInKey;
+  struct HashedKey {
+    StoreKey* store_ptr;
+    union {
+      HashType store_hash;        // Valid if store_ptr != nullptr.
+      const HashedInKey* in_key;  // Valid if store_ptr == nullptr.
+    };
+  };
 
   class Comparator {
    public:
     bool operator()(const HashedKey& a, const HashedKey& b) const {
-      if (a.first != b.first) {
-        return a.first < b.first;
+      HashType a_hash = (a.store_ptr != nullptr) ? a.store_hash : a.in_key->first;
+      HashType b_hash = (b.store_ptr != nullptr) ? b.store_hash : b.in_key->first;
+      if (a_hash != b_hash) {
+        return a_hash < b_hash;
+      }
+      if (a.store_ptr != nullptr && b.store_ptr != nullptr) {
+        return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+                                            b.store_ptr->begin(), b.store_ptr->end());
+      } else if (a.store_ptr != nullptr && b.store_ptr == nullptr) {
+        return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+                                            b.in_key->second->begin(), b.in_key->second->end());
+      } else if (a.store_ptr == nullptr && b.store_ptr != nullptr) {
+        return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+                                            b.store_ptr->begin(), b.store_ptr->end());
       } else {
-        return *a.second < *b.second;
+        return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+                                            b.in_key->second->begin(), b.in_key->second->end());
       }
     }
   };
 
  public:
-  Key* Add(Thread* self, const Key& key) {
+  StoreKey* Add(Thread* self, const InKey& key) {
+    uint64_t hash_start;
+    if (kIsDebugBuild) {
+      hash_start = NanoTime();
+    }
     HashType raw_hash = HashFunc()(key);
+    if (kIsDebugBuild) {
+      uint64_t hash_end = NanoTime();
+      hash_time_ += hash_end - hash_start;
+    }
     HashType shard_hash = raw_hash / kShard;
     HashType shard_bin = raw_hash % kShard;
-    HashedKey hashed_key(shard_hash, const_cast<Key*>(&key));
+    HashedInKey hashed_in_key(shard_hash, &key);
+    HashedKey hashed_key;
+    hashed_key.store_ptr = nullptr;
+    hashed_key.in_key = &hashed_in_key;
     MutexLock lock(self, *lock_[shard_bin]);
     auto it = keys_[shard_bin].find(hashed_key);
     if (it != keys_[shard_bin].end()) {
-      return it->second;
+      DCHECK(it->store_ptr != nullptr);
+      return it->store_ptr;
     }
-    hashed_key.second = new Key(key);
+    hashed_key.store_ptr = CreateStoreKey(key);
+    hashed_key.store_hash = shard_hash;
     keys_[shard_bin].insert(hashed_key);
-    return hashed_key.second;
+    return hashed_key.store_ptr;
   }
 
-  explicit DedupeSet(const char* set_name) {
+  explicit DedupeSet(const char* set_name, SwapAllocator<void>& alloc)
+      : allocator_(alloc), hash_time_(0) {
     for (HashType i = 0; i < kShard; ++i) {
       std::ostringstream oss;
       oss << set_name << " lock " << i;
@@ -70,15 +109,59 @@
   }
 
   ~DedupeSet() {
-    for (HashType i = 0; i < kShard; ++i) {
-      STLDeleteValues(&keys_[i]);
+    // Have to manually free all pointers.
+    for (auto& shard : keys_) {
+      for (const auto& hashed_key : shard) {
+        DCHECK(hashed_key.store_ptr != nullptr);
+        DeleteStoreKey(hashed_key.store_ptr);
+      }
     }
   }
 
+  std::string DumpStats() const {
+    size_t collision_sum = 0;
+    size_t collision_max = 0;
+    for (HashType shard = 0; shard < kShard; ++shard) {
+      HashType last_hash = 0;
+      size_t collision_cur_max = 0;
+      for (const HashedKey& key : keys_[shard]) {
+        DCHECK(key.store_ptr != nullptr);
+        if (key.store_hash == last_hash) {
+          collision_cur_max++;
+          if (collision_cur_max > 1) {
+            collision_sum++;
+            if (collision_cur_max > collision_max) {
+              collision_max = collision_cur_max;
+            }
+          }
+        } else {
+          collision_cur_max = 1;
+          last_hash = key.store_hash;
+        }
+      }
+    }
+    return StringPrintf("%zu collisions, %zu max bucket size, %" PRIu64 " ns hash time",
+                        collision_sum, collision_max, hash_time_);
+  }
+
  private:
+  StoreKey* CreateStoreKey(const InKey& key) {
+    StoreKey* ret = allocator_.allocate(1);
+    allocator_.construct(ret, key.begin(), key.end(), allocator_);
+    return ret;
+  }
+
+  void DeleteStoreKey(StoreKey* key) {
+    SwapAllocator<StoreKey> alloc(allocator_);
+    alloc.destroy(key);
+    alloc.deallocate(key, 1);
+  }
+
   std::string lock_name_[kShard];
   std::unique_ptr<Mutex> lock_[kShard];
   std::set<HashedKey, Comparator> keys_[kShard];
+  SwapAllocator<StoreKey> allocator_;
+  uint64_t hash_time_;
 
   DISALLOW_COPY_AND_ASSIGN(DedupeSet);
 };
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index 8abe6de..637964e 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -15,6 +15,10 @@
  */
 
 #include "dedupe_set.h"
+
+#include <algorithm>
+#include <cstdio>
+
 #include "gtest/gtest.h"
 #include "thread-inl.h"
 
@@ -35,19 +39,22 @@
 TEST(DedupeSetTest, Test) {
   Thread* self = Thread::Current();
   typedef std::vector<uint8_t> ByteArray;
-  DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator("test");
-  ByteArray* array1;
+  SwapAllocator<void> swap(nullptr);
+  DedupeSet<ByteArray, SwapVector<uint8_t>, size_t, DedupeHashFunc> deduplicator("test", swap);
+  SwapVector<uint8_t>* array1;
   {
     ByteArray test1;
     test1.push_back(10);
     test1.push_back(20);
     test1.push_back(30);
     test1.push_back(45);
+
     array1 = deduplicator.Add(self, test1);
-    ASSERT_EQ(test1, *array1);
+    ASSERT_NE(array1, nullptr);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array1->begin()));
   }
 
-  ByteArray* array2;
+  SwapVector<uint8_t>* array2;
   {
     ByteArray test1;
     test1.push_back(10);
@@ -56,10 +63,10 @@
     test1.push_back(45);
     array2 = deduplicator.Add(self, test1);
     ASSERT_EQ(array2, array1);
-    ASSERT_EQ(test1, *array2);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array2->begin()));
   }
 
-  ByteArray* array3;
+  SwapVector<uint8_t>* array3;
   {
     ByteArray test1;
     test1.push_back(10);
@@ -67,8 +74,8 @@
     test1.push_back(30);
     test1.push_back(47);
     array3 = deduplicator.Add(self, test1);
-    ASSERT_NE(array3, &test1);
-    ASSERT_EQ(test1, *array3);
+    ASSERT_NE(array3, nullptr);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array3->begin()));
   }
 }
 
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
new file mode 100644
index 0000000..fc51435
--- /dev/null
+++ b/compiler/utils/swap_space.cc
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2014 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 "swap_space.h"
+
+#include <algorithm>
+#include <numeric>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "thread-inl.h"
+
+namespace art {
+
+// The chunk size by which the swap file is increased and mapped.
+static constexpr size_t kMininumMapSize = 16 * MB;
+
+static constexpr bool kCheckFreeMaps = false;
+
+template <typename FreeBySizeSet>
+static void DumpFreeMap(const FreeBySizeSet& free_by_size) {
+  size_t last_size = static_cast<size_t>(-1);
+  for (const auto& entry : free_by_size) {
+    if (last_size != entry.first) {
+      last_size = entry.first;
+      LOG(INFO) << "Size " << last_size;
+    }
+    LOG(INFO) << "  0x" << std::hex << entry.second->Start()
+        << " size=" << std::dec << entry.second->size;
+  }
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void RemoveChunk(FreeByStartSet* free_by_start,
+                        FreeBySizeSet* free_by_size,
+                        typename FreeBySizeSet::const_iterator free_by_size_pos) {
+  auto free_by_start_pos = free_by_size_pos->second;
+  free_by_size->erase(free_by_size_pos);
+  free_by_start->erase(free_by_start_pos);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void InsertChunk(FreeByStartSet* free_by_start,
+                        FreeBySizeSet* free_by_size,
+                        const SpaceChunk& chunk) {
+  DCHECK_NE(chunk.size, 0u);
+  auto insert_result = free_by_start->insert(chunk);
+  DCHECK(insert_result.second);
+  free_by_size->emplace(chunk.size, insert_result.first);
+}
+
+SwapSpace::SwapSpace(int fd, size_t initial_size)
+    : fd_(fd),
+      size_(0),
+      lock_("SwapSpace lock", static_cast<LockLevel>(LockLevel::kDefaultMutexLevel - 1)) {
+  // Assume that the file is unlinked.
+
+  InsertChunk(&free_by_start_, &free_by_size_, NewFileChunk(initial_size));
+}
+
+SwapSpace::~SwapSpace() {
+  // All arenas are backed by the same file. Just close the descriptor.
+  close(fd_);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static size_t CollectFree(const FreeByStartSet& free_by_start, const FreeBySizeSet& free_by_size) {
+  if (free_by_start.size() != free_by_size.size()) {
+    LOG(FATAL) << "Size: " << free_by_start.size() << " vs " << free_by_size.size();
+  }
+
+  // Calculate over free_by_size.
+  size_t sum1 = 0;
+  for (const auto& entry : free_by_size) {
+    sum1 += entry.second->size;
+  }
+
+  // Calculate over free_by_start.
+  size_t sum2 = 0;
+  for (const auto& entry : free_by_start) {
+    sum2 += entry.size;
+  }
+
+  if (sum1 != sum2) {
+    LOG(FATAL) << "Sum: " << sum1 << " vs " << sum2;
+  }
+  return sum1;
+}
+
+void* SwapSpace::Alloc(size_t size) {
+  MutexLock lock(Thread::Current(), lock_);
+  size = RoundUp(size, 8U);
+
+  // Check the free list for something that fits.
+  // TODO: Smarter implementation. Global biggest chunk, ...
+  SpaceChunk old_chunk;
+  auto it = free_by_start_.empty()
+      ? free_by_size_.end()
+      : free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() });
+  if (it != free_by_size_.end()) {
+    old_chunk = *it->second;
+    RemoveChunk(&free_by_start_, &free_by_size_, it);
+  } else {
+    // Not a big enough free chunk, need to increase file size.
+    old_chunk = NewFileChunk(size);
+  }
+
+  void* ret = old_chunk.ptr;
+
+  if (old_chunk.size != size) {
+    // Insert the remainder.
+    SpaceChunk new_chunk = { old_chunk.ptr + size, old_chunk.size - size };
+    InsertChunk(&free_by_start_, &free_by_size_, new_chunk);
+  }
+
+  return ret;
+}
+
+SpaceChunk SwapSpace::NewFileChunk(size_t min_size) {
+#if !defined(__APPLE__)
+  size_t next_part = std::max(RoundUp(min_size, kPageSize), RoundUp(kMininumMapSize, kPageSize));
+  int result = TEMP_FAILURE_RETRY(ftruncate64(fd_, size_ + next_part));
+  if (result != 0) {
+    PLOG(FATAL) << "Unable to increase swap file.";
+  }
+  uint8_t* ptr = reinterpret_cast<uint8_t*>(
+      mmap(nullptr, next_part, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, size_));
+  if (ptr == MAP_FAILED) {
+    LOG(ERROR) << "Unable to mmap new swap file chunk.";
+    LOG(ERROR) << "Current size: " << size_ << " requested: " << next_part << "/" << min_size;
+    LOG(ERROR) << "Free list:";
+    MutexLock lock(Thread::Current(), lock_);
+    DumpFreeMap(free_by_size_);
+    LOG(ERROR) << "In free list: " << CollectFree(free_by_start_, free_by_size_);
+    LOG(FATAL) << "Aborting...";
+  }
+  size_ += next_part;
+  SpaceChunk new_chunk = {ptr, next_part};
+  maps_.push_back(new_chunk);
+  return new_chunk;
+#else
+  UNUSED(kMininumMapSize);
+  LOG(FATAL) << "No swap file support on the Mac.";
+  return {nullptr, 0U};  // NOLINT [readability/braces] [4]
+#endif
+}
+
+// TODO: Full coalescing.
+void SwapSpace::Free(void* ptrV, size_t size) {
+  MutexLock lock(Thread::Current(), lock_);
+  size = RoundUp(size, 8U);
+
+  size_t free_before = 0;
+  if (kCheckFreeMaps) {
+    free_before = CollectFree(free_by_start_, free_by_size_);
+  }
+
+  SpaceChunk chunk = { reinterpret_cast<uint8_t*>(ptrV), size };
+  auto it = free_by_start_.lower_bound(chunk);
+  if (it != free_by_start_.begin()) {
+    auto prev = it;
+    --prev;
+    CHECK_LE(prev->End(), chunk.Start());
+    if (prev->End() == chunk.Start()) {
+      // Merge *prev with this chunk.
+      chunk.size += prev->size;
+      chunk.ptr -= prev->size;
+      auto erase_pos = free_by_size_.find(FreeBySizeEntry { prev->size, prev });
+      DCHECK(erase_pos != free_by_size_.end());
+      RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+      // "prev" is invalidated but "it" remains valid.
+    }
+  }
+  if (it != free_by_start_.end()) {
+    CHECK_LE(chunk.End(), it->Start());
+    if (chunk.End() == it->Start()) {
+      // Merge *it with this chunk.
+      chunk.size += it->size;
+      auto erase_pos = free_by_size_.find(FreeBySizeEntry { it->size, it });
+      DCHECK(erase_pos != free_by_size_.end());
+      RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+      // "it" is invalidated but we don't need it anymore.
+    }
+  }
+  InsertChunk(&free_by_start_, &free_by_size_, chunk);
+
+  if (kCheckFreeMaps) {
+    size_t free_after = CollectFree(free_by_start_, free_by_size_);
+
+    if (free_after != free_before + size) {
+      DumpFreeMap(free_by_size_);
+      CHECK_EQ(free_after, free_before + size) << "Should be " << size << " difference from " << free_before;
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
new file mode 100644
index 0000000..6c7f0b4
--- /dev/null
+++ b/compiler/utils/swap_space.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2014 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_COMPILER_UTILS_SWAP_SPACE_H_
+#define ART_COMPILER_UTILS_SWAP_SPACE_H_
+
+#include <cstdlib>
+#include <list>
+#include <set>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "mem_map.h"
+#include "utils.h"
+#include "utils/debug_stack.h"
+
+namespace art {
+
+// Chunk of space.
+struct SpaceChunk {
+  uint8_t* ptr;
+  size_t size;
+
+  uintptr_t Start() const {
+    return reinterpret_cast<uintptr_t>(ptr);
+  }
+  uintptr_t End() const {
+    return reinterpret_cast<uintptr_t>(ptr) + size;
+  }
+};
+
+inline bool operator==(const SpaceChunk& lhs, const SpaceChunk& rhs) {
+  return (lhs.size == rhs.size) && (lhs.ptr == rhs.ptr);
+}
+
+class SortChunkByPtr {
+ public:
+  bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
+    return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
+  }
+};
+
+// An arena pool that creates arenas backed by an mmaped file.
+class SwapSpace {
+ public:
+  SwapSpace(int fd, size_t initial_size);
+  ~SwapSpace();
+  void* Alloc(size_t size) LOCKS_EXCLUDED(lock_);
+  void Free(void* ptr, size_t size) LOCKS_EXCLUDED(lock_);
+
+  size_t GetSize() {
+    return size_;
+  }
+
+ private:
+  SpaceChunk NewFileChunk(size_t min_size);
+
+  int fd_;
+  size_t size_;
+  std::list<SpaceChunk> maps_;
+
+  // NOTE: Boost.Bimap would be useful for the two following members.
+
+  // Map start of a free chunk to its size.
+  typedef std::set<SpaceChunk, SortChunkByPtr> FreeByStartSet;
+  FreeByStartSet free_by_start_ GUARDED_BY(lock_);
+
+  // Map size to an iterator to free_by_start_'s entry.
+  typedef std::pair<size_t, FreeByStartSet::const_iterator> FreeBySizeEntry;
+  struct FreeBySizeComparator {
+    bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) {
+      if (lhs.first != rhs.first) {
+        return lhs.first < rhs.first;
+      } else {
+        return lhs.second->Start() < rhs.second->Start();
+      }
+    }
+  };
+  typedef std::set<FreeBySizeEntry, FreeBySizeComparator> FreeBySizeSet;
+  FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
+
+  mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  DISALLOW_COPY_AND_ASSIGN(SwapSpace);
+};
+
+template <typename T> class SwapAllocator;
+
+template <>
+class SwapAllocator<void> {
+ public:
+  typedef void value_type;
+  typedef void* pointer;
+  typedef const void* const_pointer;
+
+  template <typename U>
+  struct rebind {
+    typedef SwapAllocator<U> other;
+  };
+
+  explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+  template <typename U>
+  SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+  SwapAllocator(const SwapAllocator& other) = default;
+  SwapAllocator& operator=(const SwapAllocator& other) = default;
+  ~SwapAllocator() = default;
+
+ private:
+  SwapSpace* swap_space_;
+
+  template <typename U>
+  friend class SwapAllocator;
+};
+
+template <typename T>
+class SwapAllocator {
+ public:
+  typedef T value_type;
+  typedef T* pointer;
+  typedef T& reference;
+  typedef const T* const_pointer;
+  typedef const T& const_reference;
+  typedef size_t size_type;
+  typedef ptrdiff_t difference_type;
+
+  template <typename U>
+  struct rebind {
+    typedef SwapAllocator<U> other;
+  };
+
+  explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+  template <typename U>
+  SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+  SwapAllocator(const SwapAllocator& other) = default;
+  SwapAllocator& operator=(const SwapAllocator& other) = default;
+  ~SwapAllocator() = default;
+
+  size_type max_size() const {
+    return static_cast<size_type>(-1) / sizeof(T);
+  }
+
+  pointer address(reference x) const { return &x; }
+  const_pointer address(const_reference x) const { return &x; }
+
+  pointer allocate(size_type n, SwapAllocator<void>::pointer hint = nullptr) {
+    DCHECK_LE(n, max_size());
+    if (swap_space_ == nullptr) {
+      return reinterpret_cast<T*>(malloc(n * sizeof(T)));
+    } else {
+      return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
+    }
+  }
+  void deallocate(pointer p, size_type n) {
+    if (swap_space_ == nullptr) {
+      free(p);
+    } else {
+      swap_space_->Free(p, n * sizeof(T));
+    }
+  }
+
+  void construct(pointer p, const_reference val) {
+    new (static_cast<void*>(p)) value_type(val);
+  }
+  template <class U, class... Args>
+  void construct(U* p, Args&&... args) {
+    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+  }
+  void destroy(pointer p) {
+    p->~value_type();
+  }
+
+  inline bool operator==(SwapAllocator const& other) {
+    return swap_space_ == other.swap_space_;
+  }
+  inline bool operator!=(SwapAllocator const& other) {
+    return !operator==(other);
+  }
+
+ private:
+  SwapSpace* swap_space_;
+
+  template <typename U>
+  friend class SwapAllocator;
+};
+
+template <typename T>
+using SwapVector = std::vector<T, SwapAllocator<T>>;
+template <typename T, typename Comparator>
+using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_SWAP_SPACE_H_
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
new file mode 100644
index 0000000..bf50ac3
--- /dev/null
+++ b/compiler/utils/swap_space_test.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 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/swap_space.h"
+
+#include <cstdio>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "gtest/gtest.h"
+
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "os.h"
+
+namespace art {
+
+class SwapSpaceTest : public CommonRuntimeTest {
+};
+
+static void SwapTest(bool use_file) {
+  ScratchFile scratch;
+  int fd = scratch.GetFd();
+  unlink(scratch.GetFilename().c_str());
+
+  SwapSpace pool(fd, 1 * MB);
+  SwapAllocator<void> alloc(use_file ? &pool : nullptr);
+
+  SwapVector<int32_t> v(alloc);
+  v.reserve(1000000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v.push_back(i);
+    EXPECT_EQ(i, v[i]);
+  }
+
+  SwapVector<int32_t> v2(alloc);
+  v2.reserve(1000000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v2.push_back(i);
+    EXPECT_EQ(i, v2[i]);
+  }
+
+  SwapVector<int32_t> v3(alloc);
+  v3.reserve(500000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v3.push_back(i);
+    EXPECT_EQ(i, v2[i]);
+  }
+
+  // Verify contents.
+  for (int32_t i = 0; i < 1000000; ++i) {
+    EXPECT_EQ(i, v[i]);
+    EXPECT_EQ(i, v2[i]);
+    EXPECT_EQ(i, v3[i]);
+  }
+
+  scratch.Close();
+}
+
+TEST_F(SwapSpaceTest, Memory) {
+  SwapTest(false);
+}
+
+TEST_F(SwapSpaceTest, Swap) {
+  SwapTest(true);
+}
+
+}  // namespace art
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index a06b5c5..0ef20d6 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -27,7 +27,9 @@
 LOCAL_SRC_FILES := dalvikvm.cc
 LOCAL_CFLAGS := $(dalvikvm_cflags)
 LOCAL_C_INCLUDES := art/runtime
-LOCAL_SHARED_LIBRARIES := libdl libnativehelper
+LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper
+LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
 LOCAL_MULTILIB := both
@@ -54,7 +56,12 @@
 LOCAL_CFLAGS := $(dalvikvm_cflags)
 LOCAL_C_INCLUDES := art/runtime
 LOCAL_SHARED_LIBRARIES := libnativehelper
+LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
 LOCAL_LDFLAGS := -ldl -lpthread
+# Mac OS linker doesn't understand --export-dynamic.
+ifneq ($(HOST_OS),darwin)
+  LOCAL_LDFLAGS += -Wl,--export-dynamic
+endif
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
 LOCAL_IS_HOST_MODULE := true
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 544b063..c411472 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -229,6 +229,12 @@
   UsageError("  --disable-passes=<pass-names>:  disable one or more passes separated by comma.");
   UsageError("      Example: --disable-passes=UseCount,BBOptimizations");
   UsageError("");
+  UsageError("  --swap-file=<file-name>:  specifies a file to use for swap.");
+  UsageError("      Example: --swap-file=/data/tmp/swap.001");
+  UsageError("");
+  UsageError("  --swap-fd=<file-descriptor>:  specifies a file to use for swap (by descriptor).");
+  UsageError("      Example: --swap-fd=10");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -264,12 +270,12 @@
 
   ~Dex2Oat() {
     delete runtime_;
-    LogCompletionTime();
   }
 
-  void LogCompletionTime() {
+  void LogCompletionTime(const CompilerDriver* compiler) {
     LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
-              << " (threads: " << thread_count_ << ")";
+              << " (threads: " << thread_count_ << ") "
+              << compiler->GetMemoryUsageString(kIsDebugBuild || VLOG_IS_ON(compiler));
   }
 
 
@@ -358,6 +364,7 @@
                                       bool dump_passes,
                                       TimingLogger& timings,
                                       CumulativeLogger& compiler_phases_timings,
+                                      int swap_fd,
                                       std::string profile_file,
                                       SafeMap<std::string, std::string>* key_value_store) {
     CHECK(key_value_store != nullptr);
@@ -393,6 +400,7 @@
                                                               dump_stats,
                                                               dump_passes,
                                                               &compiler_phases_timings,
+                                                              swap_fd,
                                                               profile_file));
 
     driver->GetCompiler()->SetBitcodeFileName(*driver.get(), bitcode_filename);
@@ -428,19 +436,25 @@
     t2.NewTiming("Writing ELF");
     if (!driver->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
       LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
+      oat_file->Erase();
       return nullptr;
     }
 
     // Flush result to disk. Patching code will re-open the file (mmap), so ensure that our view
     // of the file already made it there and won't be re-ordered with writes from PatchOat or
     // image patching.
-    oat_file->Flush();
+    if (oat_file->Flush() != 0) {
+      PLOG(ERROR) << "Failed flushing oat file " << oat_file->GetPath();
+      oat_file->Erase();
+      return nullptr;
+    }
 
     if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) {
       t2.NewTiming("Patching ELF");
       std::string error_msg;
       if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
         LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath() << ": " << error_msg;
+        oat_file->Erase();
         return nullptr;
       }
     }
@@ -466,15 +480,23 @@
       oat_data_begin = image_writer.GetOatDataBegin();
     }
 
-    std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
-    if (oat_file.get() == nullptr) {
-      PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
-      return false;
-    }
+
     // Do not fix up the ELF file if we are --compile-pic
     if (!compiler_options_->GetCompilePic()) {
+      std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
+      if (oat_file.get() == nullptr) {
+        PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
+        return false;
+      }
+
       if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
         LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+        oat_file->Erase();
+        return false;
+      }
+
+      if (oat_file->FlushCloseOrErase() != 0) {
+        PLOG(ERROR) << "Failed to flush and close patched oat file " << oat_filename;
         return false;
       }
     }
@@ -789,6 +811,25 @@
 #endif
 }
 
+static constexpr size_t kMinDexFilesForSwap = 2;
+static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
+
+static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
+  if (is_image) {
+    // Don't use swap, we know generation should succeed, and we don't want to slow it down.
+    return false;
+  }
+  if (dex_files.size() < kMinDexFilesForSwap) {
+    // If there are less dex files than the threshold, assume it's gonna be fine.
+    return false;
+  }
+  size_t dex_files_size = 0;
+  for (const auto* dex_file : dex_files) {
+    dex_files_size += dex_file->GetHeader().file_size_;
+  }
+  return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
+}
+
 static int dex2oat(int argc, char** argv) {
   b13564922();
 
@@ -863,6 +904,10 @@
   bool implicit_so_checks = false;
   bool implicit_suspend_checks = false;
 
+  // Swap file.
+  std::string swap_file_name;
+  int swap_fd = -1;  // No swap file descriptor;
+
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
     const bool log_options = false;
@@ -1049,6 +1094,16 @@
       include_patch_information = true;
     } else if (option == "--no-include-patch-information") {
       include_patch_information = false;
+    } else if (option.starts_with("--swap-file=")) {
+      swap_file_name = option.substr(strlen("--swap-file=")).data();
+    } else if (option.starts_with("--swap-fd=")) {
+      const char* swap_fd_str = option.substr(strlen("--swap-fd=")).data();
+      if (!ParseInt(swap_fd_str, &swap_fd)) {
+        Usage("Failed to parse --swap-fd argument '%s' as an integer", swap_fd_str);
+      }
+      if (swap_fd < 0) {
+        Usage("--swap-fd passed a negative value %d", swap_fd);
+      }
     } else {
       Usage("Unknown argument %s", option.data());
     }
@@ -1235,9 +1290,11 @@
       oat_location = oat_filename;
     }
   } else {
-    oat_file.reset(new File(oat_fd, oat_location));
+    oat_file.reset(new File(oat_fd, oat_location, true));
     oat_file->DisableAutoClose();
-    oat_file->SetLength(0);
+    if (oat_file->SetLength(0)) {  // Only warn for truncation error.
+      PLOG(WARNING) << "Truncating oat file " << oat_location << " failed.";
+    }
   }
   if (oat_file.get() == nullptr) {
     PLOG(ERROR) << "Failed to create oat file: " << oat_location;
@@ -1245,9 +1302,29 @@
   }
   if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
     PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location;
+    oat_file->Erase();
     return EXIT_FAILURE;
   }
 
+  // Swap file handling.
+  //
+  // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file
+  // that we can use for swap.
+  //
+  // If the swap fd is -1 and we have a swap-file string, open the given file as a swap file. We
+  // will immediately unlink to satisfy the swap fd assumption.
+  std::unique_ptr<File> swap_file;
+  if (swap_fd == -1 && !swap_file_name.empty()) {
+    swap_file.reset(OS::CreateEmptyFile(swap_file_name.c_str()));
+    if (swap_file.get() == nullptr) {
+      PLOG(ERROR) << "Failed to create swap file: " << swap_file_name;
+      return EXIT_FAILURE;
+    }
+    swap_fd = swap_file->Fd();
+    swap_file->MarkUnchecked();  // We don't we to track this, it will be unlinked immediately.
+    unlink(swap_file_name.c_str());
+  }
+
   timings.StartTiming("dex2oat Setup");
   LOG(INFO) << CommandLine();
 
@@ -1258,6 +1335,7 @@
     size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
     if (failure_count > 0) {
       LOG(ERROR) << "Failed to open some dex files: " << failure_count;
+      oat_file->Erase();
       return EXIT_FAILURE;
     }
     runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
@@ -1277,6 +1355,11 @@
       std::make_pair("imageinstructionset",
                      reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
 
+  if (swap_fd != -1) {
+    // Swap file indicates low-memory mode. Use GC.
+    runtime_options.push_back(std::make_pair("-Xgc:MS", nullptr));
+  }
+
   Dex2Oat* p_dex2oat;
   if (!Dex2Oat::Create(&p_dex2oat,
                        runtime_options,
@@ -1289,6 +1372,7 @@
                        thread_count)) {
     LOG(ERROR) << "Failed to create dex2oat";
     timings.EndTiming();
+    oat_file->Erase();
     return EXIT_FAILURE;
   }
   std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat);
@@ -1318,6 +1402,7 @@
       LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
           "': " << error_msg;
       timings.EndTiming();
+      oat_file->Erase();
       return EXIT_FAILURE;
     }
   } else if (image) {
@@ -1339,6 +1424,7 @@
       LOG(ERROR) << "Failed to create list of compiled classes from '" << compiled_classes_filename
                  << "': " << error_msg;
       timings.EndTiming();
+      oat_file->Erase();
       return EXIT_FAILURE;
     }
   } else if (image) {
@@ -1358,12 +1444,14 @@
         LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
             << error_msg;
         timings.EndTiming();
+        oat_file->Erase();
         return EXIT_FAILURE;
       }
       if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location, &error_msg, &dex_files)) {
         LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location
             << "': " << error_msg;
         timings.EndTiming();
+        oat_file->Erase();
         return EXIT_FAILURE;
       }
       ATRACE_END();
@@ -1372,6 +1460,7 @@
       if (failure_count > 0) {
         LOG(ERROR) << "Failed to open some dex files: " << failure_count;
         timings.EndTiming();
+        oat_file->Erase();
         return EXIT_FAILURE;
       }
     }
@@ -1387,7 +1476,10 @@
                         << ". Try: adb shell chmod 777 /data/local/tmp";
             continue;
         }
-        tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
+        // This is just dumping files for debugging. Ignore errors, and leave remnants.
+        UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()));
+        UNUSED(tmp_file->Flush());
+        UNUSED(tmp_file->Close());
         LOG(INFO) << "Wrote input to " << tmp_file_name;
       }
     }
@@ -1398,6 +1490,16 @@
       PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n";
     }
   }
+  // If we use a swap file, ensure we are above the threshold to make it necessary.
+  if (swap_fd != -1) {
+    if (!UseSwap(image, dex_files)) {
+      close(swap_fd);
+      swap_fd = -1;
+      LOG(INFO) << "Decided to run without swap.";
+    } else {
+      LOG(INFO) << "Accepted running with swap.";
+    }
+  }
 
   /*
    * If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
@@ -1450,6 +1552,7 @@
                                                                         dump_passes,
                                                                         timings,
                                                                         compiler_phases_timings,
+                                                                        swap_fd,
                                                                         profile_file,
                                                                         key_value_store.get()));
   if (compiler.get() == nullptr) {
@@ -1458,8 +1561,16 @@
     return EXIT_FAILURE;
   }
 
-  VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location;
+  if (!kUsePortableCompiler) {
+    if (oat_file->FlushCloseOrErase() != 0) {
+      PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location;
+      timings.EndTiming();
+      return EXIT_FAILURE;
+    }
+    oat_file.reset();
+  }
 
+  VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location;
   // Notes on the interleaving of creating the image and oat file to
   // ensure the references between the two are correct.
   //
@@ -1539,8 +1650,14 @@
   // We need to strip after image creation because FixupElf needs to use .strtab.
   if (oat_unstripped != oat_stripped) {
     TimingLogger::ScopedTiming t("dex2oat OatFile copy", &timings);
-    oat_file.reset();
-     std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped.c_str()));
+    if (kUsePortableCompiler) {
+      if (oat_file->FlushCloseOrErase() != 0) {
+        PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location;
+        return EXIT_FAILURE;
+      }
+      oat_file.reset();
+    }
+    std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped.c_str()));
     std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped.c_str()));
     size_t buffer_size = 8192;
     std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
@@ -1556,22 +1673,34 @@
     VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped;
   }
 
-#if ART_USE_PORTABLE_COMPILER  // We currently only generate symbols on Portable
-  if (!compiler_options.GetIncludeDebugSymbols()) {
-    timings.NewSplit("dex2oat ElfStripper");
-    // Strip unneeded sections for target
-    off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
-    CHECK_EQ(0, seek_actual);
-    std::string error_msg;
-    CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
+  if (kUsePortableCompiler) {
+    if (!compiler_options->GetIncludeDebugSymbols()) {
+      timings.NewTiming("dex2oat ElfStripper");
+      // Strip unneeded sections for target
+      off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
+      CHECK_EQ(0, seek_actual);
+      std::string error_msg;
+      CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
 
 
-    // We wrote the oat file successfully, and want to keep it.
-    VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
-  } else {
-    VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location;
+      // We wrote the oat file successfully, and want to keep it.
+      VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
+    } else {
+      VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location;
+    }
+    if (oat_file->FlushCloseOrErase() != 0) {
+      LOG(ERROR) << "Failed to flush and close oat file: " << oat_location;
+      return EXIT_FAILURE;
+    }
+    oat_file.reset(nullptr);
   }
-#endif  // ART_USE_PORTABLE_COMPILER
+
+  if (oat_file.get() != nullptr) {
+    if (oat_file->FlushCloseOrErase() != 0) {
+      PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location << "/" << oat_filename;
+      return EXIT_FAILURE;
+    }
+  }
 
   timings.EndTiming();
 
@@ -1582,10 +1711,10 @@
     LOG(INFO) << Dumpable<CumulativeLogger>(compiler_phases_timings);
   }
 
+  dex2oat->LogCompletionTime(compiler.get());
   // Everything was successfully written, do an explicit exit here to avoid running Runtime
   // destructors that take time (bug 10645725) unless we're a debug build or running on valgrind.
   if (!kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
-    dex2oat->LogCompletionTime();
     exit(EXIT_SUCCESS);
   }
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 926a04a..4734a9c 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -108,6 +108,7 @@
 const char* image_roots_descriptions_[] = {
   "kResolutionMethod",
   "kImtConflictMethod",
+  "kImtUnimplementedMethod",
   "kDefaultImt",
   "kCalleeSaveMethod",
   "kRefsOnlySaveMethod",
@@ -142,7 +143,8 @@
     : oat_file_(oat_file),
       oat_dex_files_(oat_file.GetOatDexFiles()),
       options_(options),
-      disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet(),
+      instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()),
+      disassembler_(Disassembler::Create(instruction_set_,
                                          new DisassemblerOptions(options_->absolute_addresses_,
                                                                  oat_file.Begin()))) {
     AddAllOffsets();
@@ -153,6 +155,10 @@
     delete disassembler_;
   }
 
+  InstructionSet GetInstructionSet() {
+    return instruction_set_;
+  }
+
   bool Dump(std::ostream& os) {
     bool success = true;
     const OatHeader& oat_header = oat_file_.GetOatHeader();
@@ -264,7 +270,7 @@
     return end_offset - begin_offset;
   }
 
-  InstructionSet GetInstructionSet() {
+  InstructionSet GetOatInstructionSet() {
     return oat_file_.GetOatHeader().GetInstructionSet();
   }
 
@@ -278,8 +284,9 @@
         LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
             << "': " << error_msg;
       } else {
+        const char* descriptor = m->GetDeclaringClassDescriptor();
         const DexFile::ClassDef* class_def =
-            dex_file->FindClassDef(m->GetDeclaringClassDescriptor());
+            dex_file->FindClassDef(descriptor, ComputeModifiedUtf8Hash(descriptor));
         if (class_def != nullptr) {
           uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def);
           const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
@@ -348,7 +355,7 @@
     offsets_.insert(code_offset);
     offsets_.insert(oat_method.GetMappingTableOffset());
     offsets_.insert(oat_method.GetVmapTableOffset());
-    offsets_.insert(oat_method.GetNativeGcMapOffset());
+    offsets_.insert(oat_method.GetGcMapOffset());
   }
 
   bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) {
@@ -493,9 +500,9 @@
 
       *indent2_os << "gc_map: ";
       if (options_->absolute_addresses_) {
-        *indent2_os << StringPrintf("%p ", oat_method.GetNativeGcMap());
+        *indent2_os << StringPrintf("%p ", oat_method.GetGcMap());
       }
-      uint32_t gc_map_offset = oat_method.GetNativeGcMapOffset();
+      uint32_t gc_map_offset = oat_method.GetGcMapOffset();
       *indent2_os << StringPrintf("(offset=0x%08x)\n", gc_map_offset);
       if (gc_map_offset > oat_file_.Size()) {
         *indent2_os << StringPrintf("WARNING: "
@@ -737,7 +744,7 @@
   }
   void DumpGcMap(std::ostream& os, const OatFile::OatMethod& oat_method,
                  const DexFile::CodeItem* code_item) {
-    const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
+    const uint8_t* gc_map_raw = oat_method.GetGcMap();
     if (gc_map_raw == nullptr) {
       return;  // No GC map.
     }
@@ -815,7 +822,7 @@
 
   void DumpGcMapAtNativePcOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
                                  const DexFile::CodeItem* code_item, size_t native_pc_offset) {
-    const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
+    const uint8_t* gc_map_raw = oat_method.GetGcMap();
     if (gc_map_raw != nullptr) {
       NativePcOffsetToReferenceMap map(gc_map_raw);
       if (map.HasEntry(native_pc_offset)) {
@@ -949,6 +956,7 @@
   const OatFile& oat_file_;
   const std::vector<const OatFile::OatDexFile*> oat_dex_files_;
   const OatDumperOptions* options_;
+  InstructionSet instruction_set_;
   std::set<uintptr_t> offsets_;
   Disassembler* disassembler_;
 };
@@ -1199,7 +1207,8 @@
 
   const void* GetQuickOatCodeBegin(mirror::ArtMethod* m)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    const void* quick_code = m->GetEntryPointFromQuickCompiledCode();
+    const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
+        InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()));
     if (quick_code == Runtime::Current()->GetClassLinker()->GetQuickResolutionTrampoline()) {
       quick_code = oat_dumper_->GetQuickOatCode(m);
     }
@@ -1300,11 +1309,13 @@
         }
       }
     } else if (obj->IsArtMethod()) {
+      const size_t image_pointer_size = InstructionSetPointerSize(
+          state->oat_dumper_->GetOatInstructionSet());
       mirror::ArtMethod* method = obj->AsArtMethod();
       if (method->IsNative()) {
         // TODO: portable dumping.
-        DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method);
-        DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method);
+        DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method);
+        DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
         bool first_occurrence;
         const void* quick_oat_code = state->GetQuickOatCodeBegin(method);
         uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
@@ -1312,33 +1323,35 @@
         if (first_occurrence) {
           state->stats_.native_to_managed_code_bytes += quick_oat_code_size;
         }
-        if (quick_oat_code != method->GetEntryPointFromQuickCompiledCode()) {
+        if (quick_oat_code != method->GetEntryPointFromQuickCompiledCodePtrSize(
+            image_pointer_size)) {
           indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code);
         }
       } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
           method->IsResolutionMethod() || method->IsImtConflictMethod() ||
-          method->IsClassInitializer()) {
-        DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method);
-        DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method);
+          method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
+        DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method);
+        DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
       } else {
         const DexFile::CodeItem* code_item = method->GetCodeItem();
         size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
         state->stats_.dex_instruction_bytes += dex_instruction_bytes;
 
         bool first_occurrence;
-        size_t gc_map_bytes = state->ComputeOatSize(method->GetNativeGcMap(), &first_occurrence);
+        size_t gc_map_bytes =
+            state->ComputeOatSize(method->GetNativeGcMap(image_pointer_size), &first_occurrence);
         if (first_occurrence) {
           state->stats_.gc_map_bytes += gc_map_bytes;
         }
 
         size_t pc_mapping_table_bytes =
-            state->ComputeOatSize(method->GetMappingTable(), &first_occurrence);
+            state->ComputeOatSize(method->GetMappingTable(image_pointer_size), &first_occurrence);
         if (first_occurrence) {
           state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
         }
 
         size_t vmap_table_bytes =
-            state->ComputeOatSize(method->GetVmapTable(), &first_occurrence);
+            state->ComputeOatSize(method->GetVmapTable(image_pointer_size), &first_occurrence);
         if (first_occurrence) {
           state->stats_.vmap_table_bytes += vmap_table_bytes;
         }
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index b9637d0..b046ea1 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -176,7 +176,7 @@
   }
   gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace();
 
-  PatchOat p(image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
+  PatchOat p(isa, image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
              delta, timings);
   t.NewTiming("Patching files");
   if (!p.PatchImage()) {
@@ -298,7 +298,7 @@
     CHECK(is_oat_pic == NOT_PIC);
   }
 
-  PatchOat p(elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
+  PatchOat p(isa, elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
              delta, timings);
   t.NewTiming("Patching files");
   if (!skip_patching_oat && !p.PatchElf()) {
@@ -523,41 +523,40 @@
   PatchOat::PatchVisitor visitor(this, copy);
   object->VisitReferences<true, kVerifyNone>(visitor, visitor);
   if (object->IsArtMethod<kVerifyNone>()) {
-    FixupMethod(static_cast<mirror::ArtMethod*>(object),
-                static_cast<mirror::ArtMethod*>(copy));
+    FixupMethod(down_cast<mirror::ArtMethod*>(object), down_cast<mirror::ArtMethod*>(copy));
   }
 }
 
 void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) {
+  const size_t pointer_size = InstructionSetPointerSize(isa_);
   // Just update the entry points if it looks like we should.
   // TODO: sanity check all the pointers' values
 #if defined(ART_USE_PORTABLE_COMPILER)
   uintptr_t portable = reinterpret_cast<uintptr_t>(
-      object->GetEntryPointFromPortableCompiledCode<kVerifyNone>());
+      object->GetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(pointer_size));
   if (portable != 0) {
-    copy->SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(portable + delta_));
+    copy->SetEntryPointFromPortableCompiledCodePtrSize(reinterpret_cast<void*>(portable + delta_),
+                                                       pointer_size);
   }
 #endif
   uintptr_t quick= reinterpret_cast<uintptr_t>(
-      object->GetEntryPointFromQuickCompiledCode<kVerifyNone>());
+      object->GetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(pointer_size));
   if (quick != 0) {
-    copy->SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(quick + delta_));
+    copy->SetEntryPointFromQuickCompiledCodePtrSize(reinterpret_cast<void*>(quick + delta_),
+                                                    pointer_size);
   }
   uintptr_t interpreter = reinterpret_cast<uintptr_t>(
-      object->GetEntryPointFromInterpreter<kVerifyNone>());
+      object->GetEntryPointFromInterpreterPtrSize<kVerifyNone>(pointer_size));
   if (interpreter != 0) {
-    copy->SetEntryPointFromInterpreter(
-        reinterpret_cast<mirror::EntryPointFromInterpreter*>(interpreter + delta_));
+    copy->SetEntryPointFromInterpreterPtrSize(
+        reinterpret_cast<mirror::EntryPointFromInterpreter*>(interpreter + delta_), pointer_size);
   }
 
-  uintptr_t native_method = reinterpret_cast<uintptr_t>(object->GetNativeMethod());
+  uintptr_t native_method = reinterpret_cast<uintptr_t>(
+      object->GetEntryPointFromJniPtrSize(pointer_size));
   if (native_method != 0) {
-    copy->SetNativeMethod(reinterpret_cast<void*>(native_method + delta_));
-  }
-
-  uintptr_t native_gc_map = reinterpret_cast<uintptr_t>(object->GetNativeGcMap());
-  if (native_gc_map != 0) {
-    copy->SetNativeGcMap(reinterpret_cast<uint8_t*>(native_gc_map + delta_));
+    copy->SetEntryPointFromJniPtrSize(reinterpret_cast<void*>(native_method + delta_),
+                                      pointer_size);
   }
 }
 
@@ -895,6 +894,20 @@
   }
 }
 
+// Either try to close the file (close=true), or erase it.
+static bool FinishFile(File* file, bool close) {
+  if (close) {
+    if (file->FlushCloseOrErase() != 0) {
+      PLOG(ERROR) << "Failed to flush and close file.";
+      return false;
+    }
+    return true;
+  } else {
+    file->Erase();
+    return false;
+  }
+}
+
 static int patchoat(int argc, char **argv) {
   InitLogging(argv);
   MemMap::Init();
@@ -1166,7 +1179,7 @@
       if (output_image_filename.empty()) {
         output_image_filename = "output-image-file";
       }
-      output_image.reset(new File(output_image_fd, output_image_filename));
+      output_image.reset(new File(output_image_fd, output_image_filename, true));
     } else {
       CHECK(!output_image_filename.empty());
       output_image.reset(CreateOrOpen(output_image_filename.c_str(), &new_image_out));
@@ -1180,7 +1193,7 @@
       if (input_oat_filename.empty()) {
         input_oat_filename = "input-oat-file";
       }
-      input_oat.reset(new File(input_oat_fd, input_oat_filename));
+      input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
       if (input_oat == nullptr) {
         // Unlikely, but ensure exhaustive logging in non-0 exit code case
         LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
@@ -1199,7 +1212,7 @@
       if (output_oat_filename.empty()) {
         output_oat_filename = "output-oat-file";
       }
-      output_oat.reset(new File(output_oat_fd, output_oat_filename));
+      output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
       if (output_oat == nullptr) {
         // Unlikely, but ensure exhaustive logging in non-0 exit code case
         LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
@@ -1272,14 +1285,20 @@
                           output_oat.get(), output_image.get(), isa, &timings,
                           output_oat_fd >= 0,  // was it opened from FD?
                           new_oat_out);
+    // The order here doesn't matter. If the first one is successfully saved and the second one
+    // erased, ImageSpace will still detect a problem and not use the files.
+    ret = ret && FinishFile(output_image.get(), ret);
+    ret = ret && FinishFile(output_oat.get(), ret);
   } else if (have_oat_files) {
     TimingLogger::ScopedTiming pt("patch oat", &timings);
     ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
                           output_oat_fd >= 0,  // was it opened from FD?
                           new_oat_out);
+    ret = ret && FinishFile(output_oat.get(), ret);
   } else if (have_image_files) {
     TimingLogger::ScopedTiming pt("patch image", &timings);
     ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
+    ret = ret && FinishFile(output_image.get(), ret);
   } else {
     CHECK(false);
     ret = true;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 21041fb..03d915a 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -60,15 +60,16 @@
  private:
   // Takes ownership only of the ElfFile. All other pointers are only borrowed.
   PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings)
-      : oat_file_(oat_file), delta_(delta), timings_(timings) {}
-  PatchOat(MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
+      : oat_file_(oat_file), delta_(delta), isa_(kNone), timings_(timings) {}
+  PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
            MemMap* heap, off_t delta, TimingLogger* timings)
       : image_(image), bitmap_(bitmap), heap_(heap),
-        delta_(delta), timings_(timings) {}
-  PatchOat(ElfFile* oat_file, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
-           MemMap* heap, off_t delta, TimingLogger* timings)
+        delta_(delta), isa_(isa), timings_(timings) {}
+  PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image,
+           gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta,
+           TimingLogger* timings)
       : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap),
-        delta_(delta), timings_(timings) {}
+        delta_(delta), isa_(isa), timings_(timings) {}
   ~PatchOat() {}
 
   // Was the .art image at image_path made with --compile-pic ?
@@ -147,6 +148,9 @@
   const MemMap* heap_;
   // The amount we are changing the offset by.
   off_t delta_;
+  // Active instruction set, used to know the entrypoint size.
+  const InstructionSet isa_;
+
   TimingLogger* timings_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat);
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 17ee8ab..84f2248 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -295,8 +295,10 @@
   arch/x86_64/registers_x86_64.h \
   base/allocator.h \
   base/mutex.h \
+  base/unix_file/fd_file.h \
   dex_file.h \
   dex_instruction.h \
+  gc_root.h \
   gc/collector/gc_type.h \
   gc/collector_type.h \
   gc/space/space.h \
@@ -309,7 +311,6 @@
   lock_word.h \
   mirror/class.h \
   oat.h \
-  object_callbacks.h \
   quick/inline_method_analyser.h \
   thread.h \
   thread_state.h \
diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S
index 3491c18..a714bca 100644
--- a/runtime/arch/arm/portable_entrypoints_arm.S
+++ b/runtime/arch/arm/portable_entrypoints_arm.S
@@ -53,7 +53,7 @@
     mov    ip, #0                          @ set ip to 0
     str    ip, [sp]                        @ store NULL for method* at bottom of frame
     add    sp, #16                         @ first 4 args are not passed on stack for portable
-    ldr    ip, [r0, #METHOD_PORTABLE_CODE_OFFSET]  @ get pointer to the code
+    ldr    ip, [r0, #METHOD_PORTABLE_CODE_OFFSET_32]  @ get pointer to the code
     blx    ip                              @ call the method
     mov    sp, r11                         @ restore the stack pointer
     ldr    ip, [sp, #24]                   @ load the result pointer
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1b30c9c..26e6937 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -322,7 +322,7 @@
     ldr    r3, [sp, #12]                   @ copy arg value for r3
     mov    ip, #0                          @ set ip to 0
     str    ip, [sp]                        @ store NULL for method* at bottom of frame
-    ldr    ip, [r0, #METHOD_QUICK_CODE_OFFSET]  @ get pointer to the code
+    ldr    ip, [r0, #METHOD_QUICK_CODE_OFFSET_32]  @ get pointer to the code
     blx    ip                              @ call the method
     mov    sp, r11                         @ restore the stack pointer
     ldr    ip, [sp, #24]                   @ load the result pointer
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 2a19e27..3c5db50 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -551,7 +551,7 @@
 .macro INVOKE_STUB_CALL_AND_RETURN
 
     // load method-> METHOD_QUICK_CODE_OFFSET
-    ldr x9, [x0 , #METHOD_QUICK_CODE_OFFSET]
+    ldr x9, [x0 , #METHOD_QUICK_CODE_OFFSET_64]
     // Branch to method.
     blr x9
 
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 4db5ea6..6add93b 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -22,9 +22,9 @@
 // Offset of field Thread::tls32_.state_and_flags verified in InitCpu
 #define THREAD_FLAGS_OFFSET 0
 // Offset of field Thread::tlsPtr_.card_table verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 112
+#define THREAD_CARD_TABLE_OFFSET 120
 // Offset of field Thread::tlsPtr_.exception verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 116
+#define THREAD_EXCEPTION_OFFSET 124
 
 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64
diff --git a/runtime/arch/mips/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S
index 7545ce0..1e9fe33 100644
--- a/runtime/arch/mips/portable_entrypoints_mips.S
+++ b/runtime/arch/mips/portable_entrypoints_mips.S
@@ -100,7 +100,7 @@
     lw    $a1, 4($sp)           # copy arg value for a1
     lw    $a2, 8($sp)           # copy arg value for a2
     lw    $a3, 12($sp)          # copy arg value for a3
-    lw    $t9, METHOD_PORTABLE_CODE_OFFSET($a0)  # get pointer to the code
+    lw    $t9, METHOD_PORTABLE_CODE_OFFSET_32($a0)  # get pointer to the code
     jalr  $t9                   # call the method
     sw    $zero, 0($sp)         # store NULL for method* at bottom of frame
     move  $sp, $fp              # restore the stack
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 8786222..08b74c6 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -507,7 +507,7 @@
     lw    $a1, 4($sp)           # copy arg value for a1
     lw    $a2, 8($sp)           # copy arg value for a2
     lw    $a3, 12($sp)          # copy arg value for a3
-    lw    $t9, METHOD_QUICK_CODE_OFFSET($a0)  # get pointer to the code
+    lw    $t9, METHOD_QUICK_CODE_OFFSET_32($a0)  # get pointer to the code
     jalr  $t9                   # call the method
     sw    $zero, 0($sp)         # store NULL for method* at bottom of frame
     move  $sp, $fp              # restore the stack
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
index 9365795..f8e05dd 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -46,7 +46,7 @@
     addl LITERAL(12), %esp        // pop arguments to memcpy
     mov 12(%ebp), %eax            // move method pointer into eax
     mov %eax, (%esp)              // push method pointer onto stack
-    call *METHOD_PORTABLE_CODE_OFFSET(%eax) // call the method
+    call *METHOD_PORTABLE_CODE_OFFSET_32(%eax) // call the method
     mov %ebp, %esp                // restore stack pointer
     POP ebx                       // pop ebx
     POP ebp                       // pop ebp
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 75c8646..6a10755 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -292,7 +292,7 @@
     mov 4(%esp), %ecx             // copy arg1 into ecx
     mov 8(%esp), %edx             // copy arg2 into edx
     mov 12(%esp), %ebx            // copy arg3 into ebx
-    call *METHOD_QUICK_CODE_OFFSET(%eax) // call the method
+    call *METHOD_QUICK_CODE_OFFSET_32(%eax) // call the method
     mov %ebp, %esp                // restore stack pointer
     CFI_DEF_CFA_REGISTER(esp)
     POP ebx                       // pop ebx
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 5798092..0de8dfd 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -481,7 +481,7 @@
     LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished
     LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished
 .Lgpr_setup_finished:
-    call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+    call *METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
     movq %rbp, %rsp               // Restore stack pointer.
     CFI_DEF_CFA_REGISTER(rsp)
     POP r9                        // Pop r9 - shorty*.
@@ -564,7 +564,7 @@
     LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2
     LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
 .Lgpr_setup_finished2:
-    call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+    call *METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
     movq %rbp, %rsp               // Restore stack pointer.
     CFI_DEF_CFA_REGISTER(rsp)
     POP r9                        // Pop r9 - shorty*.
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5978443..98fe0fa 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -21,7 +21,7 @@
 
 // Value loaded into rSUSPEND for quick. When this value is counted down to zero we do a suspend
 // check.
-#define SUSPEND_CHECK_INTERVAL (1000)
+#define SUSPEND_CHECK_INTERVAL (96)
 
 // Offsets within java.lang.Object.
 #define CLASS_OFFSET 0
@@ -44,13 +44,12 @@
 
 // Offsets within java.lang.Method.
 #define METHOD_DEX_CACHE_METHODS_OFFSET 12
-#if defined(ART_USE_PORTABLE_COMPILER)
-#define METHOD_PORTABLE_CODE_OFFSET 40
-#define METHOD_QUICK_CODE_OFFSET 48
-#else
-#define METHOD_PORTABLE_CODE_OFFSET 40
-#define METHOD_QUICK_CODE_OFFSET 40
-#endif  // ART_USE_PORTABLE_COMPILER
+
+// Verified by object_test.
+#define METHOD_QUICK_CODE_OFFSET_32 44
+#define METHOD_QUICK_CODE_OFFSET_64 56
+#define METHOD_PORTABLE_CODE_OFFSET_32 56
+#define METHOD_PORTABLE_CODE_OFFSET_64 72
 
 #else
 
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 5f43bec..c2d3379 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -52,7 +52,7 @@
   // Pass function is called by the last thread, the count will
   // be decremented to zero and a Broadcast will be made on the
   // condition variable, thus waking this up.
-  if (count_ != 0) {
+  while (count_ != 0) {
     condition_.Wait(self);
   }
 }
@@ -61,7 +61,18 @@
   MutexLock mu(self, lock_);
   SetCountLocked(self, count_ + delta);
   if (count_ != 0) {
-    condition_.TimedWait(self, timeout_ms, 0);
+    uint32_t timeout_ns = 0;
+    uint64_t abs_timeout = NanoTime() + MsToNs(timeout_ms);
+    for (;;) {
+      condition_.TimedWait(self, timeout_ms, timeout_ns);
+      if (count_ == 0) return;
+      // Compute time remaining on timeout.
+      uint64_t now = NanoTime();
+      int64_t time_left = abs_timeout - now;
+      if (time_left <= 0) return;
+      timeout_ns = time_left % (1000*1000);
+      timeout_ms = time_left / (1000*1000);
+    }
   }
 }
 
diff --git a/runtime/barrier.h b/runtime/barrier.h
index a433cac..8ba9372 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -14,6 +14,16 @@
  * limitations under the License.
  */
 
+// CAUTION: THIS IS NOT A FULLY GENERAL BARRIER API.
+
+// It may either be used as a "latch" or single-use barrier, or it may be reused under
+// very limited conditions, e.g. if only Pass(), but not Wait() is called.  Unlike a standard
+// latch API, it is possible to initialize the latch to a count of zero, repeatedly call
+// Pass() or Wait(), and only then set the count using the Increment() method.  Threads at
+// a Wait() are only awoken if the count reaches zero AFTER the decrement is applied.
+// This works because, also unlike most latch APIs, there is no way to Wait() without
+// decrementing the count, and thus nobody can spuriosly wake up on the initial zero.
+
 #ifndef ART_RUNTIME_BARRIER_H_
 #define ART_RUNTIME_BARRIER_H_
 
@@ -22,20 +32,23 @@
 
 namespace art {
 
+// TODO: Maybe give this a better name.
 class Barrier {
  public:
   explicit Barrier(int count);
   virtual ~Barrier();
 
-  // Pass through the barrier, decrements the count but does not block.
+  // Pass through the barrier, decrement the count but do not block.
   void Pass(Thread* self);
 
   // Wait on the barrier, decrement the count.
   void Wait(Thread* self);
 
-  // Set the count to a new value, if the value is 0 then everyone waiting on the condition
-  // variable is resumed.
-  void Init(Thread* self, int count);
+  // The following three calls are only safe if we somehow know that no other thread both
+  // - has been woken up, and
+  // - has not left the Wait() or Increment() call.
+  // If these calls are made in that situation, the offending thread is likely to go back
+  // to sleep, resulting in a deadlock.
 
   // Increment the count by delta, wait on condition if count is non zero.
   void Increment(Thread* self, int delta);
@@ -43,6 +56,10 @@
   // Increment the count by delta, wait on condition if count is non zero, with a timeout
   void Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_);
 
+  // Set the count to a new value.  This should only be used if there is no possibility that
+  // another thread is still in Wait().  See above.
+  void Init(Thread* self, int count);
+
  private:
   void SetCountLocked(Thread* self, int count) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index de348dc..f68a5d4 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -27,22 +27,17 @@
 namespace art {
 class CheckWaitTask : public Task {
  public:
-  CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2,
-                   AtomicInteger* count3)
+  CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2)
       : barrier_(barrier),
         count1_(count1),
-        count2_(count2),
-        count3_(count3) {}
+        count2_(count2) {}
 
   void Run(Thread* self) {
-    LOG(INFO) << "Before barrier 1 " << *self;
+    LOG(INFO) << "Before barrier" << *self;
     ++*count1_;
     barrier_->Wait(self);
     ++*count2_;
-    LOG(INFO) << "Before barrier 2 " << *self;
-    barrier_->Wait(self);
-    ++*count3_;
-    LOG(INFO) << "After barrier 2 " << *self;
+    LOG(INFO) << "After barrier" << *self;
   }
 
   virtual void Finalize() {
@@ -53,7 +48,6 @@
   Barrier* const barrier_;
   AtomicInteger* const count1_;
   AtomicInteger* const count2_;
-  AtomicInteger* const count3_;
 };
 
 class BarrierTest : public CommonRuntimeTest {
@@ -67,31 +61,27 @@
 TEST_F(BarrierTest, CheckWait) {
   Thread* self = Thread::Current();
   ThreadPool thread_pool("Barrier test thread pool", num_threads);
-  Barrier barrier(0);
+  Barrier barrier(num_threads + 1);  // One extra Wait() in main thread.
+  Barrier timeout_barrier(0);  // Only used for sleeping on timeout.
   AtomicInteger count1(0);
   AtomicInteger count2(0);
-  AtomicInteger count3(0);
   for (int32_t i = 0; i < num_threads; ++i) {
-    thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2, &count3));
+    thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2));
   }
   thread_pool.StartWorkers(self);
-  barrier.Increment(self, num_threads);
-  // At this point each thread should have passed through the barrier. The first count should be
-  // equal to num_threads.
-  EXPECT_EQ(num_threads, count1.LoadRelaxed());
-  // Count 3 should still be zero since no thread should have gone past the second barrier.
-  EXPECT_EQ(0, count3.LoadRelaxed());
-  // Now lets tell the threads to pass again.
-  barrier.Increment(self, num_threads);
-  // Count 2 should be equal to num_threads since each thread must have passed the second barrier
-  // at this point.
-  EXPECT_EQ(num_threads, count2.LoadRelaxed());
+  while (count1.LoadRelaxed() != num_threads) {
+    timeout_barrier.Increment(self, 1, 100);  // sleep 100 msecs
+  }
+  // Count 2 should still be zero since no thread should have gone past the barrier.
+  EXPECT_EQ(0, count2.LoadRelaxed());
+  // Perform one additional Wait(), allowing pool threads to proceed.
+  barrier.Wait(self);
   // Wait for all the threads to finish.
   thread_pool.Wait(self, true, false);
-  // All three counts should be equal to num_threads now.
-  EXPECT_EQ(count1.LoadRelaxed(), count2.LoadRelaxed());
-  EXPECT_EQ(count2.LoadRelaxed(), count3.LoadRelaxed());
-  EXPECT_EQ(num_threads, count3.LoadRelaxed());
+  // Both counts should be equal to num_threads now.
+  EXPECT_EQ(count1.LoadRelaxed(), num_threads);
+  EXPECT_EQ(count2.LoadRelaxed(), num_threads);
+  timeout_barrier.Init(self, 0);  // Reset to zero for destruction.
 }
 
 class CheckPassTask : public Task {
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index a7adb02..da5ac29 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -91,7 +91,7 @@
 
 // Tracking allocator, tracks how much memory is used.
 template<class T, AllocatorTag kTag>
-class TrackingAllocatorImpl {
+class TrackingAllocatorImpl : public std::allocator<T> {
  public:
   typedef typename std::allocator<T>::value_type value_type;
   typedef typename std::allocator<T>::size_type size_type;
diff --git a/runtime/base/hash_map.h b/runtime/base/hash_map.h
new file mode 100644
index 0000000..c0f903f
--- /dev/null
+++ b/runtime/base/hash_map.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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_HASH_MAP_H_
+#define ART_RUNTIME_BASE_HASH_MAP_H_
+
+#include <utility>
+
+#include "hash_set.h"
+
+namespace art {
+
+template <typename Fn>
+class HashMapWrapper {
+ public:
+  // Hash function.
+  template <class Key, class Value>
+  size_t operator()(const std::pair<Key, Value>& pair) const {
+    return fn_(pair.first);
+  }
+  template <class Key>
+  size_t operator()(const Key& key) const {
+    return fn_(key);
+  }
+  template <class Key, class Value>
+  bool operator()(const std::pair<Key, Value>& a, const std::pair<Key, Value>& b) const {
+    return fn_(a.first, b.first);
+  }
+  template <class Key, class Value, class Element>
+  bool operator()(const std::pair<Key, Value>& a, const Element& element) const {
+    return fn_(a.first, element);
+  }
+
+ private:
+  Fn fn_;
+};
+
+template <class Key, class Value, class EmptyFn = DefaultEmptyFn<Key>,
+    class HashFn = std::hash<Key>, class Pred = std::equal_to<Key>,
+    class Alloc = std::allocator<std::pair<Key, Value>>>
+class HashMap : public HashSet<std::pair<Key, Value>, EmptyFn, HashMapWrapper<HashFn>,
+                               HashMapWrapper<Pred>, Alloc> {
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_HASH_MAP_H_
diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h
new file mode 100644
index 0000000..992e5b1
--- /dev/null
+++ b/runtime/base/hash_set.h
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2014 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_HASH_SET_H_
+#define ART_RUNTIME_BASE_HASH_SET_H_
+
+#include <functional>
+#include <memory>
+#include <stdint.h>
+#include <utility>
+
+#include "logging.h"
+
+namespace art {
+
+// Returns true if an item is empty.
+template <class T>
+class DefaultEmptyFn {
+ public:
+  void MakeEmpty(T& item) const {
+    item = T();
+  }
+  bool IsEmpty(const T& item) const {
+    return item == T();
+  }
+};
+
+template <class T>
+class DefaultEmptyFn<T*> {
+ public:
+  void MakeEmpty(T*& item) const {
+    item = nullptr;
+  }
+  bool IsEmpty(const T*& item) const {
+    return item == nullptr;
+  }
+};
+
+// Low memory version of a hash set, uses less memory than std::unordered_set since elements aren't
+// boxed. Uses linear probing.
+// EmptyFn needs to implement two functions MakeEmpty(T& item) and IsEmpty(const T& item)
+template <class T, class EmptyFn = DefaultEmptyFn<T>, class HashFn = std::hash<T>,
+    class Pred = std::equal_to<T>, class Alloc = std::allocator<T>>
+class HashSet {
+ public:
+  static constexpr double kDefaultMinLoadFactor = 0.5;
+  static constexpr double kDefaultMaxLoadFactor = 0.9;
+  static constexpr size_t kMinBuckets = 1000;
+
+  class Iterator {
+   public:
+    Iterator(const Iterator&) = default;
+    Iterator(HashSet* hash_set, size_t index) : hash_set_(hash_set), index_(index) {
+    }
+    Iterator& operator=(const Iterator&) = default;
+    bool operator==(const Iterator& other) const {
+      return hash_set_ == other.hash_set_ && index_ == other.index_;
+    }
+    bool operator!=(const Iterator& other) const {
+      return !(*this == other);
+    }
+    Iterator operator++() {  // Value after modification.
+      index_ = NextNonEmptySlot(index_);
+      return *this;
+    }
+    Iterator operator++(int) {
+      Iterator temp = *this;
+      index_ = NextNonEmptySlot(index_);
+      return temp;
+    }
+    T& operator*() {
+      DCHECK(!hash_set_->IsFreeSlot(GetIndex()));
+      return hash_set_->ElementForIndex(index_);
+    }
+    const T& operator*() const {
+      DCHECK(!hash_set_->IsFreeSlot(GetIndex()));
+      return hash_set_->ElementForIndex(index_);
+    }
+    T* operator->() {
+      return &**this;
+    }
+    const T* operator->() const {
+      return &**this;
+    }
+    // TODO: Operator -- --(int)
+
+   private:
+    HashSet* hash_set_;
+    size_t index_;
+
+    size_t GetIndex() const {
+      return index_;
+    }
+    size_t NextNonEmptySlot(size_t index) const {
+      const size_t num_buckets = hash_set_->NumBuckets();
+      DCHECK_LT(index, num_buckets);
+      do {
+        ++index;
+      } while (index < num_buckets && hash_set_->IsFreeSlot(index));
+      return index;
+    }
+
+    friend class HashSet;
+  };
+
+  void Clear() {
+    DeallocateStorage();
+    AllocateStorage(1);
+    num_elements_ = 0;
+    elements_until_expand_ = 0;
+  }
+  HashSet() : num_elements_(0), num_buckets_(0), data_(nullptr),
+      min_load_factor_(kDefaultMinLoadFactor), max_load_factor_(kDefaultMaxLoadFactor) {
+    Clear();
+  }
+  HashSet(const HashSet& other) : num_elements_(0), num_buckets_(0), data_(nullptr) {
+    *this = other;
+  }
+  HashSet(HashSet&& other) : num_elements_(0), num_buckets_(0), data_(nullptr) {
+    *this = std::move(other);
+  }
+  ~HashSet() {
+    DeallocateStorage();
+  }
+  HashSet& operator=(HashSet&& other) {
+    std::swap(data_, other.data_);
+    std::swap(num_buckets_, other.num_buckets_);
+    std::swap(num_elements_, other.num_elements_);
+    std::swap(elements_until_expand_, other.elements_until_expand_);
+    std::swap(min_load_factor_, other.min_load_factor_);
+    std::swap(max_load_factor_, other.max_load_factor_);
+    return *this;
+  }
+  HashSet& operator=(const HashSet& other) {
+    DeallocateStorage();
+    AllocateStorage(other.NumBuckets());
+    for (size_t i = 0; i < num_buckets_; ++i) {
+      ElementForIndex(i) = other.data_[i];
+    }
+    num_elements_ = other.num_elements_;
+    elements_until_expand_ = other.elements_until_expand_;
+    min_load_factor_ = other.min_load_factor_;
+    max_load_factor_ = other.max_load_factor_;
+    return *this;
+  }
+  // Lower case for c++11 for each.
+  Iterator begin() {
+    Iterator ret(this, 0);
+    if (num_buckets_ != 0 && IsFreeSlot(ret.GetIndex())) {
+      ++ret;  // Skip all the empty slots.
+    }
+    return ret;
+  }
+  // Lower case for c++11 for each.
+  Iterator end() {
+    return Iterator(this, NumBuckets());
+  }
+  bool Empty() {
+    return begin() == end();
+  }
+  // Erase algorithm:
+  // Make an empty slot where the iterator is pointing.
+  // Scan fowards until we hit another empty slot.
+  // If an element inbetween doesn't rehash to the range from the current empty slot to the
+  // iterator. It must be before the empty slot, in that case we can move it to the empty slot
+  // and set the empty slot to be the location we just moved from.
+  // Relies on maintaining the invariant that there's no empty slots from the 'ideal' index of an
+  // element to its actual location/index.
+  Iterator Erase(Iterator it) {
+    // empty_index is the index that will become empty.
+    size_t empty_index = it.GetIndex();
+    DCHECK(!IsFreeSlot(empty_index));
+    size_t next_index = empty_index;
+    bool filled = false;  // True if we filled the empty index.
+    while (true) {
+      next_index = NextIndex(next_index);
+      T& next_element = ElementForIndex(next_index);
+      // If the next element is empty, we are done. Make sure to clear the current empty index.
+      if (emptyfn_.IsEmpty(next_element)) {
+        emptyfn_.MakeEmpty(ElementForIndex(empty_index));
+        break;
+      }
+      // Otherwise try to see if the next element can fill the current empty index.
+      const size_t next_hash = hashfn_(next_element);
+      // Calculate the ideal index, if it is within empty_index + 1 to next_index then there is
+      // nothing we can do.
+      size_t next_ideal_index = IndexForHash(next_hash);
+      // Loop around if needed for our check.
+      size_t unwrapped_next_index = next_index;
+      if (unwrapped_next_index < empty_index) {
+        unwrapped_next_index += NumBuckets();
+      }
+      // Loop around if needed for our check.
+      size_t unwrapped_next_ideal_index = next_ideal_index;
+      if (unwrapped_next_ideal_index < empty_index) {
+        unwrapped_next_ideal_index += NumBuckets();
+      }
+      if (unwrapped_next_ideal_index <= empty_index ||
+          unwrapped_next_ideal_index > unwrapped_next_index) {
+        // If the target index isn't within our current range it must have been probed from before
+        // the empty index.
+        ElementForIndex(empty_index) = std::move(next_element);
+        filled = true;  // TODO: Optimize
+        empty_index = next_index;
+      }
+    }
+    --num_elements_;
+    // If we didn't fill the slot then we need go to the next non free slot.
+    if (!filled) {
+      ++it;
+    }
+    return it;
+  }
+  // Find an element, returns end() if not found.
+  // Allows custom K types, example of when this is useful.
+  // Set of Class* sorted by name, want to find a class with a name but can't allocate a dummy
+  // object in the heap for performance solution.
+  template <typename K>
+  Iterator Find(const K& element) {
+    return FindWithHash(element, hashfn_(element));
+  }
+  template <typename K>
+  Iterator FindWithHash(const K& element, size_t hash) {
+    DCHECK_EQ(hashfn_(element), hash);
+    size_t index = IndexForHash(hash);
+    while (true) {
+      T& slot = ElementForIndex(index);
+      if (emptyfn_.IsEmpty(slot)) {
+        return end();
+      }
+      if (pred_(slot, element)) {
+        return Iterator(this, index);
+      }
+      index = NextIndex(index);
+    }
+  }
+  // Insert an element, allows duplicates.
+  void Insert(const T& element) {
+    InsertWithHash(element, hashfn_(element));
+  }
+  void InsertWithHash(const T& element, size_t hash) {
+    DCHECK_EQ(hash, hashfn_(element));
+    if (num_elements_ >= elements_until_expand_) {
+      Expand();
+      DCHECK_LT(num_elements_, elements_until_expand_);
+    }
+    const size_t index = FirstAvailableSlot(IndexForHash(hash));
+    data_[index] = element;
+    ++num_elements_;
+  }
+  size_t Size() const {
+    return num_elements_;
+  }
+  void ShrinkToMaximumLoad() {
+    Resize(Size() / max_load_factor_);
+  }
+  // To distance that inserted elements were probed. Used for measuring how good hash functions
+  // are.
+  size_t TotalProbeDistance() const {
+    size_t total = 0;
+    for (size_t i = 0; i < NumBuckets(); ++i) {
+      const T& element = ElementForIndex(i);
+      if (!emptyfn_.IsEmpty(element)) {
+        size_t ideal_location = IndexForHash(hashfn_(element));
+        if (ideal_location > i) {
+          total += i + NumBuckets() - ideal_location;
+        } else {
+          total += i - ideal_location;
+        }
+      }
+    }
+    return total;
+  }
+  // Calculate the current load factor and return it.
+  double CalculateLoadFactor() const {
+    return static_cast<double>(Size()) / static_cast<double>(NumBuckets());
+  }
+  // Make sure that everything reinserts in the right spot. Returns the number of errors.
+  size_t Verify() {
+    size_t errors = 0;
+    for (size_t i = 0; i < num_buckets_; ++i) {
+      T& element = data_[i];
+      if (!emptyfn_.IsEmpty(element)) {
+        T temp;
+        emptyfn_.MakeEmpty(temp);
+        std::swap(temp, element);
+        size_t first_slot = FirstAvailableSlot(IndexForHash(hashfn_(temp)));
+        if (i != first_slot) {
+          LOG(ERROR) << "Element " << i << " should be in slot " << first_slot;
+          ++errors;
+        }
+        std::swap(temp, element);
+      }
+    }
+    return errors;
+  }
+
+ private:
+  T& ElementForIndex(size_t index) {
+    DCHECK_LT(index, NumBuckets());
+    DCHECK(data_ != nullptr);
+    return data_[index];
+  }
+  const T& ElementForIndex(size_t index) const {
+    DCHECK_LT(index, NumBuckets());
+    DCHECK(data_ != nullptr);
+    return data_[index];
+  }
+  size_t IndexForHash(size_t hash) const {
+    return hash % num_buckets_;
+  }
+  size_t NextIndex(size_t index) const {
+    if (UNLIKELY(++index >= num_buckets_)) {
+      DCHECK_EQ(index, NumBuckets());
+      return 0;
+    }
+    return index;
+  }
+  bool IsFreeSlot(size_t index) const {
+    return emptyfn_.IsEmpty(ElementForIndex(index));
+  }
+  size_t NumBuckets() const {
+    return num_buckets_;
+  }
+  // Allocate a number of buckets.
+  void AllocateStorage(size_t num_buckets) {
+    num_buckets_ = num_buckets;
+    data_ = allocfn_.allocate(num_buckets_);
+    for (size_t i = 0; i < num_buckets_; ++i) {
+      allocfn_.construct(allocfn_.address(data_[i]));
+      emptyfn_.MakeEmpty(data_[i]);
+    }
+  }
+  void DeallocateStorage() {
+    if (num_buckets_ != 0) {
+      for (size_t i = 0; i < NumBuckets(); ++i) {
+        allocfn_.destroy(allocfn_.address(data_[i]));
+      }
+      allocfn_.deallocate(data_, NumBuckets());
+      data_ = nullptr;
+      num_buckets_ = 0;
+    }
+  }
+  // Expand the set based on the load factors.
+  void Expand() {
+    size_t min_index = static_cast<size_t>(Size() / min_load_factor_);
+    if (min_index < kMinBuckets) {
+      min_index = kMinBuckets;
+    }
+    // Resize based on the minimum load factor.
+    Resize(min_index);
+    // When we hit elements_until_expand_, we are at the max load factor and must expand again.
+    elements_until_expand_ = NumBuckets() * max_load_factor_;
+  }
+  // Expand / shrink the table to the new specified size.
+  void Resize(size_t new_size) {
+    DCHECK_GE(new_size, Size());
+    T* old_data = data_;
+    size_t old_num_buckets = num_buckets_;
+    // Reinsert all of the old elements.
+    AllocateStorage(new_size);
+    for (size_t i = 0; i < old_num_buckets; ++i) {
+      T& element = old_data[i];
+      if (!emptyfn_.IsEmpty(element)) {
+        data_[FirstAvailableSlot(IndexForHash(hashfn_(element)))] = std::move(element);
+      }
+      allocfn_.destroy(allocfn_.address(element));
+    }
+    allocfn_.deallocate(old_data, old_num_buckets);
+  }
+  ALWAYS_INLINE size_t FirstAvailableSlot(size_t index) const {
+    while (!emptyfn_.IsEmpty(data_[index])) {
+      index = NextIndex(index);
+    }
+    return index;
+  }
+
+  Alloc allocfn_;  // Allocator function.
+  HashFn hashfn_;  // Hashing function.
+  EmptyFn emptyfn_;  // IsEmpty/SetEmpty function.
+  Pred pred_;  // Equals function.
+  size_t num_elements_;  // Number of inserted elements.
+  size_t num_buckets_;  // Number of hash table buckets.
+  size_t elements_until_expand_;  // Maxmimum number of elements until we expand the table.
+  T* data_;  // Backing storage.
+  double min_load_factor_;
+  double max_load_factor_;
+
+  friend class Iterator;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_HASH_SET_H_
diff --git a/runtime/base/hash_set_test.cc b/runtime/base/hash_set_test.cc
new file mode 100644
index 0000000..5f498d9
--- /dev/null
+++ b/runtime/base/hash_set_test.cc
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 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 "hash_set.h"
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <unordered_set>
+
+#include "common_runtime_test.h"
+#include "hash_map.h"
+
+namespace art {
+
+struct IsEmptyFnString {
+  void MakeEmpty(std::string& item) const {
+    item.clear();
+  }
+  bool IsEmpty(const std::string& item) const {
+    return item.empty();
+  }
+};
+
+class HashSetTest : public CommonRuntimeTest {
+ public:
+  HashSetTest() : seed_(97421), unique_number_(0) {
+  }
+  std::string RandomString(size_t len) {
+    std::ostringstream oss;
+    for (size_t i = 0; i < len; ++i) {
+      oss << static_cast<char>('A' + PRand() % 64);
+    }
+    static_assert(' ' < 'A', "space must be less than a");
+    oss << " " << unique_number_++;  // Relies on ' ' < 'A'
+    return oss.str();
+  }
+  void SetSeed(size_t seed) {
+    seed_ = seed;
+  }
+  size_t PRand() {  // Pseudo random.
+    seed_ = seed_ * 1103515245 + 12345;
+    return seed_;
+  }
+
+ private:
+  size_t seed_;
+  size_t unique_number_;
+};
+
+TEST_F(HashSetTest, TestSmoke) {
+  HashSet<std::string, IsEmptyFnString> hash_set;
+  const std::string test_string = "hello world 1234";
+  ASSERT_TRUE(hash_set.Empty());
+  ASSERT_EQ(hash_set.Size(), 0U);
+  hash_set.Insert(test_string);
+  auto it = hash_set.Find(test_string);
+  ASSERT_EQ(*it, test_string);
+  auto after_it = hash_set.Erase(it);
+  ASSERT_TRUE(after_it == hash_set.end());
+  ASSERT_TRUE(hash_set.Empty());
+  ASSERT_EQ(hash_set.Size(), 0U);
+  it = hash_set.Find(test_string);
+  ASSERT_TRUE(it == hash_set.end());
+}
+
+TEST_F(HashSetTest, TestInsertAndErase) {
+  HashSet<std::string, IsEmptyFnString> hash_set;
+  static constexpr size_t count = 1000;
+  std::vector<std::string> strings;
+  for (size_t i = 0; i < count; ++i) {
+    // Insert a bunch of elements and make sure we can find them.
+    strings.push_back(RandomString(10));
+    hash_set.Insert(strings[i]);
+    auto it = hash_set.Find(strings[i]);
+    ASSERT_TRUE(it != hash_set.end());
+    ASSERT_EQ(*it, strings[i]);
+  }
+  ASSERT_EQ(strings.size(), hash_set.Size());
+  // Try to erase the odd strings.
+  for (size_t i = 1; i < count; i += 2) {
+    auto it = hash_set.Find(strings[i]);
+    ASSERT_TRUE(it != hash_set.end());
+    ASSERT_EQ(*it, strings[i]);
+    hash_set.Erase(it);
+  }
+  // Test removed.
+  for (size_t i = 1; i < count; i += 2) {
+    auto it = hash_set.Find(strings[i]);
+    ASSERT_TRUE(it == hash_set.end());
+  }
+  for (size_t i = 0; i < count; i += 2) {
+    auto it = hash_set.Find(strings[i]);
+    ASSERT_TRUE(it != hash_set.end());
+    ASSERT_EQ(*it, strings[i]);
+  }
+}
+
+TEST_F(HashSetTest, TestIterator) {
+  HashSet<std::string, IsEmptyFnString> hash_set;
+  ASSERT_TRUE(hash_set.begin() == hash_set.end());
+  static constexpr size_t count = 1000;
+  std::vector<std::string> strings;
+  for (size_t i = 0; i < count; ++i) {
+    // Insert a bunch of elements and make sure we can find them.
+    strings.push_back(RandomString(10));
+    hash_set.Insert(strings[i]);
+  }
+  // Make sure we visit each string exactly once.
+  std::map<std::string, size_t> found_count;
+  for (const std::string& s : hash_set) {
+    ++found_count[s];
+  }
+  for (size_t i = 0; i < count; ++i) {
+    ASSERT_EQ(found_count[strings[i]], 1U);
+  }
+  found_count.clear();
+  // Remove all the elements with iterator erase.
+  for (auto it = hash_set.begin(); it != hash_set.end();) {
+    ++found_count[*it];
+    it = hash_set.Erase(it);
+    ASSERT_EQ(hash_set.Verify(), 0U);
+  }
+  for (size_t i = 0; i < count; ++i) {
+    ASSERT_EQ(found_count[strings[i]], 1U);
+  }
+}
+
+TEST_F(HashSetTest, TestSwap) {
+  HashSet<std::string, IsEmptyFnString> hash_seta, hash_setb;
+  std::vector<std::string> strings;
+  static constexpr size_t count = 1000;
+  for (size_t i = 0; i < count; ++i) {
+    strings.push_back(RandomString(10));
+    hash_seta.Insert(strings[i]);
+  }
+  std::swap(hash_seta, hash_setb);
+  hash_seta.Insert("TEST");
+  hash_setb.Insert("TEST2");
+  for (size_t i = 0; i < count; ++i) {
+    strings.push_back(RandomString(10));
+    hash_seta.Insert(strings[i]);
+  }
+}
+
+TEST_F(HashSetTest, TestStress) {
+  HashSet<std::string, IsEmptyFnString> hash_set;
+  std::unordered_multiset<std::string> std_set;
+  std::vector<std::string> strings;
+  static constexpr size_t string_count = 2000;
+  static constexpr size_t operations = 100000;
+  static constexpr size_t target_size = 5000;
+  for (size_t i = 0; i < string_count; ++i) {
+    strings.push_back(RandomString(i % 10 + 1));
+  }
+  const size_t seed = time(nullptr);
+  SetSeed(seed);
+  LOG(INFO) << "Starting stress test with seed " << seed;
+  for (size_t i = 0; i < operations; ++i) {
+    ASSERT_EQ(hash_set.Size(), std_set.size());
+    size_t delta = std::abs(static_cast<ssize_t>(target_size) -
+                            static_cast<ssize_t>(hash_set.Size()));
+    size_t n = PRand();
+    if (n % target_size == 0) {
+      hash_set.Clear();
+      std_set.clear();
+      ASSERT_TRUE(hash_set.Empty());
+      ASSERT_TRUE(std_set.empty());
+    } else  if (n % target_size < delta) {
+      // Skew towards adding elements until we are at the desired size.
+      const std::string& s = strings[PRand() % string_count];
+      hash_set.Insert(s);
+      std_set.insert(s);
+      ASSERT_EQ(*hash_set.Find(s), *std_set.find(s));
+    } else {
+      const std::string& s = strings[PRand() % string_count];
+      auto it1 = hash_set.Find(s);
+      auto it2 = std_set.find(s);
+      ASSERT_EQ(it1 == hash_set.end(), it2 == std_set.end());
+      if (it1 != hash_set.end()) {
+        ASSERT_EQ(*it1, *it2);
+        hash_set.Erase(it1);
+        std_set.erase(it2);
+      }
+    }
+  }
+}
+
+struct IsEmptyStringPair {
+  void MakeEmpty(std::pair<std::string, int>& pair) const {
+    pair.first.clear();
+  }
+  bool IsEmpty(const std::pair<std::string, int>& pair) const {
+    return pair.first.empty();
+  }
+};
+
+TEST_F(HashSetTest, TestHashMap) {
+  HashMap<std::string, int, IsEmptyStringPair> hash_map;
+  hash_map.Insert(std::make_pair(std::string("abcd"), 123));
+  hash_map.Insert(std::make_pair(std::string("abcd"), 124));
+  hash_map.Insert(std::make_pair(std::string("bags"), 444));
+  auto it = hash_map.Find(std::string("abcd"));
+  ASSERT_EQ(it->second, 123);
+  hash_map.Erase(it);
+  it = hash_map.Find(std::string("abcd"));
+  ASSERT_EQ(it->second, 124);
+}
+
+}  // namespace art
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 0d747c3..be29a73 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -276,16 +276,26 @@
   exclusive_owner_ = 0;
 }
 
+// Helper to ignore the lock requirement.
+static bool IsShuttingDown() NO_THREAD_SAFETY_ANALYSIS {
+  Runtime* runtime = Runtime::Current();
+  return runtime == nullptr || runtime->IsShuttingDownLocked();
+}
+
 Mutex::~Mutex() {
+  bool shutting_down = IsShuttingDown();
 #if ART_USE_FUTEXES
   if (state_.LoadRelaxed() != 0) {
-    Runtime* runtime = Runtime::Current();
-    bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current());
-    LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_;
+    LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: "
+                                                 << exclusive_owner_;
   } else {
-    CHECK_EQ(exclusive_owner_, 0U)  << "unexpectedly found an owner on unlocked mutex " << name_;
-    CHECK_EQ(num_contenders_.LoadSequentiallyConsistent(), 0)
-        << "unexpectedly found a contender on mutex " << name_;
+    if (exclusive_owner_ != 0) {
+      LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found an owner on unlocked mutex "
+                                           << name_;
+    }
+    if (num_contenders_.LoadSequentiallyConsistent() != 0) {
+      LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found a contender on mutex " << name_;
+    }
   }
 #else
   // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread
@@ -295,8 +305,6 @@
     errno = rc;
     // TODO: should we just not log at all if shutting down? this could be the logging mutex!
     MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
-    Runtime* runtime = Runtime::Current();
-    bool shutting_down = (runtime == NULL) || runtime->IsShuttingDownLocked();
     PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_;
   }
 #endif
@@ -647,7 +655,13 @@
 void ReaderWriterMutex::Dump(std::ostream& os) const {
   os << name_
       << " level=" << static_cast<int>(level_)
-      << " owner=" << GetExclusiveOwnerTid() << " ";
+      << " owner=" << GetExclusiveOwnerTid()
+#if ART_USE_FUTEXES
+      << " state=" << state_.LoadSequentiallyConsistent()
+      << " num_pending_writers=" << num_pending_writers_.LoadSequentiallyConsistent()
+      << " num_pending_readers=" << num_pending_readers_.LoadSequentiallyConsistent()
+#endif
+      << " ";
   DumpContention(os);
 }
 
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index bf091d0..0e93eee 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -27,6 +27,9 @@
 
 bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
   while (true) {
+    if (file_.get() != nullptr) {
+      UNUSED(file_->FlushCloseOrErase());  // Ignore result.
+    }
     file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR));
     if (file_.get() == NULL) {
       *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno));
@@ -59,7 +62,7 @@
 }
 
 bool ScopedFlock::Init(File* file, std::string* error_msg) {
-  file_.reset(new File(dup(file->Fd())));
+  file_.reset(new File(dup(file->Fd()), true));
   if (file_->Fd() == -1) {
     file_.reset();
     *error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
@@ -89,6 +92,9 @@
   if (file_.get() != NULL) {
     int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
     CHECK_EQ(0, flock_result);
+    if (file_->FlushCloseOrErase() != 0) {
+      PLOG(WARNING) << "Could not close scoped file lock file.";
+    }
   }
 }
 
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index f29a7ec..96d9d31 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -14,28 +14,68 @@
  * limitations under the License.
  */
 
-#include "base/logging.h"
 #include "base/unix_file/fd_file.h"
+
 #include <errno.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "base/logging.h"
+
 namespace unix_file {
 
-FdFile::FdFile() : fd_(-1), auto_close_(true) {
+FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) {
 }
 
-FdFile::FdFile(int fd) : fd_(fd), auto_close_(true) {
+FdFile::FdFile(int fd, bool check_usage)
+    : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
+      fd_(fd), auto_close_(true) {
 }
 
-FdFile::FdFile(int fd, const std::string& path) : fd_(fd), file_path_(path), auto_close_(true) {
+FdFile::FdFile(int fd, const std::string& path, bool check_usage)
+    : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
+      fd_(fd), file_path_(path), auto_close_(true) {
   CHECK_NE(0U, path.size());
 }
 
 FdFile::~FdFile() {
+  if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) {
+    if (guard_state_ < GuardState::kFlushed) {
+      LOG(ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
+    }
+    if (guard_state_ < GuardState::kClosed) {
+      LOG(ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
+    }
+    CHECK_GE(guard_state_, GuardState::kClosed);
+  }
   if (auto_close_ && fd_ != -1) {
-    Close();
+    if (Close() != 0) {
+      PLOG(WARNING) << "Failed to close file " << file_path_;
+    }
+  }
+}
+
+void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) {
+  if (kCheckSafeUsage) {
+    if (guard_state_ < GuardState::kNoCheck) {
+      if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) {
+        LOG(ERROR) << warning;
+      }
+      guard_state_ = target;
+    }
+  }
+}
+
+void FdFile::moveUp(GuardState target, const char* warning) {
+  if (kCheckSafeUsage) {
+    if (guard_state_ < GuardState::kNoCheck) {
+      if (guard_state_ < target) {
+        guard_state_ = target;
+      } else if (target < guard_state_) {
+        LOG(ERROR) << warning;
+      }
+    }
   }
 }
 
@@ -54,11 +94,28 @@
     return false;
   }
   file_path_ = path;
+  static_assert(O_RDONLY == 0, "Readonly flag has unexpected value.");
+  if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) {
+    // Start in the base state (not flushed, not closed).
+    guard_state_ = GuardState::kBase;
+  } else {
+    // We are not concerned with read-only files. In that case, proper flushing and closing is
+    // not important.
+    guard_state_ = GuardState::kNoCheck;
+  }
   return true;
 }
 
 int FdFile::Close() {
   int result = TEMP_FAILURE_RETRY(close(fd_));
+
+  // Test here, so the file is closed and not leaked.
+  if (kCheckSafeUsage) {
+    CHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
+        << " has not been flushed before closing.";
+    moveUp(GuardState::kClosed, nullptr);
+  }
+
   if (result == -1) {
     return -errno;
   } else {
@@ -74,6 +131,7 @@
 #else
   int rc = TEMP_FAILURE_RETRY(fsync(fd_));
 #endif
+  moveUp(GuardState::kFlushed, "Flushing closed file.");
   return (rc == -1) ? -errno : rc;
 }
 
@@ -92,6 +150,7 @@
 #else
   int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length));
 #endif
+  moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file.");
   return (rc == -1) ? -errno : rc;
 }
 
@@ -107,6 +166,7 @@
 #else
   int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset));
 #endif
+  moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
   return (rc == -1) ? -errno : rc;
 }
 
@@ -135,6 +195,7 @@
 
 bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
   const char* ptr = static_cast<const char*>(buffer);
+  moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
   while (byte_count > 0) {
     ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
     if (bytes_written == -1) {
@@ -146,4 +207,42 @@
   return true;
 }
 
+void FdFile::Erase() {
+  TEMP_FAILURE_RETRY(SetLength(0));
+  TEMP_FAILURE_RETRY(Flush());
+  TEMP_FAILURE_RETRY(Close());
+}
+
+int FdFile::FlushCloseOrErase() {
+  int flush_result = TEMP_FAILURE_RETRY(Flush());
+  if (flush_result != 0) {
+    LOG(ERROR) << "CloseOrErase failed while flushing a file.";
+    Erase();
+    return flush_result;
+  }
+  int close_result = TEMP_FAILURE_RETRY(Close());
+  if (close_result != 0) {
+    LOG(ERROR) << "CloseOrErase failed while closing a file.";
+    Erase();
+    return close_result;
+  }
+  return 0;
+}
+
+int FdFile::FlushClose() {
+  int flush_result = TEMP_FAILURE_RETRY(Flush());
+  if (flush_result != 0) {
+    LOG(ERROR) << "FlushClose failed while flushing a file.";
+  }
+  int close_result = TEMP_FAILURE_RETRY(Close());
+  if (close_result != 0) {
+    LOG(ERROR) << "FlushClose failed while closing a file.";
+  }
+  return (flush_result != 0) ? flush_result : close_result;
+}
+
+void FdFile::MarkUnchecked() {
+  guard_state_ = GuardState::kNoCheck;
+}
+
 }  // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 01f4ca2..ae1d3f7 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -24,6 +24,9 @@
 
 namespace unix_file {
 
+// If true, check whether Flush and Close are called before destruction.
+static constexpr bool kCheckSafeUsage = true;
+
 // A RandomAccessFile implementation backed by a file descriptor.
 //
 // Not thread safe.
@@ -32,8 +35,8 @@
   FdFile();
   // Creates an FdFile using the given file descriptor. Takes ownership of the
   // file descriptor. (Use DisableAutoClose to retain ownership.)
-  explicit FdFile(int fd);
-  explicit FdFile(int fd, const std::string& path);
+  explicit FdFile(int fd, bool checkUsage);
+  explicit FdFile(int fd, const std::string& path, bool checkUsage);
 
   // Destroys an FdFile, closing the file descriptor if Close hasn't already
   // been called. (If you care about the return value of Close, call it
@@ -47,12 +50,21 @@
   bool Open(const std::string& file_path, int flags, mode_t mode);
 
   // RandomAccessFile API.
-  virtual int Close();
-  virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
-  virtual int SetLength(int64_t new_length);
+  virtual int Close() WARN_UNUSED;
+  virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const WARN_UNUSED;
+  virtual int SetLength(int64_t new_length) WARN_UNUSED;
   virtual int64_t GetLength() const;
-  virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
-  virtual int Flush();
+  virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset) WARN_UNUSED;
+  virtual int Flush() WARN_UNUSED;
+
+  // Short for SetLength(0); Flush(); Close();
+  void Erase();
+
+  // Try to Flush(), then try to Close(); If either fails, call Erase().
+  int FlushCloseOrErase() WARN_UNUSED;
+
+  // Try to Flush and Close(). Attempts both, but returns the first error.
+  int FlushClose() WARN_UNUSED;
 
   // Bonus API.
   int Fd() const;
@@ -61,8 +73,38 @@
     return file_path_;
   }
   void DisableAutoClose();
-  bool ReadFully(void* buffer, size_t byte_count);
-  bool WriteFully(const void* buffer, size_t byte_count);
+  bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
+  bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
+
+  // This enum is public so that we can define the << operator over it.
+  enum class GuardState {
+    kBase,           // Base, file has not been flushed or closed.
+    kFlushed,        // File has been flushed, but not closed.
+    kClosed,         // File has been flushed and closed.
+    kNoCheck         // Do not check for the current file instance.
+  };
+
+  // WARNING: Only use this when you know what you're doing!
+  void MarkUnchecked();
+
+ protected:
+  // If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
+  // given warning if the current state is or exceeds warn_threshold.
+  void moveTo(GuardState target, GuardState warn_threshold, const char* warning);
+
+  // If the guard state indicates checking (<kNoCheck), and is below the target state "target", go
+  // to "target." If the current state is higher (excluding kNoCheck) than the trg state, print the
+  // warning.
+  void moveUp(GuardState target, const char* warning);
+
+  // Forcefully sets the state to the given one. This can overwrite kNoCheck.
+  void resetGuard(GuardState new_state) {
+    if (kCheckSafeUsage) {
+      guard_state_ = new_state;
+    }
+  }
+
+  GuardState guard_state_;
 
  private:
   int fd_;
@@ -72,6 +114,8 @@
   DISALLOW_COPY_AND_ASSIGN(FdFile);
 };
 
+std::ostream& operator<<(std::ostream& os, const FdFile::GuardState& kind);
+
 }  // namespace unix_file
 
 #endif  // ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index 3481f2f..a7e5b96 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -24,7 +24,7 @@
 class FdFileTest : public RandomAccessFileTest {
  protected:
   virtual RandomAccessFile* MakeTestFile() {
-    return new FdFile(fileno(tmpfile()));
+    return new FdFile(fileno(tmpfile()), false);
   }
 };
 
@@ -53,6 +53,7 @@
   ASSERT_TRUE(file.Open(good_path, O_CREAT | O_WRONLY));
   EXPECT_GE(file.Fd(), 0);
   EXPECT_TRUE(file.IsOpened());
+  EXPECT_EQ(0, file.Flush());
   EXPECT_EQ(0, file.Close());
   EXPECT_EQ(-1, file.Fd());
   EXPECT_FALSE(file.IsOpened());
@@ -60,7 +61,7 @@
   EXPECT_GE(file.Fd(), 0);
   EXPECT_TRUE(file.IsOpened());
 
-  file.Close();
+  ASSERT_EQ(file.Close(), 0);
   ASSERT_EQ(unlink(good_path.c_str()), 0);
 }
 
diff --git a/runtime/base/unix_file/mapped_file.cc b/runtime/base/unix_file/mapped_file.cc
index 63927b1..ad43382 100644
--- a/runtime/base/unix_file/mapped_file.cc
+++ b/runtime/base/unix_file/mapped_file.cc
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "base/logging.h"
 #include "base/unix_file/mapped_file.h"
+
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -24,6 +24,8 @@
 #include <algorithm>
 #include <string>
 
+#include "base/logging.h"
+
 namespace unix_file {
 
 MappedFile::~MappedFile() {
@@ -39,6 +41,10 @@
 bool MappedFile::MapReadOnly() {
   CHECK(IsOpened());
   CHECK(!IsMapped());
+
+  // Mapping readonly means we don't need to enforce Flush and Close.
+  resetGuard(GuardState::kNoCheck);
+
   struct stat st;
   int result = TEMP_FAILURE_RETRY(fstat(Fd(), &st));
   if (result == -1) {
@@ -71,6 +77,10 @@
                 << "' to size " << file_size;
     return false;
   }
+
+  // Need to track this now.
+  resetGuard(GuardState::kBase);
+
   file_size_ = file_size;
   do {
     mapped_file_ =
@@ -130,6 +140,7 @@
 }
 
 int MappedFile::Flush() {
+  moveUp(GuardState::kFlushed, "Flushing closed file.");
   int rc = IsMapped() ? TEMP_FAILURE_RETRY(msync(mapped_file_, file_size_, 0)) : FdFile::Flush();
   return rc == -1 ? -errno : 0;
 }
@@ -145,6 +156,7 @@
                                   std::min(byte_count, file_size_ - offset));
     if (write_size > 0) {
       memcpy(data() + offset, buf, write_size);
+      moveTo(GuardState::kBase, GuardState::kClosed, "Writing into a closed file.");
     }
     return write_size;
   } else {
diff --git a/runtime/base/unix_file/mapped_file.h b/runtime/base/unix_file/mapped_file.h
index 73056e9..4ff259b 100644
--- a/runtime/base/unix_file/mapped_file.h
+++ b/runtime/base/unix_file/mapped_file.h
@@ -41,11 +41,12 @@
 #endif
   };
 
-  MappedFile() : FdFile(), file_size_(-1), mapped_file_(NULL) {
+  MappedFile() : FdFile(), file_size_(-1), mapped_file_(NULL), map_mode_(kMapReadOnly) {
   }
   // Creates a MappedFile using the given file descriptor. Takes ownership of
   // the file descriptor.
-  explicit MappedFile(int fd) : FdFile(fd), file_size_(-1), mapped_file_(NULL) {
+  explicit MappedFile(int fd, bool check_usage) : FdFile(fd, check_usage), file_size_(-1),
+      mapped_file_(NULL), map_mode_(kMapReadOnly) {
   }
 
   // Unmaps and closes the file if needed.
diff --git a/runtime/base/unix_file/mapped_file_test.cc b/runtime/base/unix_file/mapped_file_test.cc
index 59334d4..0455df6 100644
--- a/runtime/base/unix_file/mapped_file_test.cc
+++ b/runtime/base/unix_file/mapped_file_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include "base/unix_file/mapped_file.h"
+#include "base/casts.h"
 #include "base/logging.h"
 #include "base/unix_file/fd_file.h"
 #include "base/unix_file/random_access_file_test.h"
@@ -34,12 +35,13 @@
 
     good_path_ = GetTmpPath("some-file.txt");
     int fd = TEMP_FAILURE_RETRY(open(good_path_.c_str(), O_CREAT|O_RDWR, 0666));
-    FdFile dst(fd);
+    FdFile dst(fd, false);
 
     StringFile src;
     src.Assign(kContent);
 
     ASSERT_TRUE(CopyFile(src, &dst));
+    ASSERT_EQ(dst.FlushClose(), 0);
   }
 
   void TearDown() {
@@ -55,6 +57,15 @@
     return f;
   }
 
+  void CleanUp(RandomAccessFile* file) OVERRIDE {
+    if (file == nullptr) {
+      return;
+    }
+    MappedFile* f = ::art::down_cast<MappedFile*>(file);
+    UNUSED(f->Flush());
+    UNUSED(f->Close());
+  }
+
   const std::string kContent;
   std::string good_path_;
 };
@@ -80,7 +91,7 @@
 TEST_F(MappedFileTest, OpenFdClose) {
   FILE* f = tmpfile();
   ASSERT_TRUE(f != NULL);
-  MappedFile file(fileno(f));
+  MappedFile file(fileno(f), false);
   EXPECT_GE(file.Fd(), 0);
   EXPECT_TRUE(file.IsOpened());
   EXPECT_EQ(0, file.Close());
@@ -108,6 +119,7 @@
   ASSERT_TRUE(file.data());
   EXPECT_EQ(kContent[0], *file.data());
   EXPECT_EQ(0, file.Flush());
+  file.Close();
 }
 
 TEST_F(MappedFileTest, CanWriteNewData) {
@@ -122,10 +134,11 @@
   EXPECT_EQ(kContent.size(), static_cast<uint64_t>(file.size()));
   ASSERT_TRUE(file.data());
   memcpy(file.data(), kContent.c_str(), kContent.size());
+  EXPECT_EQ(0, file.Flush());
   EXPECT_EQ(0, file.Close());
   EXPECT_FALSE(file.IsMapped());
 
-  FdFile new_file(TEMP_FAILURE_RETRY(open(new_path.c_str(), O_RDONLY)));
+  FdFile new_file(TEMP_FAILURE_RETRY(open(new_path.c_str(), O_RDONLY)), false);
   StringFile buffer;
   ASSERT_TRUE(CopyFile(new_file, &buffer));
   EXPECT_EQ(kContent, buffer.ToStringPiece());
@@ -192,6 +205,7 @@
   ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
   ASSERT_TRUE(file.MapReadWrite(kContent.size()));
   TestReadContent(kContent, &file);
+  UNUSED(file.FlushClose());
 }
 
 TEST_F(MappedFileTest, WriteMappedReadWrite) {
@@ -217,6 +231,7 @@
   EXPECT_EQ(kContent.size(),
             static_cast<uint64_t>(file.Write(kContent.c_str(), kContent.size(), 0)));
   EXPECT_EQ(0, memcmp(kContent.c_str(), file.data(), kContent.size()));
+  UNUSED(file.FlushClose());
 }
 
 #if 0  // death tests don't work on android yet
diff --git a/runtime/base/unix_file/random_access_file_test.h b/runtime/base/unix_file/random_access_file_test.h
index 0002433..7734dc4 100644
--- a/runtime/base/unix_file/random_access_file_test.h
+++ b/runtime/base/unix_file/random_access_file_test.h
@@ -76,6 +76,8 @@
     ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Write(content.data(), content.size(), 0)));
 
     TestReadContent(content, file.get());
+
+    CleanUp(file.get());
   }
 
   void TestReadContent(const std::string& content, RandomAccessFile* file) {
@@ -131,6 +133,8 @@
     ASSERT_EQ(new_length, file->GetLength());
     ASSERT_TRUE(ReadString(file.get(), &new_content));
     ASSERT_EQ('\0', new_content[new_length - 1]);
+
+    CleanUp(file.get());
   }
 
   void TestWrite() {
@@ -163,6 +167,11 @@
     ASSERT_EQ(file->GetLength(), new_length);
     ASSERT_TRUE(ReadString(file.get(), &new_content));
     ASSERT_EQ(std::string("hello\0hello", new_length), new_content);
+
+    CleanUp(file.get());
+  }
+
+  virtual void CleanUp(RandomAccessFile* file) {
   }
 
  protected:
diff --git a/runtime/base/unix_file/random_access_file_utils_test.cc b/runtime/base/unix_file/random_access_file_utils_test.cc
index 6317922..9457d22 100644
--- a/runtime/base/unix_file/random_access_file_utils_test.cc
+++ b/runtime/base/unix_file/random_access_file_utils_test.cc
@@ -37,14 +37,14 @@
 }
 
 TEST_F(RandomAccessFileUtilsTest, BadSrc) {
-  FdFile src(-1);
+  FdFile src(-1, false);
   StringFile dst;
   ASSERT_FALSE(CopyFile(src, &dst));
 }
 
 TEST_F(RandomAccessFileUtilsTest, BadDst) {
   StringFile src;
-  FdFile dst(-1);
+  FdFile dst(-1, false);
 
   // We need some source content to trigger a write.
   // Copying an empty file is a no-op.
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index d05f7af..c1af0fb 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -58,9 +58,9 @@
 
 inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx,
                                                   mirror::ArtMethod* referrer) {
-  mirror::String* resolved_string = referrer->GetDexCacheStrings()->Get(string_idx);
+  mirror::Class* declaring_class = referrer->GetDeclaringClass();
+  mirror::String* resolved_string = declaring_class->GetDexCacheStrings()->Get(string_idx);
   if (UNLIKELY(resolved_string == NULL)) {
-    mirror::Class* declaring_class = referrer->GetDeclaringClass();
     StackHandleScope<1> hs(Thread::Current());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
     const DexFile& dex_file = *dex_cache->GetDexFile();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 64f2484..5911735 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <sys/file.h>
 #include <sys/stat.h>
+#include <unistd.h>
 #include <deque>
 #include <memory>
 #include <string>
@@ -128,15 +129,6 @@
   }
 }
 
-static size_t Hash(const char* s) {
-  // This is the java.lang.String hashcode for convenience, not interoperability.
-  size_t hash = 0;
-  for (; *s != '\0'; ++s) {
-    hash = hash * 31 + *s;
-  }
-  return hash;
-}
-
 const char* ClassLinker::class_roots_descriptors_[] = {
   "Ljava/lang/Class;",
   "Ljava/lang/Object;",
@@ -192,7 +184,8 @@
       portable_imt_conflict_trampoline_(nullptr),
       quick_imt_conflict_trampoline_(nullptr),
       quick_generic_jni_trampoline_(nullptr),
-      quick_to_interpreter_bridge_trampoline_(nullptr) {
+      quick_to_interpreter_bridge_trampoline_(nullptr),
+      image_pointer_size_(sizeof(void*)) {
   CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
   memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
 }
@@ -327,10 +320,9 @@
   Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle(
     AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize())));
   CHECK(java_lang_reflect_ArtMethod.Get() != nullptr);
-  java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize());
+  java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
   SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get());
   java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
-
   mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get());
 
   // Set up array classes for string, field, method
@@ -356,8 +348,7 @@
   // DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
   // these roots.
   CHECK_NE(0U, boot_class_path.size());
-  for (size_t i = 0; i != boot_class_path.size(); ++i) {
-    const DexFile* dex_file = boot_class_path[i];
+  for (const DexFile* dex_file : boot_class_path) {
     CHECK(dex_file != nullptr);
     AppendToBootClassPath(*dex_file);
   }
@@ -372,6 +363,7 @@
   Runtime* runtime = Runtime::Current();
   runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
   runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
+  runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod());
   runtime->SetDefaultImt(runtime->CreateDefaultImt(this));
 
   // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
@@ -525,6 +517,23 @@
                FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
   mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
 
+  // Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly
+  // initialized.
+  {
+    const DexFile& dex_file = java_lang_Object->GetDexFile();
+    const DexFile::StringId* void_string_id = dex_file.FindStringId("V");
+    CHECK(void_string_id != nullptr);
+    uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id);
+    const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index);
+    CHECK(void_type_id != nullptr);
+    uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
+    // Now we resolve void type so the dex cache contains it. We use java.lang.Object class
+    // as referrer so the used dex cache is core's one.
+    mirror::Class* resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get());
+    CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid));
+    self->AssertNoPendingException();
+  }
+
   FinishInit(self);
 
   VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
@@ -654,7 +663,14 @@
     argv.push_back(compiler_options[i].c_str());
   }
 
-  return Exec(argv, error_msg);
+  if (!Exec(argv, error_msg)) {
+    // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly
+    // died. Ignore unlink failure, propagate the original error.
+    TEMP_FAILURE_RETRY(unlink(oat_cache_filename));
+    return false;
+  }
+
+  return true;
 }
 
 const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
@@ -1639,6 +1655,20 @@
   // Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
   // bitmap walk.
   mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod));
+  size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize();
+  if (!Runtime::Current()->IsCompiler()) {
+    // Compiler supports having an image with a different pointer size than the runtime. This
+    // happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may
+    // also use 32 bit dex2oat on a system with 64 bit apps.
+    CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*)))
+        << sizeof(void*);
+  }
+  if (art_method_object_size == mirror::ArtMethod::InstanceSize(4)) {
+    image_pointer_size_ = 4;
+  } else {
+    CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(8));
+    image_pointer_size_ = 8;
+  }
 
   // Set entry point to interpreter if in InterpretOnly mode.
   if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
@@ -1652,7 +1682,7 @@
 
   // reinit array_iftable_ from any array class instance, they should be ==
   array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable());
-  DCHECK(array_iftable_.Read() == GetClassRoot(kBooleanArrayClass)->GetIfTable());
+  DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable());
   // String class root was set above
   mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
   mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField));
@@ -1675,25 +1705,24 @@
 void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
   if ((flags & kVisitRootFlagAllRoots) != 0) {
-    for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
-      it.second.VisitRoot(callback, arg, 0, kRootStickyClass);
+    for (GcRoot<mirror::Class>& root : class_table_) {
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
+    }
+    for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
     }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
-    for (auto& pair : new_class_roots_) {
-      mirror::Class* old_ref = pair.second.Read<kWithoutReadBarrier>();
-      pair.second.VisitRoot(callback, arg, 0, kRootStickyClass);
-      mirror::Class* new_ref = pair.second.Read<kWithoutReadBarrier>();
+    for (auto& root : new_class_roots_) {
+      mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
+      mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
       if (UNLIKELY(new_ref != old_ref)) {
         // Uh ohes, GC moved a root in the log. Need to search the class_table and update the
         // corresponding object. This is slow, but luckily for us, this may only happen with a
         // concurrent moving GC.
-        for (auto it = class_table_.lower_bound(pair.first), end = class_table_.end();
-            it != end && it->first == pair.first; ++it) {
-          // If the class stored matches the old class, update it to the new value.
-          if (old_ref == it->second.Read<kWithoutReadBarrier>()) {
-            it->second = GcRoot<mirror::Class>(new_ref);
-          }
-        }
+        auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref));
+        DCHECK(it != class_table_.end());
+        *it = GcRoot<mirror::Class>(new_ref);
       }
     }
   }
@@ -1713,17 +1742,17 @@
 // reinit references to when reinitializing a ClassLinker from a
 // mapped image.
 void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
-  class_roots_.VisitRoot(callback, arg, 0, kRootVMInternal);
+  class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   Thread* self = Thread::Current();
   {
     ReaderMutexLock mu(self, dex_lock_);
     if ((flags & kVisitRootFlagAllRoots) != 0) {
       for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) {
-        dex_cache.VisitRoot(callback, arg, 0, kRootVMInternal);
+        dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
       }
     } else if ((flags & kVisitRootFlagNewRoots) != 0) {
       for (size_t index : new_dex_cache_roots_) {
-        dex_caches_[index].VisitRoot(callback, arg, 0, kRootVMInternal);
+        dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal));
       }
     }
     if ((flags & kVisitRootFlagClearRootLog) != 0) {
@@ -1736,12 +1765,10 @@
     }
   }
   VisitClassRoots(callback, arg, flags);
-  array_iftable_.VisitRoot(callback, arg, 0, kRootVMInternal);
+  array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   DCHECK(!array_iftable_.IsNull());
   for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
-    if (!find_array_class_cache_[i].IsNull()) {
-      find_array_class_cache_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
-    }
+    find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   }
 }
 
@@ -1751,9 +1778,13 @@
   }
   // TODO: why isn't this a ReaderMutexLock?
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
-    mirror::Class* c = it.second.Read();
-    if (!visitor(c, arg)) {
+  for (GcRoot<mirror::Class>& root : class_table_) {
+    if (!visitor(root.Read(), arg)) {
+      return;
+    }
+  }
+  for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
+    if (!visitor(root.Read(), arg)) {
       return;
     }
   }
@@ -1809,7 +1840,7 @@
       size_t class_table_size;
       {
         ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-        class_table_size = class_table_.size();
+        class_table_size = class_table_.Size() + pre_zygote_class_table_.Size();
       }
       mirror::Class* class_type = mirror::Class::GetJavaLangClass();
       mirror::Class* array_of_class = FindArrayClass(self, &class_type);
@@ -1952,7 +1983,8 @@
     }
     CHECK(h_class->IsRetired());
     // Get the updated class from class table.
-    klass = LookupClass(descriptor, h_class.Get()->GetClassLoader());
+    klass = LookupClass(descriptor, ComputeModifiedUtf8Hash(descriptor),
+                        h_class.Get()->GetClassLoader());
   }
 
   // Wait for the class if it has not already been linked.
@@ -1986,21 +2018,19 @@
 
 // Search a collection of DexFiles for a descriptor
 ClassPathEntry FindInClassPath(const char* descriptor,
-                               const std::vector<const DexFile*>& class_path) {
-  for (size_t i = 0; i != class_path.size(); ++i) {
-    const DexFile* dex_file = class_path[i];
-    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+                               size_t hash, const std::vector<const DexFile*>& class_path) {
+  for (const DexFile* dex_file : class_path) {
+    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);
     if (dex_class_def != nullptr) {
       return ClassPathEntry(dex_file, dex_class_def);
     }
   }
-  // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
-  return ClassPathEntry(static_cast<const DexFile*>(nullptr),
-                        static_cast<const DexFile::ClassDef*>(nullptr));
+  return ClassPathEntry(nullptr, nullptr);
 }
 
 mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                                        Thread* self, const char* descriptor,
+                                                       size_t hash,
                                                        Handle<mirror::ClassLoader> class_loader) {
   if (class_loader->GetClass() !=
       soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) ||
@@ -2008,14 +2038,14 @@
           soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
     return nullptr;
   }
-  ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+  ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
   // Check if this would be found in the parent boot class loader.
   if (pair.second != nullptr) {
-    mirror::Class* klass = LookupClass(descriptor, nullptr);
+    mirror::Class* klass = LookupClass(descriptor, hash, nullptr);
     if (klass != nullptr) {
       return EnsureResolved(self, descriptor, klass);
     }
-    klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+    klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
                         *pair.second);
     if (klass != nullptr) {
       return klass;
@@ -2063,11 +2093,11 @@
               break;
             }
             for (const DexFile* dex_file : *dex_files) {
-              const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+              const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);
               if (dex_class_def != nullptr) {
                 RegisterDexFile(*dex_file);
-                mirror::Class* klass =
-                    DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+                mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, *dex_file,
+                                                   *dex_class_def);
                 if (klass == nullptr) {
                   CHECK(self->IsExceptionPending()) << descriptor;
                   self->ClearException();
@@ -2094,19 +2124,21 @@
     // for primitive classes that aren't backed by dex files.
     return FindPrimitiveClass(descriptor[0]);
   }
+  const size_t hash = ComputeModifiedUtf8Hash(descriptor);
   // Find the class in the loaded classes table.
-  mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
+  mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
   if (klass != nullptr) {
     return EnsureResolved(self, descriptor, klass);
   }
   // Class is not yet loaded.
   if (descriptor[0] == '[') {
-    return CreateArrayClass(self, descriptor, class_loader);
+    return CreateArrayClass(self, descriptor, hash, class_loader);
   } else if (class_loader.Get() == nullptr) {
     // The boot class loader, search the boot class path.
-    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
     if (pair.second != nullptr) {
-      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+      return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
+                         *pair.second);
     } else {
       // The boot class loader is searched ahead of the application class loader, failures are
       // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
@@ -2118,16 +2150,17 @@
   } else if (Runtime::Current()->UseCompileTimeClassPath()) {
     // First try with the bootstrap class loader.
     if (class_loader.Get() != nullptr) {
-      klass = LookupClass(descriptor, nullptr);
+      klass = LookupClass(descriptor, hash, nullptr);
       if (klass != nullptr) {
         return EnsureResolved(self, descriptor, klass);
       }
     }
     // If the lookup failed search the boot class path. We don't perform a recursive call to avoid
     // a NoClassDefFoundError being allocated.
-    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
     if (pair.second != nullptr) {
-      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+      return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
+                         *pair.second);
     }
     // Next try the compile time class path.
     const std::vector<const DexFile*>* class_path;
@@ -2137,13 +2170,13 @@
                                             soa.AddLocalReference<jobject>(class_loader.Get()));
       class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
     }
-    pair = FindInClassPath(descriptor, *class_path);
+    pair = FindInClassPath(descriptor, hash, *class_path);
     if (pair.second != nullptr) {
-      return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
+      return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second);
     }
   } else {
     ScopedObjectAccessUnchecked soa(self);
-    mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, class_loader);
+    mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader);
     if (klass != nullptr) {
       return klass;
     }
@@ -2182,14 +2215,12 @@
   return nullptr;
 }
 
-mirror::Class* ClassLinker::DefineClass(const char* descriptor,
+mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
                                         Handle<mirror::ClassLoader> class_loader,
                                         const DexFile& dex_file,
                                         const DexFile::ClassDef& dex_class_def) {
-  Thread* self = Thread::Current();
   StackHandleScope<3> hs(self);
   auto klass = hs.NewHandle<mirror::Class>(nullptr);
-  bool should_allocate = false;
 
   // Load the class from the dex file.
   if (UNLIKELY(!init_done_)) {
@@ -2208,14 +2239,10 @@
       klass.Assign(GetClassRoot(kJavaLangReflectArtField));
     } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
       klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
-    } else {
-      should_allocate = true;
     }
-  } else {
-    should_allocate = true;
   }
 
-  if (should_allocate) {
+  if (klass.Get() == nullptr) {
     // Allocate a class with the status of not ready.
     // Interface object should get the right size here. Regular class will
     // figure out the right size later and be replaced with one of the right
@@ -2240,7 +2267,7 @@
   klass->SetClinitThreadId(self->GetTid());
 
   // Add the newly loaded class to the loaded classes table.
-  mirror::Class* existing = InsertClass(descriptor, klass.Get(), Hash(descriptor));
+  mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
   if (existing != nullptr) {
     // We failed to insert because we raced with another thread. Calling EnsureResolved may cause
     // this thread to block.
@@ -2463,6 +2490,15 @@
 }
 #endif
 
+const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) {
+  if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
+    return nullptr;
+  }
+  OatFile::OatMethod oat_method;
+  bool found = FindOatMethodFor(method, &oat_method);
+  return found ? oat_method.GetQuickCode() : nullptr;
+}
+
 const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
                                             uint32_t method_idx) {
   OatFile::OatClass oat_class;
@@ -2711,6 +2747,7 @@
 
   klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
   klass->SetDexTypeIndex(dex_class_def.class_idx_);
+  CHECK(klass->GetDexCacheStrings() != nullptr);
 
   const byte* class_data = dex_file.GetClassData(dex_class_def);
   if (class_data == nullptr) {
@@ -2859,7 +2896,6 @@
   dst->SetDeclaringClass(klass.Get());
   dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
 
-  dst->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
   dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
   dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
 
@@ -3035,7 +3071,8 @@
   primitive_class->SetPrimitiveType(type);
   primitive_class->SetStatus(mirror::Class::kStatusInitialized, self);
   const char* descriptor = Primitive::Descriptor(type);
-  mirror::Class* existing = InsertClass(descriptor, primitive_class, Hash(descriptor));
+  mirror::Class* existing = InsertClass(descriptor, primitive_class,
+                                        ComputeModifiedUtf8Hash(descriptor));
   CHECK(existing == nullptr) << "InitPrimitiveClass(" << type << ") failed";
   return primitive_class;
 }
@@ -3053,7 +3090,7 @@
 // array class; that always comes from the base element class.
 //
 // Returns nullptr with an exception raised on failure.
-mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor,
+mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor, size_t hash,
                                              Handle<mirror::ClassLoader> class_loader) {
   // Identify the underlying component type
   CHECK_EQ('[', descriptor[0]);
@@ -3062,7 +3099,8 @@
   if (component_type.Get() == nullptr) {
     DCHECK(self->IsExceptionPending());
     // We need to accept erroneous classes as component types.
-    component_type.Assign(LookupClass(descriptor + 1, class_loader.Get()));
+    const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1);
+    component_type.Assign(LookupClass(descriptor + 1, component_hash, class_loader.Get()));
     if (component_type.Get() == nullptr) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
@@ -3092,7 +3130,7 @@
   // class to the hash table --- necessary because of possible races with
   // other threads.)
   if (class_loader.Get() != component_type->GetClassLoader()) {
-    mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader());
+    mirror::Class* new_class = LookupClass(descriptor, hash, component_type->GetClassLoader());
     if (new_class != nullptr) {
       return new_class;
     }
@@ -3142,7 +3180,11 @@
   new_class->SetPrimitiveType(Primitive::kPrimNot);
   new_class->SetClassLoader(component_type->GetClassLoader());
   new_class->SetStatus(mirror::Class::kStatusLoaded, self);
-  new_class->PopulateEmbeddedImtAndVTable();
+  {
+    StackHandleScope<mirror::Class::kImtSize> hs(self,
+                                                 Runtime::Current()->GetImtUnimplementedMethod());
+    new_class->PopulateEmbeddedImtAndVTable(&hs);
+  }
   new_class->SetStatus(mirror::Class::kStatusInitialized, self);
   // don't need to set new_class->SetObjectSize(..)
   // because Object::SizeOf delegates to Array::SizeOf
@@ -3177,7 +3219,7 @@
 
   new_class->SetAccessFlags(access_flags);
 
-  mirror::Class* existing = InsertClass(descriptor, new_class.Get(), Hash(descriptor));
+  mirror::Class* existing = InsertClass(descriptor, new_class.Get(), hash);
   if (existing == nullptr) {
     return new_class.Get();
   }
@@ -3230,8 +3272,7 @@
     LOG(INFO) << "Loaded class " << descriptor << source;
   }
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  mirror::Class* existing =
-      LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
+  mirror::Class* existing = LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
   if (existing != nullptr) {
     return existing;
   }
@@ -3241,13 +3282,13 @@
     // is in the image.
     existing = LookupClassFromImage(descriptor);
     if (existing != nullptr) {
-      CHECK(klass == existing);
+      CHECK_EQ(klass, existing);
     }
   }
   VerifyObject(klass);
-  class_table_.insert(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+  class_table_.InsertWithHash(GcRoot<mirror::Class>(klass), hash);
   if (log_new_class_table_roots_) {
-    new_class_roots_.push_back(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+    new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
   }
   return nullptr;
 }
@@ -3255,27 +3296,18 @@
 mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass,
                                         size_t hash) {
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  mirror::Class* existing =
-      LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
-
-  if (existing == nullptr) {
+  auto existing_it = class_table_.FindWithHash(std::make_pair(descriptor, klass->GetClassLoader()),
+                                               hash);
+  if (existing_it == class_table_.end()) {
     CHECK(klass->IsProxyClass());
     return nullptr;
   }
 
+  mirror::Class* existing = existing_it->Read();
   CHECK_NE(existing, klass) << descriptor;
   CHECK(!existing->IsResolved()) << descriptor;
   CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor;
 
-  for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
-       it != end && it->first == hash; ++it) {
-    mirror::Class* klass = it->second.Read();
-    if (klass == existing) {
-      class_table_.erase(it);
-      break;
-    }
-  }
-
   CHECK(!klass->IsTemp()) << descriptor;
   if (kIsDebugBuild && klass->GetClassLoader() == nullptr &&
       dex_cache_image_class_lookup_required_) {
@@ -3283,37 +3315,38 @@
     // is in the image.
     existing = LookupClassFromImage(descriptor);
     if (existing != nullptr) {
-      CHECK(klass == existing) << descriptor;
+      CHECK_EQ(klass, existing) << descriptor;
     }
   }
   VerifyObject(klass);
 
-  class_table_.insert(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+  // Update the element in the hash set.
+  *existing_it = GcRoot<mirror::Class>(klass);
   if (log_new_class_table_roots_) {
-    new_class_roots_.push_back(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+    new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
   }
 
   return existing;
 }
 
-bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) {
-  size_t hash = Hash(descriptor);
+bool ClassLinker::RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader) {
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
-       it != end && it->first == hash;
-       ++it) {
-    mirror::Class* klass = it->second.Read();
-    if (klass->GetClassLoader() == class_loader && klass->DescriptorEquals(descriptor)) {
-      class_table_.erase(it);
-      return true;
-    }
+  auto pair = std::make_pair(descriptor, class_loader);
+  auto it = class_table_.Find(pair);
+  if (it != class_table_.end()) {
+    class_table_.Erase(it);
+    return true;
+  }
+  it = pre_zygote_class_table_.Find(pair);
+  if (it != pre_zygote_class_table_.end()) {
+    pre_zygote_class_table_.Erase(it);
+    return true;
   }
   return false;
 }
 
-mirror::Class* ClassLinker::LookupClass(const char* descriptor,
-                                        const mirror::ClassLoader* class_loader) {
-  size_t hash = Hash(descriptor);
+mirror::Class* ClassLinker::LookupClass(const char* descriptor, size_t hash,
+                                        mirror::ClassLoader* class_loader) {
   {
     ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
     mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
@@ -3342,26 +3375,17 @@
 }
 
 mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor,
-                                                       const mirror::ClassLoader* class_loader,
+                                                       mirror::ClassLoader* class_loader,
                                                        size_t hash) {
-  auto end = class_table_.end();
-  for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) {
-    mirror::Class* klass = it->second.Read();
-    if (klass->GetClassLoader() == class_loader && klass->DescriptorEquals(descriptor)) {
-      if (kIsDebugBuild) {
-        // Check for duplicates in the table.
-        for (++it; it != end && it->first == hash; ++it) {
-          mirror::Class* klass2 = it->second.Read();
-          CHECK(!(klass2->GetClassLoader() == class_loader &&
-              klass2->DescriptorEquals(descriptor)))
-              << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
-              << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
-        }
-      }
-      return klass;
+  auto descriptor_pair = std::make_pair(descriptor, class_loader);
+  auto it = pre_zygote_class_table_.FindWithHash(descriptor_pair, hash);
+  if (it == pre_zygote_class_table_.end()) {
+    it = class_table_.FindWithHash(descriptor_pair, hash);
+    if (it == class_table_.end()) {
+      return nullptr;
     }
   }
-  return nullptr;
+  return it->Read();
 }
 
 static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches()
@@ -3390,15 +3414,15 @@
       if (klass != nullptr) {
         DCHECK(klass->GetClassLoader() == nullptr);
         const char* descriptor = klass->GetDescriptor(&temp);
-        size_t hash = Hash(descriptor);
+        size_t hash = ComputeModifiedUtf8Hash(descriptor);
         mirror::Class* existing = LookupClassFromTableLocked(descriptor, nullptr, hash);
         if (existing != nullptr) {
-          CHECK(existing == klass) << PrettyClassAndClassLoader(existing) << " != "
+          CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != "
               << PrettyClassAndClassLoader(klass);
         } else {
-          class_table_.insert(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+          class_table_.Insert(GcRoot<mirror::Class>(klass));
           if (log_new_class_table_roots_) {
-            new_class_roots_.push_back(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+            new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
           }
         }
       }
@@ -3408,6 +3432,13 @@
   self->EndAssertNoThreadSuspension(old_no_suspend_cause);
 }
 
+void ClassLinker::MoveClassTableToPreZygote() {
+  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  DCHECK(pre_zygote_class_table_.Empty());
+  pre_zygote_class_table_ = std::move(class_table_);
+  class_table_.Clear();
+}
+
 mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
   Thread* self = Thread::Current();
   const char* old_no_suspend_cause =
@@ -3440,14 +3471,32 @@
   if (dex_cache_image_class_lookup_required_) {
     MoveImageClassesToClassTable();
   }
-  size_t hash = Hash(descriptor);
-  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
-      it != end && it->first == hash; ++it) {
-    mirror::Class* klass = it->second.Read();
-    if (klass->DescriptorEquals(descriptor)) {
-      result.push_back(klass);
+  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  while (true) {
+    auto it = class_table_.Find(descriptor);
+    if (it == class_table_.end()) {
+      break;
     }
+    result.push_back(it->Read());
+    class_table_.Erase(it);
+  }
+  for (mirror::Class* k : result) {
+    class_table_.Insert(GcRoot<mirror::Class>(k));
+  }
+  size_t pre_zygote_start = result.size();
+  // Now handle the pre zygote table.
+  // Note: This dirties the pre-zygote table but shouldn't be an issue since LookupClasses is only
+  // called from the debugger.
+  while (true) {
+    auto it = pre_zygote_class_table_.Find(descriptor);
+    if (it == pre_zygote_class_table_.end()) {
+      break;
+    }
+    result.push_back(it->Read());
+    pre_zygote_class_table_.Erase(it);
+  }
+  for (size_t i = pre_zygote_start; i < result.size(); ++i) {
+    pre_zygote_class_table_.Insert(GcRoot<mirror::Class>(result[i]));
   }
 }
 
@@ -3626,6 +3675,14 @@
     return false;
   }
 
+  // We may be running with a preopted oat file but without image. In this case,
+  // we don't skip verification of preverified classes to ensure we initialize
+  // dex caches with all types resolved during verification.
+  if (!Runtime::Current()->IsCompiler() &&
+      !Runtime::Current()->GetHeap()->HasImageSpace()) {
+    return false;
+  }
+
   uint16_t class_def_index = klass->GetDexClassDefIndex();
   oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
   if (oat_file_class_status == mirror::Class::kStatusVerified ||
@@ -3872,7 +3929,8 @@
     CHECK_EQ(klass.Get()->GetThrows(),
              soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
   }
-  mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), Hash(descriptor.c_str()));
+  mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(),
+                                        ComputeModifiedUtf8Hash(descriptor.c_str()));
   CHECK(existing == nullptr);
   return klass.Get();
 }
@@ -3982,7 +4040,6 @@
 
   // The proxy method doesn't have its own dex cache or dex file and so it steals those of its
   // interface prototype. The exception to this are Constructors and the Class of the Proxy itself.
-  CHECK_EQ(prototype->GetDexCacheStrings(), method->GetDexCacheStrings());
   CHECK(prototype->HasSameDexCacheResolvedMethods(method.Get()));
   CHECK(prototype->HasSameDexCacheResolvedTypes(method.Get()));
   CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
@@ -4144,13 +4201,28 @@
     }
   }
 
-  if (klass->NumStaticFields() > 0) {
+  const size_t num_static_fields = klass->NumStaticFields();
+  if (num_static_fields > 0) {
     const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
     CHECK(dex_class_def != nullptr);
     const DexFile& dex_file = klass->GetDexFile();
     StackHandleScope<2> hs(self);
     Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
+
+    // Eagerly fill in static fields so that the we don't have to do as many expensive
+    // Class::FindStaticField in ResolveField.
+    for (size_t i = 0; i < num_static_fields; ++i) {
+      mirror::ArtField* field = klass->GetStaticField(i);
+      const uint32_t field_idx = field->GetDexFieldIndex();
+      mirror::ArtField* resolved_field = dex_cache->GetResolvedField(field_idx);
+      if (resolved_field == nullptr) {
+        dex_cache->SetResolvedField(field_idx, field);
+      } else {
+        DCHECK_EQ(field, resolved_field);
+      }
+    }
+
     EncodedStaticFieldValueIterator it(dex_file, &dex_cache, &class_loader,
                                        this, *dex_class_def);
     if (it.HasNext()) {
@@ -4371,7 +4443,9 @@
   if (!LinkSuperClass(klass)) {
     return false;
   }
-  if (!LinkMethods(self, klass, interfaces)) {
+  StackHandleScope<mirror::Class::kImtSize> imt_handle_scope(
+      self, Runtime::Current()->GetImtUnimplementedMethod());
+  if (!LinkMethods(self, klass, interfaces, &imt_handle_scope)) {
     return false;
   }
   if (!LinkInstanceFields(klass)) {
@@ -4391,7 +4465,7 @@
     CHECK_EQ(klass->GetClassSize(), class_size) << PrettyDescriptor(klass.Get());
 
     if (klass->ShouldHaveEmbeddedImtAndVTable()) {
-      klass->PopulateEmbeddedImtAndVTable();
+      klass->PopulateEmbeddedImtAndVTable(&imt_handle_scope);
     }
 
     // This will notify waiters on klass that saw the not yet resolved
@@ -4401,7 +4475,7 @@
   } else {
     CHECK(!klass->IsResolved());
     // Retire the temporary class and create the correctly sized resolved class.
-    *new_class = klass->CopyOf(self, class_size);
+    *new_class = klass->CopyOf(self, class_size, &imt_handle_scope);
     if (UNLIKELY(*new_class == nullptr)) {
       CHECK(self->IsExceptionPending());  // Expect an OOME.
       klass->SetStatus(mirror::Class::kStatusError, self);
@@ -4415,7 +4489,8 @@
 
     FixupTemporaryDeclaringClass(klass.Get(), new_class_h.Get());
 
-    mirror::Class* existing = UpdateClass(descriptor, new_class_h.Get(), Hash(descriptor));
+    mirror::Class* existing = UpdateClass(descriptor, new_class_h.Get(),
+                                          ComputeModifiedUtf8Hash(descriptor));
     CHECK(existing == nullptr || existing == klass.Get());
 
     // This will notify waiters on temp class that saw the not yet resolved class in the
@@ -4430,6 +4505,171 @@
   return true;
 }
 
+static void CountMethodsAndFields(ClassDataItemIterator& dex_data,
+                                  size_t* virtual_methods,
+                                  size_t* direct_methods,
+                                  size_t* static_fields,
+                                  size_t* instance_fields) {
+  *virtual_methods = *direct_methods = *static_fields = *instance_fields = 0;
+
+  while (dex_data.HasNextStaticField()) {
+    dex_data.Next();
+    (*static_fields)++;
+  }
+  while (dex_data.HasNextInstanceField()) {
+    dex_data.Next();
+    (*instance_fields)++;
+  }
+  while (dex_data.HasNextDirectMethod()) {
+    (*direct_methods)++;
+    dex_data.Next();
+  }
+  while (dex_data.HasNextVirtualMethod()) {
+    (*virtual_methods)++;
+    dex_data.Next();
+  }
+  DCHECK(!dex_data.HasNext());
+}
+
+static void DumpClass(std::ostream& os,
+                      const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+                      const char* suffix) {
+  ClassDataItemIterator dex_data(dex_file, dex_file.GetClassData(dex_class_def));
+  os << dex_file.GetClassDescriptor(dex_class_def) << suffix << ":\n";
+  os << " Static fields:\n";
+  while (dex_data.HasNextStaticField()) {
+    const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+    dex_data.Next();
+  }
+  os << " Instance fields:\n";
+  while (dex_data.HasNextInstanceField()) {
+    const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+    dex_data.Next();
+  }
+  os << " Direct methods:\n";
+  while (dex_data.HasNextDirectMethod()) {
+    const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+    dex_data.Next();
+  }
+  os << " Virtual methods:\n";
+  while (dex_data.HasNextVirtualMethod()) {
+    const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+    dex_data.Next();
+  }
+}
+
+static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+                               const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2) {
+  std::ostringstream os;
+  DumpClass(os, dex_file1, dex_class_def1, " (Compile time)");
+  DumpClass(os, dex_file2, dex_class_def2, " (Runtime)");
+  return os.str();
+}
+
+
+// Very simple structural check on whether the classes match. Only compares the number of
+// methods and fields.
+static bool SimpleStructuralCheck(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+                                  const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2,
+                                  std::string* error_msg) {
+  ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1));
+  ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2));
+
+  // Counters for current dex file.
+  size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1;
+  CountMethodsAndFields(dex_data1, &dex_virtual_methods1, &dex_direct_methods1, &dex_static_fields1,
+                        &dex_instance_fields1);
+  // Counters for compile-time dex file.
+  size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2;
+  CountMethodsAndFields(dex_data2, &dex_virtual_methods2, &dex_direct_methods2, &dex_static_fields2,
+                        &dex_instance_fields2);
+
+  if (dex_virtual_methods1 != dex_virtual_methods2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s", dex_virtual_methods1,
+                              dex_virtual_methods2, class_dump.c_str());
+    return false;
+  }
+  if (dex_direct_methods1 != dex_direct_methods2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s", dex_direct_methods1,
+                              dex_direct_methods2, class_dump.c_str());
+    return false;
+  }
+  if (dex_static_fields1 != dex_static_fields2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s", dex_static_fields1,
+                              dex_static_fields2, class_dump.c_str());
+    return false;
+  }
+  if (dex_instance_fields1 != dex_instance_fields2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s", dex_instance_fields1,
+                              dex_instance_fields2, class_dump.c_str());
+    return false;
+  }
+
+  return true;
+}
+
+// Checks whether a the super-class changed from what we had at compile-time. This would
+// invalidate quickening.
+static bool CheckSuperClassChange(Handle<mirror::Class> klass,
+                                  const DexFile& dex_file,
+                                  const DexFile::ClassDef& class_def,
+                                  mirror::Class* super_class)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Check for unexpected changes in the superclass.
+  // Quick check 1) is the super_class class-loader the boot class loader? This always has
+  // precedence.
+  if (super_class->GetClassLoader() != nullptr &&
+      // Quick check 2) different dex cache? Breaks can only occur for different dex files,
+      // which is implied by different dex cache.
+      klass->GetDexCache() != super_class->GetDexCache()) {
+    // Now comes the expensive part: things can be broken if (a) the klass' dex file has a
+    // definition for the super-class, and (b) the files are in separate oat files. The oat files
+    // are referenced from the dex file, so do (b) first. Only relevant if we have oat files.
+    const OatFile* class_oat_file = dex_file.GetOatFile();
+    if (class_oat_file != nullptr) {
+      const OatFile* loaded_super_oat_file = super_class->GetDexFile().GetOatFile();
+      if (loaded_super_oat_file != nullptr && class_oat_file != loaded_super_oat_file) {
+        // Now check (a).
+        const DexFile::ClassDef* super_class_def = dex_file.FindClassDef(class_def.superclass_idx_);
+        if (super_class_def != nullptr) {
+          // Uh-oh, we found something. Do our check.
+          std::string error_msg;
+          if (!SimpleStructuralCheck(dex_file, *super_class_def,
+                                     super_class->GetDexFile(), *super_class->GetClassDef(),
+                                     &error_msg)) {
+            // Print a warning to the log. This exception might be caught, e.g., as common in test
+            // drivers. When the class is later tried to be used, we re-throw a new instance, as we
+            // only save the type of the exception.
+            LOG(WARNING) << "Incompatible structural change detected: " <<
+                StringPrintf(
+                    "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+                    PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+                    class_oat_file->GetLocation().c_str(),
+                    loaded_super_oat_file->GetLocation().c_str(),
+                    error_msg.c_str());
+            ThrowIncompatibleClassChangeError(klass.Get(),
+                "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+                PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+                class_oat_file->GetLocation().c_str(),
+                loaded_super_oat_file->GetLocation().c_str(),
+                error_msg.c_str());
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
 bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) {
   CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus());
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
@@ -4449,6 +4689,11 @@
     }
     CHECK(super_class->IsResolved());
     klass->SetSuperClass(super_class);
+
+    if (!CheckSuperClassChange(klass, dex_file, class_def, super_class)) {
+      DCHECK(Thread::Current()->IsExceptionPending());
+      return false;
+    }
   }
   const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);
   if (interfaces != nullptr) {
@@ -4535,7 +4780,8 @@
 
 // Populate the class vtable and itable. Compute return type indices.
 bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
-                              Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
+                              Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+                              StackHandleScope<mirror::Class::kImtSize>* out_imt) {
   if (klass->IsInterface()) {
     // No vtable.
     size_t count = klass->NumVirtualMethods();
@@ -4546,22 +4792,127 @@
     for (size_t i = 0; i < count; ++i) {
       klass->GetVirtualMethodDuringLinking(i)->SetMethodIndex(i);
     }
-    // Link interface method tables
-    return LinkInterfaceMethods(klass, interfaces);
-  } else {
-    // Link virtual and interface method tables
-    return LinkVirtualMethods(self, klass) && LinkInterfaceMethods(klass, interfaces);
+  } else if (!LinkVirtualMethods(self, klass)) {  // Link virtual methods first.
+    return false;
   }
-  return true;
+  return LinkInterfaceMethods(self, klass, interfaces, out_imt);  // Link interface method last.
 }
 
+// Comparator for name and signature of a method, used in finding overriding methods. Implementation
+// avoids the use of handles, if it didn't then rather than compare dex files we could compare dex
+// caches in the implementation below.
+class MethodNameAndSignatureComparator FINAL {
+ public:
+  explicit MethodNameAndSignatureComparator(mirror::ArtMethod* method)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+      dex_file_(method->GetDexFile()), mid_(&dex_file_->GetMethodId(method->GetDexMethodIndex())),
+      name_(nullptr), name_len_(0) {
+    DCHECK(!method->IsProxyMethod()) << PrettyMethod(method);
+  }
+
+  const char* GetName() {
+    if (name_ == nullptr) {
+      name_ = dex_file_->StringDataAndUtf16LengthByIdx(mid_->name_idx_, &name_len_);
+    }
+    return name_;
+  }
+
+  bool HasSameNameAndSignature(mirror::ArtMethod* other)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK(!other->IsProxyMethod()) << PrettyMethod(other);
+    const DexFile* other_dex_file = other->GetDexFile();
+    const DexFile::MethodId& other_mid = other_dex_file->GetMethodId(other->GetDexMethodIndex());
+    if (dex_file_ == other_dex_file) {
+      return mid_->name_idx_ == other_mid.name_idx_ && mid_->proto_idx_ == other_mid.proto_idx_;
+    }
+    GetName();  // Only used to make sure its calculated.
+    uint32_t other_name_len;
+    const char* other_name = other_dex_file->StringDataAndUtf16LengthByIdx(other_mid.name_idx_,
+                                                                           &other_name_len);
+    if (name_len_ != other_name_len || strcmp(name_, other_name) != 0) {
+      return false;
+    }
+    return dex_file_->GetMethodSignature(*mid_) == other_dex_file->GetMethodSignature(other_mid);
+  }
+
+ private:
+  // Dex file for the method to compare against.
+  const DexFile* const dex_file_;
+  // MethodId for the method to compare against.
+  const DexFile::MethodId* const mid_;
+  // Lazily computed name from the dex file's strings.
+  const char* name_;
+  // Lazily computed name length.
+  uint32_t name_len_;
+};
+
+class LinkVirtualHashTable {
+ public:
+  LinkVirtualHashTable(Handle<mirror::Class> klass, size_t hash_size, uint32_t* hash_table)
+     : klass_(klass), hash_size_(hash_size), hash_table_(hash_table) {
+    std::fill(hash_table_, hash_table_ + hash_size_, invalid_index_);
+  }
+  void Add(uint32_t virtual_method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking(virtual_method_index);
+    const char* name = local_method->GetName();
+    uint32_t hash = ComputeModifiedUtf8Hash(name);
+    uint32_t index = hash % hash_size_;
+    // Linear probe until we have an empty slot.
+    while (hash_table_[index] != invalid_index_) {
+      if (++index == hash_size_) {
+        index = 0;
+      }
+    }
+    hash_table_[index] = virtual_method_index;
+  }
+  uint32_t FindAndRemove(MethodNameAndSignatureComparator* comparator)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    const char* name = comparator->GetName();
+    uint32_t hash = ComputeModifiedUtf8Hash(name);
+    size_t index = hash % hash_size_;
+    while (true) {
+      const uint32_t value = hash_table_[index];
+      // Since linear probe makes continuous blocks, hitting an invalid index means we are done
+      // the block and can safely assume not found.
+      if (value == invalid_index_) {
+        break;
+      }
+      if (value != removed_index_) {  // This signifies not already overriden.
+        mirror::ArtMethod* virtual_method =
+            klass_->GetVirtualMethodDuringLinking(value);
+        if (comparator->HasSameNameAndSignature(virtual_method->GetInterfaceMethodIfProxy())) {
+          hash_table_[index] = removed_index_;
+          return value;
+        }
+      }
+      if (++index == hash_size_) {
+        index = 0;
+      }
+    }
+    return GetNotFoundIndex();
+  }
+  static uint32_t GetNotFoundIndex() {
+    return invalid_index_;
+  }
+
+ private:
+  static const uint32_t invalid_index_;
+  static const uint32_t removed_index_;
+
+  Handle<mirror::Class> klass_;
+  const size_t hash_size_;
+  uint32_t* const hash_table_;
+};
+
+const uint32_t LinkVirtualHashTable::invalid_index_ = std::numeric_limits<uint32_t>::max();
+const uint32_t LinkVirtualHashTable::removed_index_ = std::numeric_limits<uint32_t>::max() - 1;
+
 bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) {
+  const size_t num_virtual_methods = klass->NumVirtualMethods();
   if (klass->HasSuperClass()) {
-    uint32_t max_count = klass->NumVirtualMethods() +
-        klass->GetSuperClass()->GetVTableLength();
-    size_t actual_count = klass->GetSuperClass()->GetVTableLength();
-    CHECK_LE(actual_count, max_count);
-    StackHandleScope<4> hs(self);
+    const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
+    const size_t max_count = num_virtual_methods + super_vtable_length;
+    StackHandleScope<2> hs(self);
     Handle<mirror::Class> super_class(hs.NewHandle(klass->GetSuperClass()));
     Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable;
     if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
@@ -4570,54 +4921,90 @@
         CHECK(self->IsExceptionPending());  // OOME.
         return false;
       }
-      int len = super_class->GetVTableLength();
-      for (int i = 0; i < len; i++) {
-        vtable->Set<false>(i, super_class->GetVTableEntry(i));
+      for (size_t i = 0; i < super_vtable_length; i++) {
+        vtable->SetWithoutChecks<false>(i, super_class->GetEmbeddedVTableEntry(i));
+      }
+      if (num_virtual_methods == 0) {
+        klass->SetVTable(vtable.Get());
+        return true;
       }
     } else {
-      CHECK(super_class->GetVTable() != nullptr) << PrettyClass(super_class.Get());
-      vtable = hs.NewHandle(super_class->GetVTable()->CopyOf(self, max_count));
+      mirror::ObjectArray<mirror::ArtMethod>* super_vtable = super_class->GetVTable();
+      CHECK(super_vtable != nullptr) << PrettyClass(super_class.Get());
+      if (num_virtual_methods == 0) {
+        klass->SetVTable(super_vtable);
+        return true;
+      }
+      vtable = hs.NewHandle(super_vtable->CopyOf(self, max_count));
       if (UNLIKELY(vtable.Get() == nullptr)) {
         CHECK(self->IsExceptionPending());  // OOME.
         return false;
       }
     }
-
-    // See if any of our virtual methods override the superclass.
-    MethodHelper local_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-    MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-    for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
-      mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i);
-      local_mh.ChangeMethod(local_method);
-      size_t j = 0;
-      for (; j < actual_count; ++j) {
-        mirror::ArtMethod* super_method = vtable->Get(j);
-        super_mh.ChangeMethod(super_method);
-        if (local_mh.HasSameNameAndSignature(&super_mh)) {
-          if (klass->CanAccessMember(super_method->GetDeclaringClass(),
-                                     super_method->GetAccessFlags())) {
-            if (super_method->IsFinal()) {
-              ThrowLinkageError(klass.Get(), "Method %s overrides final method in class %s",
-                                PrettyMethod(local_method).c_str(),
-                                super_method->GetDeclaringClassDescriptor());
-              return false;
-            }
-            vtable->Set<false>(j, local_method);
-            local_method->SetMethodIndex(j);
-            break;
-          } else {
-            LOG(WARNING) << "Before Android 4.1, method " << PrettyMethod(local_method)
-                         << " would have incorrectly overridden the package-private method in "
-                         << PrettyDescriptor(super_method->GetDeclaringClassDescriptor());
+    // How the algorithm works:
+    // 1. Populate hash table by adding num_virtual_methods from klass. The values in the hash
+    // table are: invalid_index for unused slots, index super_vtable_length + i for a virtual
+    // method which has not been matched to a vtable method, and j if the virtual method at the
+    // index overrode the super virtual method at index j.
+    // 2. Loop through super virtual methods, if they overwrite, update hash table to j
+    // (j < super_vtable_length) to avoid redundant checks. (TODO maybe use this info for reducing
+    // the need for the initial vtable which we later shrink back down).
+    // 3. Add non overridden methods to the end of the vtable.
+    static constexpr size_t kMaxStackHash = 250;
+    const size_t hash_table_size = num_virtual_methods * 3;
+    uint32_t* hash_table_ptr;
+    std::unique_ptr<uint32_t[]> hash_heap_storage;
+    if (hash_table_size <= kMaxStackHash) {
+      hash_table_ptr = reinterpret_cast<uint32_t*>(
+          alloca(hash_table_size * sizeof(*hash_table_ptr)));
+    } else {
+      hash_heap_storage.reset(new uint32_t[hash_table_size]);
+      hash_table_ptr = hash_heap_storage.get();
+    }
+    LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr);
+    // Add virtual methods to the hash table.
+    for (size_t i = 0; i < num_virtual_methods; ++i) {
+      hash_table.Add(i);
+    }
+    // Loop through each super vtable method and see if they are overriden by a method we added to
+    // the hash table.
+    for (size_t j = 0; j < super_vtable_length; ++j) {
+      // Search the hash table to see if we are overidden by any method.
+      mirror::ArtMethod* super_method = vtable->GetWithoutChecks(j);
+      MethodNameAndSignatureComparator super_method_name_comparator(
+          super_method->GetInterfaceMethodIfProxy());
+      uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator);
+      if (hash_index != hash_table.GetNotFoundIndex()) {
+        mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(hash_index);
+        if (klass->CanAccessMember(super_method->GetDeclaringClass(),
+                                   super_method->GetAccessFlags())) {
+          if (super_method->IsFinal()) {
+            ThrowLinkageError(klass.Get(), "Method %s overrides final method in class %s",
+                              PrettyMethod(virtual_method).c_str(),
+                              super_method->GetDeclaringClassDescriptor());
+            return false;
           }
+          vtable->SetWithoutChecks<false>(j, virtual_method);
+          virtual_method->SetMethodIndex(j);
+        } else {
+          LOG(WARNING) << "Before Android 4.1, method " << PrettyMethod(virtual_method)
+                       << " would have incorrectly overridden the package-private method in "
+                       << PrettyDescriptor(super_method->GetDeclaringClassDescriptor());
         }
       }
-      if (j == actual_count) {
-        // Not overriding, append.
-        vtable->Set<false>(actual_count, local_method);
-        local_method->SetMethodIndex(actual_count);
-        actual_count += 1;
+    }
+    // Add the non overridden methods at the end.
+    size_t actual_count = super_vtable_length;
+    for (size_t i = 0; i < num_virtual_methods; ++i) {
+      mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i);
+      size_t method_idx = local_method->GetMethodIndexDuringLinking();
+      if (method_idx < super_vtable_length &&
+          local_method == vtable->GetWithoutChecks(method_idx)) {
+        continue;
       }
+      vtable->SetWithoutChecks<false>(actual_count, local_method);
+      local_method->SetMethodIndex(actual_count);
+      ++actual_count;
     }
     if (!IsUint(16, actual_count)) {
       ThrowClassFormatError(klass.Get(), "Too many methods defined on class: %zd", actual_count);
@@ -4635,72 +5022,72 @@
     klass->SetVTable(vtable.Get());
   } else {
     CHECK_EQ(klass.Get(), GetClassRoot(kJavaLangObject));
-    uint32_t num_virtual_methods = klass->NumVirtualMethods();
     if (!IsUint(16, num_virtual_methods)) {
-      ThrowClassFormatError(klass.Get(), "Too many methods: %d", num_virtual_methods);
+      ThrowClassFormatError(klass.Get(), "Too many methods: %d",
+                            static_cast<int>(num_virtual_methods));
       return false;
     }
-    StackHandleScope<1> hs(self);
-    Handle<mirror::ObjectArray<mirror::ArtMethod>>
-        vtable(hs.NewHandle(AllocArtMethodArray(self, num_virtual_methods)));
-    if (UNLIKELY(vtable.Get() == nullptr)) {
+    mirror::ObjectArray<mirror::ArtMethod>* vtable = AllocArtMethodArray(self, num_virtual_methods);
+    if (UNLIKELY(vtable == nullptr)) {
       CHECK(self->IsExceptionPending());  // OOME.
       return false;
     }
     for (size_t i = 0; i < num_virtual_methods; ++i) {
       mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i);
-      vtable->Set<false>(i, virtual_method);
+      vtable->SetWithoutChecks<false>(i, virtual_method);
       virtual_method->SetMethodIndex(i & 0xFFFF);
     }
-    klass->SetVTable(vtable.Get());
+    klass->SetVTable(vtable);
   }
   return true;
 }
 
-bool ClassLinker::LinkInterfaceMethods(Handle<mirror::Class> klass,
-                                       Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
-  Thread* const self = Thread::Current();
+bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
+                                       Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+                                       StackHandleScope<mirror::Class::kImtSize>* out_imt) {
+  StackHandleScope<2> hs(self);
   Runtime* const runtime = Runtime::Current();
-  // Set the imt table to be all conflicts by default.
-  klass->SetImTable(runtime->GetDefaultImt());
-  size_t super_ifcount;
-  if (klass->HasSuperClass()) {
-    super_ifcount = klass->GetSuperClass()->GetIfTableCount();
-  } else {
-    super_ifcount = 0;
-  }
-  uint32_t num_interfaces =
-      interfaces.Get() == nullptr ? klass->NumDirectInterfaces() : interfaces->GetLength();
-  size_t ifcount = super_ifcount + num_interfaces;
-  for (size_t i = 0; i < num_interfaces; i++) {
-    mirror::Class* interface =
-        interfaces.Get() == nullptr ? mirror::Class::GetDirectInterface(self, klass, i) :
-            interfaces->Get(i);
-    ifcount += interface->GetIfTableCount();
-  }
-  if (ifcount == 0) {
-    // Class implements no interfaces.
-    DCHECK_EQ(klass->GetIfTableCount(), 0);
-    DCHECK(klass->GetIfTable() == nullptr);
-    return true;
-  }
-  if (ifcount == super_ifcount) {
+  const bool has_superclass = klass->HasSuperClass();
+  const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+  const bool have_interfaces = interfaces.Get() != nullptr;
+  const size_t num_interfaces =
+      have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
+  if (num_interfaces == 0) {
+    if (super_ifcount == 0) {
+      // Class implements no interfaces.
+      DCHECK_EQ(klass->GetIfTableCount(), 0);
+      DCHECK(klass->GetIfTable() == nullptr);
+      return true;
+    }
     // Class implements same interfaces as parent, are any of these not marker interfaces?
     bool has_non_marker_interface = false;
     mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();
-    for (size_t i = 0; i < ifcount; ++i) {
+    for (size_t i = 0; i < super_ifcount; ++i) {
       if (super_iftable->GetMethodArrayCount(i) > 0) {
         has_non_marker_interface = true;
         break;
       }
     }
+    // Class just inherits marker interfaces from parent so recycle parent's iftable.
     if (!has_non_marker_interface) {
-      // Class just inherits marker interfaces from parent so recycle parent's iftable.
       klass->SetIfTable(super_iftable);
       return true;
     }
   }
-  StackHandleScope<4> hs(self);
+  size_t ifcount = super_ifcount + num_interfaces;
+  for (size_t i = 0; i < num_interfaces; i++) {
+    mirror::Class* interface = have_interfaces ?
+        interfaces->GetWithoutChecks(i) : mirror::Class::GetDirectInterface(self, klass, i);
+    DCHECK(interface != nullptr);
+    if (UNLIKELY(!interface->IsInterface())) {
+      std::string temp;
+      ThrowIncompatibleClassChangeError(klass.Get(), "Class %s implements non-interface class %s",
+                                        PrettyDescriptor(klass.Get()).c_str(),
+                                        PrettyDescriptor(interface->GetDescriptor(&temp)).c_str());
+      return false;
+    }
+    ifcount += interface->GetIfTableCount();
+  }
   Handle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
   if (UNLIKELY(iftable.Get() == nullptr)) {
     CHECK(self->IsExceptionPending());  // OOME.
@@ -4716,17 +5103,8 @@
   // Flatten the interface inheritance hierarchy.
   size_t idx = super_ifcount;
   for (size_t i = 0; i < num_interfaces; i++) {
-    mirror::Class* interface =
-        interfaces.Get() == nullptr ? mirror::Class::GetDirectInterface(self, klass, i) :
-            interfaces->Get(i);
-    DCHECK(interface != nullptr);
-    if (!interface->IsInterface()) {
-      std::string temp;
-      ThrowIncompatibleClassChangeError(klass.Get(), "Class %s implements non-interface class %s",
-                                        PrettyDescriptor(klass.Get()).c_str(),
-                                        PrettyDescriptor(interface->GetDescriptor(&temp)).c_str());
-      return false;
-    }
+    mirror::Class* interface = have_interfaces ? interfaces->Get(i) :
+        mirror::Class::GetDirectInterface(self, klass, i);
     // Check if interface is already in iftable
     bool duplicate = false;
     for (size_t j = 0; j < idx; j++) {
@@ -4758,6 +5136,7 @@
   }
   // Shrink iftable in case duplicates were found
   if (idx < ifcount) {
+    DCHECK_NE(num_interfaces, 0U);
     iftable.Assign(down_cast<mirror::IfTable*>(iftable->CopyOf(self, idx * mirror::IfTable::kMax)));
     if (UNLIKELY(iftable.Get() == nullptr)) {
       CHECK(self->IsExceptionPending());  // OOME.
@@ -4765,41 +5144,94 @@
     }
     ifcount = idx;
   } else {
-    CHECK_EQ(idx, ifcount);
+    DCHECK_EQ(idx, ifcount);
   }
   klass->SetIfTable(iftable.Get());
-
   // If we're an interface, we don't need the vtable pointers, so we're done.
   if (klass->IsInterface()) {
     return true;
   }
-  // Allocate imtable
-  bool imtable_changed = false;
-  Handle<mirror::ObjectArray<mirror::ArtMethod>> imtable(
-      hs.NewHandle(AllocArtMethodArray(self, mirror::Class::kImtSize)));
-  if (UNLIKELY(imtable.Get() == nullptr)) {
-    CHECK(self->IsExceptionPending());  // OOME.
-    return false;
-  }
-  MethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-  MethodHelper vtable_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+  Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
+      hs.NewHandle(klass->GetVTableDuringLinking()));
   std::vector<mirror::ArtMethod*> miranda_list;
+  // Copy the IMT from the super class if possible.
+  bool extend_super_iftable = false;
+  if (has_superclass) {
+    mirror::Class* super_class = klass->GetSuperClass();
+    extend_super_iftable = true;
+    if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
+      for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+        out_imt->SetReference(i, super_class->GetEmbeddedImTableEntry(i));
+      }
+    } else {
+      // No imt in the super class, need to reconstruct from the iftable.
+      mirror::IfTable* if_table = super_class->GetIfTable();
+      mirror::ArtMethod* conflict_method = runtime->GetImtConflictMethod();
+      const size_t length = super_class->GetIfTableCount();
+      for (size_t i = 0; i < length; ++i) {
+        mirror::Class* interface = iftable->GetInterface(i);
+        const size_t num_virtuals = interface->NumVirtualMethods();
+        const size_t method_array_count = if_table->GetMethodArrayCount(i);
+        DCHECK_EQ(num_virtuals, method_array_count);
+        if (method_array_count == 0) {
+          continue;
+        }
+        mirror::ObjectArray<mirror::ArtMethod>* method_array = if_table->GetMethodArray(i);
+        for (size_t j = 0; j < num_virtuals; ++j) {
+          mirror::ArtMethod* method = method_array->GetWithoutChecks(j);
+          if (method->IsMiranda()) {
+            continue;
+          }
+          mirror::ArtMethod* interface_method = interface->GetVirtualMethod(j);
+          uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+          mirror::ArtMethod* imt_ref = out_imt->GetReference(imt_index)->AsArtMethod();
+          if (imt_ref == runtime->GetImtUnimplementedMethod()) {
+            out_imt->SetReference(imt_index, method);
+          } else if (imt_ref != conflict_method) {
+            out_imt->SetReference(imt_index, conflict_method);
+          }
+        }
+      }
+    }
+  }
   for (size_t i = 0; i < ifcount; ++i) {
     size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
     if (num_methods > 0) {
       StackHandleScope<2> hs(self);
-      Handle<mirror::ObjectArray<mirror::ArtMethod>>
-          method_array(hs.NewHandle(AllocArtMethodArray(self, num_methods)));
+      const bool is_super = i < super_ifcount;
+      const bool super_interface = is_super && extend_super_iftable;
+      Handle<mirror::ObjectArray<mirror::ArtMethod>> method_array;
+      Handle<mirror::ObjectArray<mirror::ArtMethod>> input_array;
+      if (super_interface) {
+        mirror::IfTable* if_table = klass->GetSuperClass()->GetIfTable();
+        DCHECK(if_table != nullptr);
+        DCHECK(if_table->GetMethodArray(i) != nullptr);
+        // If we are working on a super interface, try extending the existing method array.
+        method_array = hs.NewHandle(if_table->GetMethodArray(i)->Clone(self)->
+            AsObjectArray<mirror::ArtMethod>());
+        // We are overwriting a super class interface, try to only virtual methods instead of the
+        // whole vtable.
+        input_array = hs.NewHandle(klass->GetVirtualMethods());
+      } else {
+        method_array = hs.NewHandle(AllocArtMethodArray(self, num_methods));
+        // A new interface, we need the whole vtable incase a new interface method is implemented
+        // in the whole superclass.
+        input_array = vtable;
+      }
       if (UNLIKELY(method_array.Get() == nullptr)) {
         CHECK(self->IsExceptionPending());  // OOME.
         return false;
       }
       iftable->SetMethodArray(i, method_array.Get());
-      Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
-          hs.NewHandle(klass->GetVTableDuringLinking()));
+      if (input_array.Get() == nullptr) {
+        // If the added virtual methods is empty, do nothing.
+        DCHECK(super_interface);
+        continue;
+      }
       for (size_t j = 0; j < num_methods; ++j) {
         mirror::ArtMethod* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j);
-        interface_mh.ChangeMethod(interface_method);
+        MethodNameAndSignatureComparator interface_name_comparator(
+            interface_method->GetInterfaceMethodIfProxy());
         int32_t k;
         // For each method listed in the interface's method list, find the
         // matching method in our class's method list.  We want to favor the
@@ -4809,10 +5241,12 @@
         // it -- otherwise it would use the same vtable slot.  In .dex files
         // those don't end up in the virtual method table, so it shouldn't
         // matter which direction we go.  We walk it backward anyway.)
-        for (k = vtable->GetLength() - 1; k >= 0; --k) {
-          mirror::ArtMethod* vtable_method = vtable->Get(k);
-          vtable_mh.ChangeMethod(vtable_method);
-          if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
+        for (k = input_array->GetLength() - 1; k >= 0; --k) {
+          mirror::ArtMethod* vtable_method = input_array->GetWithoutChecks(k);
+          mirror::ArtMethod* vtable_method_for_name_comparison =
+              vtable_method->GetInterfaceMethodIfProxy();
+          if (interface_name_comparator.HasSameNameAndSignature(
+              vtable_method_for_name_comparison)) {
             if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
               ThrowIllegalAccessError(
                   klass.Get(),
@@ -4821,53 +5255,52 @@
                   PrettyMethod(interface_method).c_str());
               return false;
             }
-            method_array->Set<false>(j, vtable_method);
+            method_array->SetWithoutChecks<false>(j, vtable_method);
             // Place method in imt if entry is empty, place conflict otherwise.
             uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
-            if (imtable->Get(imt_index) == nullptr) {
-              imtable->Set<false>(imt_index, vtable_method);
-              imtable_changed = true;
-            } else {
-              imtable->Set<false>(imt_index, runtime->GetImtConflictMethod());
+            mirror::ArtMethod* imt_ref = out_imt->GetReference(imt_index)->AsArtMethod();
+            mirror::ArtMethod* conflict_method = runtime->GetImtConflictMethod();
+            if (imt_ref == runtime->GetImtUnimplementedMethod()) {
+              out_imt->SetReference(imt_index, vtable_method);
+            } else if (imt_ref != conflict_method) {
+              // If we are not a conflict and we have the same signature and name as the imt entry,
+              // it must be that we overwrote a superclass vtable entry.
+              MethodNameAndSignatureComparator imt_ref_name_comparator(
+                  imt_ref->GetInterfaceMethodIfProxy());
+              if (imt_ref_name_comparator.HasSameNameAndSignature(
+                  vtable_method_for_name_comparison)) {
+                out_imt->SetReference(imt_index, vtable_method);
+              } else {
+                out_imt->SetReference(imt_index, conflict_method);
+              }
             }
             break;
           }
         }
-        if (k < 0) {
-          StackHandleScope<1> hs(self);
-          auto miranda_method = hs.NewHandle<mirror::ArtMethod>(nullptr);
+        if (k < 0 && !super_interface) {
+          mirror::ArtMethod* miranda_method = nullptr;
           for (mirror::ArtMethod* mir_method : miranda_list) {
-            vtable_mh.ChangeMethod(mir_method);
-            if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
-              miranda_method.Assign(mir_method);
+            if (interface_name_comparator.HasSameNameAndSignature(
+                mir_method->GetInterfaceMethodIfProxy())) {
+              miranda_method = mir_method;
               break;
             }
           }
-          if (miranda_method.Get() == nullptr) {
+          if (miranda_method == nullptr) {
             // Point the interface table at a phantom slot.
-            miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_method->Clone(self)));
-            if (UNLIKELY(miranda_method.Get() == nullptr)) {
+            miranda_method = down_cast<mirror::ArtMethod*>(interface_method->Clone(self));
+            if (UNLIKELY(miranda_method == nullptr)) {
               CHECK(self->IsExceptionPending());  // OOME.
               return false;
             }
             // TODO: If a methods move then the miranda_list may hold stale references.
-            miranda_list.push_back(miranda_method.Get());
+            miranda_list.push_back(miranda_method);
           }
-          method_array->Set<false>(j, miranda_method.Get());
+          method_array->SetWithoutChecks<false>(j, miranda_method);
         }
       }
     }
   }
-  if (imtable_changed) {
-    // Fill in empty entries in interface method table with conflict.
-    mirror::ArtMethod* imt_conflict_method = runtime->GetImtConflictMethod();
-    for (size_t i = 0; i < mirror::Class::kImtSize; i++) {
-      if (imtable->Get(i) == nullptr) {
-        imtable->Set<false>(i, imt_conflict_method);
-      }
-    }
-    klass->SetImTable(imtable.Get());
-  }
   if (!miranda_list.empty()) {
     int old_method_count = klass->NumVirtualMethods();
     int new_method_count = old_method_count + miranda_list.size();
@@ -4883,10 +5316,6 @@
     }
     klass->SetVirtualMethods(virtuals);
 
-    StackHandleScope<1> hs(self);
-    Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
-        hs.NewHandle(klass->GetVTableDuringLinking()));
-    CHECK(vtable.Get() != nullptr);
     int old_vtable_count = vtable->GetLength();
     int new_vtable_count = old_vtable_count + miranda_list.size();
     vtable.Assign(vtable->CopyOf(self, new_vtable_count));
@@ -4900,19 +5329,19 @@
       method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
       method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
       klass->SetVirtualMethod(old_method_count + i, method);
-      vtable->Set<false>(old_vtable_count + i, method);
+      vtable->SetWithoutChecks<false>(old_vtable_count + i, method);
     }
     // TODO: do not assign to the vtable field until it is fully constructed.
     klass->SetVTable(vtable.Get());
   }
 
-  mirror::ObjectArray<mirror::ArtMethod>* vtable = klass->GetVTableDuringLinking();
-  for (int i = 0; i < vtable->GetLength(); ++i) {
-    CHECK(vtable->Get(i) != nullptr);
+  if (kIsDebugBuild) {
+    mirror::ObjectArray<mirror::ArtMethod>* vtable = klass->GetVTableDuringLinking();
+    for (int i = 0; i < vtable->GetLength(); ++i) {
+      CHECK(vtable->GetWithoutChecks(i) != nullptr);
+    }
   }
 
-//  klass->DumpClass(std::cerr, Class::kDumpClassFullDetail);
-
   return true;
 }
 
@@ -4936,20 +5365,27 @@
     Primitive::Type type1 = field1->GetTypeAsPrimitiveType();
     Primitive::Type type2 = field2->GetTypeAsPrimitiveType();
     if (type1 != type2) {
-      bool is_primitive1 = type1 != Primitive::kPrimNot;
-      bool is_primitive2 = type2 != Primitive::kPrimNot;
-      bool is64bit1 = is_primitive1 && (type1 == Primitive::kPrimLong ||
-                                        type1 == Primitive::kPrimDouble);
-      bool is64bit2 = is_primitive2 && (type2 == Primitive::kPrimLong ||
-                                        type2 == Primitive::kPrimDouble);
-      int order1 = !is_primitive1 ? 0 : (is64bit1 ? 1 : 2);
-      int order2 = !is_primitive2 ? 0 : (is64bit2 ? 1 : 2);
-      if (order1 != order2) {
-        return order1 < order2;
+      if (type1 == Primitive::kPrimNot) {
+        // Reference always goes first.
+        return true;
       }
+      if (type2 == Primitive::kPrimNot) {
+        // Reference always goes first.
+        return false;
+      }
+      size_t size1 = Primitive::ComponentSize(type1);
+      size_t size2 = Primitive::ComponentSize(type2);
+      if (size1 != size2) {
+        // Larger primitive types go first.
+        return size1 > size2;
+      }
+      // Primitive types differ but sizes match. Arbitrarily order by primitive type.
+      return type1 < type2;
     }
-    // same basic group? then sort by string.
-    return strcmp(field1->GetName(), field2->GetName()) < 0;
+    // Same basic group? Then sort by dex field index. This is guaranteed to be sorted
+    // by name and for equal names by type id index.
+    // NOTE: This works also for proxies. Their static fields are assigned appropriate indexes.
+    return field1->GetDexFieldIndex() < field2->GetDexFieldIndex();
   }
 };
 
@@ -4963,13 +5399,7 @@
   // Initialize field_offset
   MemberOffset field_offset(0);
   if (is_static) {
-    uint32_t base = sizeof(mirror::Class);  // Static fields come after the class.
-    if (klass->ShouldHaveEmbeddedImtAndVTable()) {
-      // Static fields come after the embedded tables.
-      base = mirror::Class::ComputeClassSize(true, klass->GetVTableDuringLinking()->GetLength(),
-                                             0, 0, 0);
-    }
-    field_offset = MemberOffset(base);
+    field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking();
   } else {
     mirror::Class* super_class = klass->GetSuperClass();
     if (super_class != nullptr) {
@@ -5004,9 +5434,9 @@
     }
     grouped_and_sorted_fields.pop_front();
     num_reference_fields++;
-    fields->Set<false>(current_field, field);
     field->SetOffset(field_offset);
-    field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
+    field_offset = MemberOffset(field_offset.Uint32Value() +
+                                sizeof(mirror::HeapReference<mirror::Object>));
   }
 
   // Now we want to pack all of the double-wide fields together.  If
@@ -5020,14 +5450,15 @@
       if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) {
         continue;
       }
-      fields->Set<false>(current_field++, field);
+      current_field++;
       field->SetOffset(field_offset);
       // drop the consumed field
       grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i);
       break;
     }
     // whether we found a 32-bit field for padding or not, we advance
-    field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
+    field_offset = MemberOffset(field_offset.Uint32Value() +
+                                sizeof(mirror::HeapReference<mirror::Object>));
   }
 
   // Alignment is good, shuffle any double-wide fields forward, and
@@ -5039,7 +5470,6 @@
     grouped_and_sorted_fields.pop_front();
     Primitive::Type type = field->GetTypeAsPrimitiveType();
     CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
-    fields->Set<false>(current_field, field);
     field->SetOffset(field_offset);
     field_offset = MemberOffset(field_offset.Uint32Value() +
                                 ((type == Primitive::kPrimLong || type == Primitive::kPrimDouble)
@@ -5057,10 +5487,39 @@
     --num_reference_fields;
   }
 
+  size_t size = field_offset.Uint32Value();
+  // Update klass
+  if (is_static) {
+    klass->SetNumReferenceStaticFields(num_reference_fields);
+    *class_size = size;
+  } else {
+    klass->SetNumReferenceInstanceFields(num_reference_fields);
+    if (!klass->IsVariableSize()) {
+      if (klass->DescriptorEquals("Ljava/lang/reflect/ArtMethod;")) {
+        klass->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
+      } else {
+        std::string temp;
+        DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp);
+        size_t previous_size = klass->GetObjectSize();
+        if (previous_size != 0) {
+          // Make sure that we didn't originally have an incorrect size.
+          CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp);
+        }
+        klass->SetObjectSize(size);
+      }
+    }
+  }
+
   if (kIsDebugBuild) {
-    // Make sure that all reference fields appear before
-    // non-reference fields, and all double-wide fields are aligned.
-    bool seen_non_ref = false;
+    // Make sure that the fields array is ordered by name but all reference
+    // offsets are at the beginning as far as alignment allows.
+    MemberOffset start_ref_offset = is_static
+        ? klass->GetFirstReferenceStaticFieldOffsetDuringLinking()
+        : klass->GetFirstReferenceInstanceFieldOffset();
+    MemberOffset end_ref_offset(start_ref_offset.Uint32Value() +
+                                num_reference_fields *
+                                    sizeof(mirror::HeapReference<mirror::Object>));
+    MemberOffset current_ref_offset = start_ref_offset;
     for (size_t i = 0; i < num_fields; i++) {
       mirror::ArtField* field = fields->Get(i);
       if (false) {  // enable to debug field layout
@@ -5070,49 +5529,40 @@
                     << " offset="
                     << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()));
       }
+      if (i != 0) {
+        mirror::ArtField* prev_field = fields->Get(i - 1u);
+        // NOTE: The field names can be the same. This is not possible in the Java language
+        // but it's valid Java/dex bytecode and for example proguard can generate such bytecode.
+        CHECK_LE(strcmp(prev_field->GetName(), field->GetName()), 0);
+      }
       Primitive::Type type = field->GetTypeAsPrimitiveType();
       bool is_primitive = type != Primitive::kPrimNot;
       if (klass->DescriptorEquals("Ljava/lang/ref/Reference;") &&
           strcmp("referent", field->GetName()) == 0) {
         is_primitive = true;  // We lied above, so we have to expect a lie here.
       }
+      MemberOffset offset = field->GetOffsetDuringLinking();
       if (is_primitive) {
-        if (!seen_non_ref) {
-          seen_non_ref = true;
-          DCHECK_EQ(num_reference_fields, i) << PrettyField(field);
+        if (offset.Uint32Value() < end_ref_offset.Uint32Value()) {
+          // Shuffled before references.
+          size_t type_size = Primitive::ComponentSize(type);
+          CHECK_LT(type_size, sizeof(mirror::HeapReference<mirror::Object>));
+          CHECK_LT(offset.Uint32Value(), start_ref_offset.Uint32Value());
+          CHECK_LE(offset.Uint32Value() + type_size, start_ref_offset.Uint32Value());
+          CHECK(!IsAligned<sizeof(mirror::HeapReference<mirror::Object>)>(offset.Uint32Value()));
         }
       } else {
-        DCHECK(!seen_non_ref) << PrettyField(field);
+        CHECK_EQ(current_ref_offset.Uint32Value(), offset.Uint32Value());
+        current_ref_offset = MemberOffset(current_ref_offset.Uint32Value() +
+                                          sizeof(mirror::HeapReference<mirror::Object>));
       }
     }
-    if (!seen_non_ref) {
-      DCHECK_EQ(num_fields, num_reference_fields) << PrettyClass(klass.Get());
-    }
-  }
-
-  size_t size = field_offset.Uint32Value();
-  // Update klass
-  if (is_static) {
-    klass->SetNumReferenceStaticFields(num_reference_fields);
-    *class_size = size;
-  } else {
-    klass->SetNumReferenceInstanceFields(num_reference_fields);
-    if (!klass->IsVariableSize()) {
-      std::string temp;
-      DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp);
-      size_t previous_size = klass->GetObjectSize();
-      if (previous_size != 0) {
-        // Make sure that we didn't originally have an incorrect size.
-        CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp);
-      }
-      klass->SetObjectSize(size);
-    }
+    CHECK_EQ(current_ref_offset.Uint32Value(), end_ref_offset.Uint32Value());
   }
   return true;
 }
 
-//  Set the bitmap of reference offsets, refOffsets, from the ifields
-//  list.
+//  Set the bitmap of reference instance field offsets.
 void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) {
   uint32_t reference_offsets = 0;
   mirror::Class* super_class = klass->GetSuperClass();
@@ -5136,23 +5586,24 @@
   size_t num_reference_fields =
       is_static ? klass->NumReferenceStaticFieldsDuringLinking()
                 : klass->NumReferenceInstanceFieldsDuringLinking();
-  mirror::ObjectArray<mirror::ArtField>* fields =
-      is_static ? klass->GetSFields() : klass->GetIFields();
-  // All of the fields that contain object references are guaranteed
-  // to be at the beginning of the fields list.
-  for (size_t i = 0; i < num_reference_fields; ++i) {
-    // Note that byte_offset is the offset from the beginning of
-    // object, not the offset into instance data
-    mirror::ArtField* field = fields->Get(i);
-    MemberOffset byte_offset = field->GetOffsetDuringLinking();
-    CHECK_EQ(byte_offset.Uint32Value() & (CLASS_OFFSET_ALIGNMENT - 1), 0U);
-    if (CLASS_CAN_ENCODE_OFFSET(byte_offset.Uint32Value())) {
-      uint32_t new_bit = CLASS_BIT_FROM_OFFSET(byte_offset.Uint32Value());
-      CHECK_NE(new_bit, 0U);
-      reference_offsets |= new_bit;
-    } else {
+  if (num_reference_fields != 0u) {
+    // All of the fields that contain object references are guaranteed be grouped in memory
+    // starting at an appropriately aligned address after super class object data for instances
+    // and after the basic class data for classes.
+    uint32_t start_offset =
+        !is_static
+        ? klass->GetFirstReferenceInstanceFieldOffset().Uint32Value()
+        // Can't use klass->GetFirstReferenceStaticFieldOffset() yet.
+        : klass->ShouldHaveEmbeddedImtAndVTable()
+          ? mirror::Class::ComputeClassSize(
+              true, klass->GetVTableDuringLinking()->GetLength(), 0, 0, 0)
+          : sizeof(mirror::Class);
+    uint32_t start_bit = start_offset / sizeof(mirror::HeapReference<mirror::Object>);
+    if (start_bit + num_reference_fields > 32) {
       reference_offsets = CLASS_WALK_SUPER;
-      break;
+    } else {
+      reference_offsets |= (0xffffffffu >> start_bit) &
+                           (0xffffffffu << (32 - (start_bit + num_reference_fields)));
     }
   }
   // Update fields in klass
@@ -5462,9 +5913,8 @@
   std::vector<mirror::Class*> all_classes;
   {
     ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-    for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
-      mirror::Class* klass = it.second.Read();
-      all_classes.push_back(klass);
+    for (GcRoot<mirror::Class>& it : class_table_) {
+      all_classes.push_back(it.Read());
     }
   }
 
@@ -5478,7 +5928,8 @@
     MoveImageClassesToClassTable();
   }
   ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  os << "Loaded classes: " << class_table_.size() << " allocated classes\n";
+  os << "Zygote loaded classes=" << pre_zygote_class_table_.Size() << " post zygote classes="
+     << class_table_.Size() << "\n";
 }
 
 size_t ClassLinker::NumLoadedClasses() {
@@ -5486,7 +5937,8 @@
     MoveImageClassesToClassTable();
   }
   ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  return class_table_.size();
+  // Only return non zygote classes since these are the ones which apps which care about.
+  return class_table_.Size();
 }
 
 pid_t ClassLinker::GetClassesLockOwner() {
@@ -5509,4 +5961,71 @@
   class_roots->Set<false>(class_root, klass);
 }
 
+std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root)
+    const {
+  std::string temp;
+  return ComputeModifiedUtf8Hash(root.Read()->GetDescriptor(&temp));
+}
+
+bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
+                                                        const GcRoot<mirror::Class>& b) {
+  if (a.Read()->GetClassLoader() != b.Read()->GetClassLoader()) {
+    return false;
+  }
+  std::string temp;
+  return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp));
+}
+
+std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(
+    const std::pair<const char*, mirror::ClassLoader*>& element) const {
+  return ComputeModifiedUtf8Hash(element.first);
+}
+
+bool ClassLinker::ClassDescriptorHashEquals::operator()(
+    const GcRoot<mirror::Class>& a, const std::pair<const char*, mirror::ClassLoader*>& b) {
+  if (a.Read()->GetClassLoader() != b.second) {
+    return false;
+  }
+  return a.Read()->DescriptorEquals(b.first);
+}
+
+bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
+                                                        const char* descriptor) {
+  return a.Read()->DescriptorEquals(descriptor);
+}
+
+std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descriptor) const {
+  return ComputeModifiedUtf8Hash(descriptor);
+}
+
+bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) {
+  // Non-image methods don't use direct code pointer.
+  if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) {
+    return false;
+  }
+  if (m->IsPrivate()) {
+    // The method can only be called inside its own oat file. Therefore it won't be called using
+    // its direct code if the oat file has been compiled in PIC mode.
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile();
+    const OatFile::OatDexFile* oat_dex_file = class_linker->FindOpenedOatDexFileForDexFile(dex_file);
+    if (oat_dex_file == nullptr) {
+      // No oat file: the method has not been compiled.
+      return false;
+    }
+    const OatFile* oat_file = oat_dex_file->GetOatFile();
+    return oat_file != nullptr && !oat_file->IsPic();
+  } else {
+    // The method can be called outside its own oat file. Therefore it won't be called using its
+    // direct code pointer only if all loaded oat files have been compiled in PIC mode.
+    ReaderMutexLock mu(Thread::Current(), dex_lock_);
+    for (const OatFile* oat_file : oat_files_) {
+      if (!oat_file->IsPic()) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 510879c..03dfe8f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "base/allocator.h"
+#include "base/hash_set.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "dex_file.h"
@@ -75,9 +76,10 @@
                            Handle<mirror::ClassLoader> class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Find a class in the path class loader, loading it if necessary.
+  // Find a class in the path class loader, loading it if necessary. Hash function is supposed to
+  // be ComputeModifiedUtf8Hash(descriptor).
   mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                                            Thread* self, const char* descriptor,
+                                            Thread* self, const char* descriptor, size_t hash,
                                             Handle<mirror::ClassLoader> class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -94,14 +96,15 @@
   bool IsInitialized() const;
 
   // Define a new a class based on a ClassDef from a DexFile
-  mirror::Class* DefineClass(const char* descriptor,
+  mirror::Class* DefineClass(Thread* self, const char* descriptor, size_t hash,
                              Handle<mirror::ClassLoader> class_loader,
                              const DexFile& dex_file, const DexFile::ClassDef& dex_class_def)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
   // by the given 'class_loader'.
-  mirror::Class* LookupClass(const char* descriptor, const mirror::ClassLoader* class_loader)
+  mirror::Class* LookupClass(const char* descriptor, size_t hash,
+                             mirror::ClassLoader* class_loader)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -114,7 +117,7 @@
 
   // General class unloading is not supported, this is used to prune
   // unwanted classes during image writing.
-  bool RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader)
+  bool RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -356,6 +359,12 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 #endif
 
+  // Get compiled code for a method, return null if no code
+  // exists. This is unlike Get..OatCodeFor which will return a bridge
+  // or interpreter entrypoint.
+  const void* GetOatMethodQuickCodeFor(mirror::ArtMethod* method)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   pid_t GetClassesLockOwner();  // For SignalCatcher.
   pid_t GetDexLockOwner();  // For SignalCatcher.
 
@@ -403,6 +412,21 @@
     return class_roots;
   }
 
+  // Move all of the image classes into the class table for faster lookups.
+  void MoveImageClassesToClassTable()
+      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // Move the class table to the pre-zygote table to reduce memory usage. This works by ensuring
+  // that no more classes are ever added to the pre zygote table which makes it that the pages
+  // always remain shared dirty instead of private dirty.
+  void MoveClassTableToPreZygote()
+      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Returns true if the method can be called with its direct code pointer, false otherwise.
+  bool MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
   bool FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -432,7 +456,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 
-  mirror::Class* CreateArrayClass(Thread* self, const char* descriptor,
+  mirror::Class* CreateArrayClass(Thread* self, const char* descriptor, size_t hash,
                                   Handle<mirror::ClassLoader> class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -514,14 +538,16 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool LinkMethods(Thread* self, Handle<mirror::Class> klass,
-                   Handle<mirror::ObjectArray<mirror::Class>> interfaces)
+                   Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+                   StackHandleScope<mirror::Class::kImtSize>* out_imt)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool LinkInterfaceMethods(Handle<mirror::Class> klass,
-                            Handle<mirror::ObjectArray<mirror::Class>> interfaces)
+  bool LinkInterfaceMethods(Thread* const self, Handle<mirror::Class> klass,
+                            Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+                            StackHandleScope<mirror::Class::kImtSize>* out_imt)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size)
@@ -643,7 +669,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   mirror::Class* LookupClassFromTableLocked(const char* descriptor,
-                                            const mirror::ClassLoader* class_loader,
+                                            mirror::ClassLoader* class_loader,
                                             size_t hash)
       SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
 
@@ -651,9 +677,6 @@
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
-      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   mirror::Class* LookupClassFromImage(const char* descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -676,15 +699,43 @@
   std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_);
   std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
 
+  class ClassDescriptorHashEquals {
+   public:
+    // Same class loader and descriptor.
+    std::size_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS;
+    bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b)
+        NO_THREAD_SAFETY_ANALYSIS;
+    // Same class loader and descriptor.
+    std::size_t operator()(const std::pair<const char*, mirror::ClassLoader*>& element) const
+        NO_THREAD_SAFETY_ANALYSIS;
+    bool operator()(const GcRoot<mirror::Class>& a,
+                    const std::pair<const char*, mirror::ClassLoader*>& b)
+        NO_THREAD_SAFETY_ANALYSIS;
+    // Same descriptor.
+    bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor)
+        NO_THREAD_SAFETY_ANALYSIS;
+    std::size_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS;
+  };
+  class GcRootEmptyFn {
+   public:
+    void MakeEmpty(GcRoot<mirror::Class>& item) const {
+      item = GcRoot<mirror::Class>();
+    }
+    bool IsEmpty(const GcRoot<mirror::Class>& item) const {
+      return item.IsNull();
+    }
+  };
 
-  // multimap from a string hash code of a class descriptor to
-  // mirror::Class* instances. Results should be compared for a matching
-  // Class::descriptor_ and Class::class_loader_.
-  typedef AllocationTrackingMultiMap<size_t, GcRoot<mirror::Class>, kAllocatorTagClassTable> Table;
+  // hash set which hashes class descriptor, and compares descriptors nad class loaders. Results
+  // should be compared for a matching Class descriptor and class loader.
+  typedef HashSet<GcRoot<mirror::Class>, GcRootEmptyFn, ClassDescriptorHashEquals,
+      ClassDescriptorHashEquals, TrackingAllocator<GcRoot<mirror::Class>, kAllocatorTagClassTable>>
+      Table;
   // This contains strong roots. To enable concurrent root scanning of
   // the class table, be careful to use a read barrier when accessing this.
   Table class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
-  std::vector<std::pair<size_t, GcRoot<mirror::Class>>> new_class_roots_;
+  Table pre_zygote_class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
+  std::vector<GcRoot<mirror::Class>> new_class_roots_;
 
   // Do we need to search dex caches to find image classes?
   bool dex_cache_image_class_lookup_required_;
@@ -734,7 +785,8 @@
   };
   GcRoot<mirror::ObjectArray<mirror::Class>> class_roots_;
 
-  mirror::Class* GetClassRoot(ClassRoot class_root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE mirror::Class* GetClassRoot(ClassRoot class_root)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetClassRoot(ClassRoot class_root, mirror::Class* klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -771,6 +823,9 @@
   const void* quick_generic_jni_trampoline_;
   const void* quick_to_interpreter_bridge_trampoline_;
 
+  // Image pointer size.
+  size_t image_pointer_size_;
+
   friend class ImageWriter;  // for GetClassRoots
   friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
   friend class ElfPatcher;  // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 37564e8..4d96287 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -164,11 +164,8 @@
     EXPECT_TRUE(method->GetName() != nullptr);
     EXPECT_TRUE(method->GetSignature() != Signature::NoSignature());
 
-    EXPECT_TRUE(method->GetDexCacheStrings() != nullptr);
     EXPECT_TRUE(method->HasDexCacheResolvedMethods());
     EXPECT_TRUE(method->HasDexCacheResolvedTypes());
-    EXPECT_EQ(method->GetDeclaringClass()->GetDexCache()->GetStrings(),
-              method->GetDexCacheStrings());
     EXPECT_TRUE(method->HasSameDexCacheResolvedMethods(
         method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods()));
     EXPECT_TRUE(method->HasSameDexCacheResolvedTypes(
@@ -205,6 +202,8 @@
     EXPECT_FALSE(klass->IsArrayClass());
     EXPECT_TRUE(klass->GetComponentType() == NULL);
     EXPECT_TRUE(klass->IsInSamePackage(klass.Get()));
+    EXPECT_TRUE(klass->GetDexCacheStrings() != nullptr);
+    EXPECT_EQ(klass->GetDexCacheStrings(), klass->GetDexCache()->GetStrings());
     std::string temp2;
     EXPECT_TRUE(mirror::Class::IsInSamePackage(klass->GetDescriptor(&temp),
                                                klass->GetDescriptor(&temp2)));
@@ -275,29 +274,41 @@
       EXPECT_TRUE(field->IsStatic());
     }
 
-    // Confirm that all instances fields are packed together at the start
+    // Confirm that all instances field offsets are packed together at the start.
     EXPECT_GE(klass->NumInstanceFields(), klass->NumReferenceInstanceFields());
     StackHandleScope<1> hs(Thread::Current());
     FieldHelper fh(hs.NewHandle<mirror::ArtField>(nullptr));
-    for (size_t i = 0; i < klass->NumReferenceInstanceFields(); i++) {
-      mirror::ArtField* field = klass->GetInstanceField(i);
-      fh.ChangeField(field);
-      ASSERT_TRUE(!field->IsPrimitiveType());
-      mirror::Class* field_type = fh.GetType();
-      ASSERT_TRUE(field_type != NULL);
-      ASSERT_TRUE(!field_type->IsPrimitive());
-    }
-    for (size_t i = klass->NumReferenceInstanceFields(); i < klass->NumInstanceFields(); i++) {
+    MemberOffset start_ref_offset = klass->GetFirstReferenceInstanceFieldOffset();
+    MemberOffset end_ref_offset(start_ref_offset.Uint32Value() +
+                                klass->NumReferenceInstanceFields() *
+                                    sizeof(mirror::HeapReference<mirror::Object>));
+    MemberOffset current_ref_offset = start_ref_offset;
+    for (size_t i = 0; i < klass->NumInstanceFields(); i++) {
       mirror::ArtField* field = klass->GetInstanceField(i);
       fh.ChangeField(field);
       mirror::Class* field_type = fh.GetType();
-      ASSERT_TRUE(field_type != NULL);
-      if (!fh.GetField()->IsPrimitiveType() || !field_type->IsPrimitive()) {
-        // While Reference.referent is not primitive, the ClassLinker
-        // treats it as such so that the garbage collector won't scan it.
-        EXPECT_EQ(PrettyField(fh.GetField()), "java.lang.Object java.lang.ref.Reference.referent");
+      ASSERT_TRUE(field_type != nullptr);
+      if (!field->IsPrimitiveType()) {
+        ASSERT_TRUE(!field_type->IsPrimitive());
+        ASSERT_EQ(current_ref_offset.Uint32Value(), field->GetOffset().Uint32Value());
+        if (current_ref_offset.Uint32Value() == end_ref_offset.Uint32Value()) {
+          // While Reference.referent is not primitive, the ClassLinker
+          // treats it as such so that the garbage collector won't scan it.
+          EXPECT_EQ(PrettyField(fh.GetField()),
+                    "java.lang.Object java.lang.ref.Reference.referent");
+        } else {
+          current_ref_offset = MemberOffset(current_ref_offset.Uint32Value() +
+                                            sizeof(mirror::HeapReference<mirror::Object>));
+        }
+      } else {
+        if (field->GetOffset().Uint32Value() < end_ref_offset.Uint32Value()) {
+          // Shuffled before references.
+          ASSERT_LT(field->GetOffset().Uint32Value(), start_ref_offset.Uint32Value());
+          CHECK(!IsAligned<4>(field->GetOffset().Uint32Value()));
+        }
       }
     }
+    ASSERT_EQ(end_ref_offset.Uint32Value(), current_ref_offset.Uint32Value());
 
     size_t total_num_reference_instance_fields = 0;
     mirror::Class* k = klass.Get();
@@ -353,7 +364,7 @@
     }
   }
 
-  static void TestRootVisitor(mirror::Object** root, void*, uint32_t, RootType) {
+  static void TestRootVisitor(mirror::Object** root, void*, const RootInfo&) {
     EXPECT_TRUE(*root != NULL);
   }
 };
@@ -380,7 +391,8 @@
 
     bool error = false;
 
-    if (!klass->IsClassClass() && !is_static) {
+    // Art method have a different size due to the padding field.
+    if (!klass->IsArtMethodClass() && !klass->IsClassClass() && !is_static) {
       size_t expected_size = is_static ? klass->GetClassSize(): klass->GetObjectSize();
       if (sizeof(T) != expected_size) {
         LOG(ERROR) << "Class size mismatch:"
@@ -453,10 +465,7 @@
 
 struct ObjectOffsets : public CheckOffsets<mirror::Object> {
   ObjectOffsets() : CheckOffsets<mirror::Object>(false, "Ljava/lang/Object;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Object, klass_),   "shadow$_klass_"));
-
-    // alphabetical 32-bit
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Object, monitor_), "shadow$_monitor_"));
 #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Object, x_rb_ptr_), "shadow$_x_rb_ptr_"));
@@ -467,11 +476,8 @@
 
 struct ArtFieldOffsets : public CheckOffsets<mirror::ArtField> {
   ArtFieldOffsets() : CheckOffsets<mirror::ArtField>(false, "Ljava/lang/reflect/ArtField;") {
-    // alphabetical references
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtField, declaring_class_), "declaringClass"));
-
-    // alphabetical 32-bit
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtField, access_flags_),    "accessFlags"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtField, declaring_class_), "declaringClass"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtField, field_dex_idx_),   "fieldDexIndex"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtField, offset_),          "offset"));
   };
@@ -479,23 +485,10 @@
 
 struct ArtMethodOffsets : public CheckOffsets<mirror::ArtMethod> {
   ArtMethodOffsets() : CheckOffsets<mirror::ArtMethod>(false, "Ljava/lang/reflect/ArtMethod;") {
-    // alphabetical references
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, access_flags_),                   "accessFlags"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, declaring_class_),                      "declaringClass"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_methods_),           "dexCacheResolvedMethods"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_types_),             "dexCacheResolvedTypes"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_strings_),                    "dexCacheStrings"));
-
-    // alphabetical 64-bit
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_interpreter_),            "entryPointFromInterpreter"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_jni_),                    "entryPointFromJni"));
-#if defined(ART_USE_PORTABLE_COMPILER)
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_portable_compiled_code_), "entryPointFromPortableCompiledCode"));
-#endif
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_quick_compiled_code_),    "entryPointFromQuickCompiledCode"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, gc_map_),                                  "gcMap"));
-
-    // alphabetical 32-bit
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, access_flags_),                   "accessFlags"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_code_item_offset_),           "dexCodeItemOffset"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_method_index_),               "dexMethodIndex"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, method_index_),                   "methodIndex"));
@@ -504,52 +497,45 @@
 
 struct ClassOffsets : public CheckOffsets<mirror::Class> {
   ClassOffsets() : CheckOffsets<mirror::Class>(false, "Ljava/lang/Class;") {
-    // alphabetical references
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, access_flags_),                  "accessFlags"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, class_loader_),                  "classLoader"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, class_size_),                    "classSize"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, clinit_thread_id_),              "clinitThreadId"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, component_type_),                "componentType"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_),                     "dexCache"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_),             "dexCacheStrings"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_),             "dexClassDefIndex"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_),                  "dexTypeIndex"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, direct_methods_),                "directMethods"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, ifields_),                       "iFields"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, iftable_),                       "ifTable"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, imtable_),                       "imTable"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, name_),                          "name"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, sfields_),                       "sFields"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, super_class_),                   "superClass"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, verify_error_class_),            "verifyErrorClass"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, virtual_methods_),               "virtualMethods"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, vtable_),                        "vtable"));
-
-    // alphabetical 32-bit
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, access_flags_),                  "accessFlags"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, class_size_),                    "classSize"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, clinit_thread_id_),              "clinitThreadId"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_),             "dexClassDefIndex"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_),                  "dexTypeIndex"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_instance_fields_), "numReferenceInstanceFields"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_static_fields_),   "numReferenceStaticFields"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, object_size_),                   "objectSize"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, primitive_type_),                "primitiveType"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, reference_instance_offsets_),    "referenceInstanceOffsets"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, reference_static_offsets_),      "referenceStaticOffsets"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, sfields_),                       "sFields"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, status_),                        "status"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, super_class_),                   "superClass"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, verify_error_class_),            "verifyErrorClass"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, virtual_methods_),               "virtualMethods"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, vtable_),                        "vtable"));
   };
 };
 
 struct StringOffsets : public CheckOffsets<mirror::String> {
   StringOffsets() : CheckOffsets<mirror::String>(false, "Ljava/lang/String;") {
-    // alphabetical references
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, array_),     "value"));
-
-    // alphabetical 32-bit
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, count_),     "count"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, hash_code_), "hashCode"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, offset_),    "offset"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, array_),     "value"));
   };
 };
 
 struct ThrowableOffsets : public CheckOffsets<mirror::Throwable> {
   ThrowableOffsets() : CheckOffsets<mirror::Throwable>(false, "Ljava/lang/Throwable;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, cause_),                 "cause"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, detail_message_),        "detailMessage"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, stack_state_),           "stackState"));
@@ -560,17 +546,15 @@
 
 struct StackTraceElementOffsets : public CheckOffsets<mirror::StackTraceElement> {
   StackTraceElementOffsets() : CheckOffsets<mirror::StackTraceElement>(false, "Ljava/lang/StackTraceElement;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, declaring_class_), "declaringClass"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, file_name_),       "fileName"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, method_name_),     "methodName"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, line_number_),     "lineNumber"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, method_name_),     "methodName"));
   };
 };
 
 struct ClassLoaderOffsets : public CheckOffsets<mirror::ClassLoader> {
   ClassLoaderOffsets() : CheckOffsets<mirror::ClassLoader>(false, "Ljava/lang/ClassLoader;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassLoader, packages_),   "packages"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassLoader, parent_),     "parent"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassLoader, proxyCache_), "proxyCache"));
@@ -579,27 +563,24 @@
 
 struct ProxyOffsets : public CheckOffsets<mirror::Proxy> {
   ProxyOffsets() : CheckOffsets<mirror::Proxy>(false, "Ljava/lang/reflect/Proxy;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Proxy, h_), "h"));
   };
 };
 
 struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> {
   DexCacheOffsets() : CheckOffsets<mirror::DexCache>(false, "Ljava/lang/DexCache;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_),                        "dex"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_),                   "dexFile"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, location_),                   "location"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_),            "resolvedFields"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_),           "resolvedMethods"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_),             "resolvedTypes"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_),                    "strings"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_),                   "dexFile"));
   };
 };
 
 struct ReferenceOffsets : public CheckOffsets<mirror::Reference> {
   ReferenceOffsets() : CheckOffsets<mirror::Reference>(false, "Ljava/lang/ref/Reference;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Reference, pending_next_),  "pendingNext"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Reference, queue_),         "queue"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Reference, queue_next_),    "queueNext"));
@@ -609,7 +590,6 @@
 
 struct FinalizerReferenceOffsets : public CheckOffsets<mirror::FinalizerReference> {
   FinalizerReferenceOffsets() : CheckOffsets<mirror::FinalizerReference>(false, "Ljava/lang/ref/FinalizerReference;") {
-    // alphabetical references
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, next_),   "next"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, prev_),   "prev"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, zombie_), "zombie"));
@@ -1155,4 +1135,24 @@
   CheckPreverified(statics.Get(), true);
 }
 
+TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) {
+  ScopedObjectAccess soa(Thread::Current());
+
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
+
+  // java.lang.Object is a bootstrap class.
+  Handle<mirror::Class> jlo_class(
+      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
+  ASSERT_TRUE(jlo_class.Get() != nullptr);
+  EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded());
+
+  // Statics is not a bootstrap class.
+  Handle<mirror::Class> statics(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
+  ASSERT_TRUE(statics.Get() != nullptr);
+  EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded());
+}
+
 }  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index e9e11f6..c299210 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -59,7 +59,7 @@
   filename_ += "/TmpFile-XXXXXX";
   int fd = mkstemp(&filename_[0]);
   CHECK_NE(-1, fd);
-  file_.reset(new File(fd, GetFilename()));
+  file_.reset(new File(fd, GetFilename(), true));
 }
 
 ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) {
@@ -67,7 +67,7 @@
   filename_ += suffix;
   int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666);
   CHECK_NE(-1, fd);
-  file_.reset(new File(fd, GetFilename()));
+  file_.reset(new File(fd, GetFilename(), true));
 }
 
 ScratchFile::ScratchFile(File* file) {
@@ -84,10 +84,19 @@
   return file_->Fd();
 }
 
+void ScratchFile::Close() {
+  if (file_.get() != nullptr) {
+    if (file_->FlushCloseOrErase() != 0) {
+      PLOG(WARNING) << "Error closing scratch file.";
+    }
+  }
+}
+
 void ScratchFile::Unlink() {
   if (!OS::FileExists(filename_.c_str())) {
     return;
   }
+  Close();
   int unlink_result = unlink(filename_.c_str());
   CHECK_EQ(0, unlink_result);
 }
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 5b014b3..c19b30f 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -55,6 +55,7 @@
 
   int GetFd() const;
 
+  void Close();
   void Unlink();
 
  private:
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e074fc1..2e23eb8 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -183,16 +183,20 @@
 
 class Breakpoint {
  public:
-  Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, bool need_full_deoptimization)
+  Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc,
+             DeoptimizationRequest::Kind deoptimization_kind)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-    : method_(nullptr), dex_pc_(dex_pc), need_full_deoptimization_(need_full_deoptimization) {
+    : method_(nullptr), dex_pc_(dex_pc), deoptimization_kind_(deoptimization_kind) {
+    CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing ||
+          deoptimization_kind_ == DeoptimizationRequest::kSelectiveDeoptimization ||
+          deoptimization_kind_ == DeoptimizationRequest::kFullDeoptimization);
     ScopedObjectAccessUnchecked soa(Thread::Current());
     method_ = soa.EncodeMethod(method);
   }
 
   Breakpoint(const Breakpoint& other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
     : method_(nullptr), dex_pc_(other.dex_pc_),
-      need_full_deoptimization_(other.need_full_deoptimization_) {
+      deoptimization_kind_(other.deoptimization_kind_) {
     ScopedObjectAccessUnchecked soa(Thread::Current());
     method_ = soa.EncodeMethod(other.Method());
   }
@@ -206,8 +210,8 @@
     return dex_pc_;
   }
 
-  bool NeedFullDeoptimization() const {
-    return need_full_deoptimization_;
+  DeoptimizationRequest::Kind GetDeoptimizationKind() const {
+    return deoptimization_kind_;
   }
 
  private:
@@ -216,7 +220,7 @@
   uint32_t dex_pc_;
 
   // Indicates whether breakpoint needs full deoptimization or selective deoptimization.
-  bool need_full_deoptimization_;
+  DeoptimizationRequest::Kind deoptimization_kind_;
 };
 
 static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs)
@@ -337,19 +341,18 @@
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
-void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                RootType root_type) {
+void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
   if (receiver != nullptr) {
-    callback(&receiver, arg, tid, root_type);
+    callback(&receiver, arg, root_info);
   }
   if (thread != nullptr) {
-    callback(&thread, arg, tid, root_type);
+    callback(&thread, arg, root_info);
   }
   if (klass != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&klass), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&klass), arg, root_info);
   }
   if (method != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
   }
 }
 
@@ -361,10 +364,9 @@
   method = nullptr;
 }
 
-void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                   RootType root_type) {
+void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
   if (method != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
   }
 }
 
@@ -731,6 +733,12 @@
   return gDisposed;
 }
 
+bool Dbg::RequiresDeoptimization() {
+  // We don't need deoptimization if everything runs with interpreter after
+  // enabling -Xint mode.
+  return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly();
+}
+
 void Dbg::GoActive() {
   // Enable all debugging features, including scans for breakpoints.
   // This is a no-op if we're already active.
@@ -763,7 +771,9 @@
   Thread* self = Thread::Current();
   ThreadState old_state = self->SetStateUnsafe(kRunnable);
   CHECK_NE(old_state, kRunnable);
-  runtime->GetInstrumentation()->EnableDeoptimization();
+  if (RequiresDeoptimization()) {
+    runtime->GetInstrumentation()->EnableDeoptimization();
+  }
   instrumentation_events_ = 0;
   gDebuggerActive = true;
   CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
@@ -801,7 +811,9 @@
                                                     instrumentation_events_);
       instrumentation_events_ = 0;
     }
-    runtime->GetInstrumentation()->DisableDeoptimization();
+    if (RequiresDeoptimization()) {
+      runtime->GetInstrumentation()->DisableDeoptimization();
+    }
     gDebuggerActive = false;
   }
   gRegistry->Clear();
@@ -2364,7 +2376,7 @@
 }
 
 void Dbg::ResumeVM() {
-  Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
+  Runtime::Current()->GetThreadList()->ResumeAllForDebugger();
 }
 
 JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspension) {
@@ -2460,298 +2472,325 @@
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                   JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
-  struct GetLocalVisitor : public StackVisitor {
-    GetLocalVisitor(const ScopedObjectAccessUnchecked& soa, Thread* thread, Context* context,
-                    JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, context), soa_(soa), frame_id_(frame_id), slot_(slot), tag_(tag),
-          buf_(buf), width_(width), error_(JDWP::ERR_NONE) {}
+// Walks the stack until we find the frame with the given FrameId.
+class FindFrameVisitor FINAL : public StackVisitor {
+ public:
+  FindFrameVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, context), frame_id_(frame_id), error_(JDWP::ERR_INVALID_FRAMEID) {}
 
-    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
-    // annotalysis.
-    bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
-      if (GetFrameId() != frame_id_) {
-        return true;  // Not our frame, carry on.
-      }
-      // TODO: check that the tag is compatible with the actual type of the slot!
-      // TODO: check slot is valid for this method or return INVALID_SLOT error.
-      mirror::ArtMethod* m = GetMethod();
-      if (m->IsNative()) {
-        // We can't read local value from native method.
-        error_ = JDWP::ERR_OPAQUE_FRAME;
-        return false;
-      }
-      uint16_t reg = DemangleSlot(slot_, m);
-      constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
-      switch (tag_) {
-        case JDWP::JT_BOOLEAN: {
-          CHECK_EQ(width_, 1U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
-            JDWP::Set1(buf_+1, intVal != 0);
-          } else {
-            VLOG(jdwp) << "failed to get boolean local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_BYTE: {
-          CHECK_EQ(width_, 1U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
-            JDWP::Set1(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get byte local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_SHORT:
-        case JDWP::JT_CHAR: {
-          CHECK_EQ(width_, 2U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
-            JDWP::Set2BE(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get short/char local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_INT: {
-          CHECK_EQ(width_, 4U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get int local " << reg << " = " << intVal;
-            JDWP::Set4BE(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get int local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_FLOAT: {
-          CHECK_EQ(width_, 4U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kFloatVReg, &intVal)) {
-            VLOG(jdwp) << "get float local " << reg << " = " << intVal;
-            JDWP::Set4BE(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get float local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_ARRAY:
-        case JDWP::JT_CLASS_LOADER:
-        case JDWP::JT_CLASS_OBJECT:
-        case JDWP::JT_OBJECT:
-        case JDWP::JT_STRING:
-        case JDWP::JT_THREAD:
-        case JDWP::JT_THREAD_GROUP: {
-          CHECK_EQ(width_, sizeof(JDWP::ObjectId));
-          uint32_t intVal;
-          if (GetVReg(m, reg, kReferenceVReg, &intVal)) {
-            mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
-            VLOG(jdwp) << "get " << tag_ << " object local " << reg << " = " << o;
-            if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
-              LOG(FATAL) << "Register " << reg << " expected to hold " << tag_ << " object: " << o;
-            }
-            tag_ = TagFromObject(soa_, o);
-            JDWP::SetObjectId(buf_+1, gRegistry->Add(o));
-          } else {
-            VLOG(jdwp) << "failed to get " << tag_ << " object local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_DOUBLE: {
-          CHECK_EQ(width_, 8U);
-          uint64_t longVal;
-          if (GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
-            VLOG(jdwp) << "get double local " << reg << " = " << longVal;
-            JDWP::Set8BE(buf_+1, longVal);
-          } else {
-            VLOG(jdwp) << "failed to get double local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_LONG: {
-          CHECK_EQ(width_, 8U);
-          uint64_t longVal;
-          if (GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) {
-            VLOG(jdwp) << "get long local " << reg << " = " << longVal;
-            JDWP::Set8BE(buf_+1, longVal);
-          } else {
-            VLOG(jdwp) << "failed to get long local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        default:
-          LOG(FATAL) << "Unknown tag " << tag_;
-          break;
-      }
-
-      // Prepend tag, which may have been updated.
-      JDWP::Set1(buf_, tag_);
-      return false;
+  // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
+  // annotalysis.
+  bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+    if (GetFrameId() != frame_id_) {
+      return true;  // Not our frame, carry on.
     }
-    const ScopedObjectAccessUnchecked& soa_;
-    const JDWP::FrameId frame_id_;
-    const int slot_;
-    JDWP::JdwpTag tag_;
-    uint8_t* const buf_;
-    const size_t width_;
-    JDWP::JdwpError error_;
-  };
+    mirror::ArtMethod* m = GetMethod();
+    if (m->IsNative()) {
+      // We can't read/write local value from/into native method.
+      error_ = JDWP::ERR_OPAQUE_FRAME;
+    } else {
+      // We found our frame.
+      error_ = JDWP::ERR_NONE;
+    }
+    return false;
+  }
+
+  JDWP::JdwpError GetError() const {
+    return error_;
+  }
+
+ private:
+  const JDWP::FrameId frame_id_;
+  JDWP::JdwpError error_;
+};
+
+JDWP::JdwpError Dbg::GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pReply) {
+  JDWP::ObjectId thread_id = request->ReadThreadId();
+  JDWP::FrameId frame_id = request->ReadFrameId();
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
-  if (error != JDWP::ERR_NONE) {
-    return error;
+  {
+    MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+    JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
   }
-  // TODO check thread is suspended by the debugger ?
+  // Find the frame with the given frame_id.
   std::unique_ptr<Context> context(Context::Create());
-  GetLocalVisitor visitor(soa, thread, context.get(), frame_id, slot, tag, buf, width);
+  FindFrameVisitor visitor(thread, context.get(), frame_id);
   visitor.WalkStack();
-  return visitor.error_;
+  if (visitor.GetError() != JDWP::ERR_NONE) {
+    return visitor.GetError();
+  }
+
+  // Read the values from visitor's context.
+  int32_t slot_count = request->ReadSigned32("slot count");
+  expandBufAdd4BE(pReply, slot_count);     /* "int values" */
+  for (int32_t i = 0; i < slot_count; ++i) {
+    uint32_t slot = request->ReadUnsigned32("slot");
+    JDWP::JdwpTag reqSigByte = request->ReadTag();
+
+    VLOG(jdwp) << "    --> slot " << slot << " " << reqSigByte;
+
+    size_t width = Dbg::GetTagWidth(reqSigByte);
+    uint8_t* ptr = expandBufAddSpace(pReply, width+1);
+    JDWP::JdwpError error = Dbg::GetLocalValue(visitor, soa, slot, reqSigByte, ptr, width);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
+  }
+  return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                   JDWP::JdwpTag tag, uint64_t value, size_t width) {
-  struct SetLocalVisitor : public StackVisitor {
-    SetLocalVisitor(Thread* thread, Context* context,
-                    JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint64_t value,
-                    size_t width)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, context),
-          frame_id_(frame_id), slot_(slot), tag_(tag), value_(value), width_(width),
-          error_(JDWP::ERR_NONE) {}
-
-    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
-    // annotalysis.
-    bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
-      if (GetFrameId() != frame_id_) {
-        return true;  // Not our frame, carry on.
+JDWP::JdwpError Dbg::GetLocalValue(const StackVisitor& visitor, ScopedObjectAccessUnchecked& soa,
+                                   int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
+  mirror::ArtMethod* m = visitor.GetMethod();
+  uint16_t reg = DemangleSlot(slot, m);
+  // TODO: check that the tag is compatible with the actual type of the slot!
+  // TODO: check slot is valid for this method or return INVALID_SLOT error.
+  constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
+  switch (tag) {
+    case JDWP::JT_BOOLEAN: {
+      CHECK_EQ(width, 1U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
+        JDWP::Set1(buf + 1, intVal != 0);
+      } else {
+        VLOG(jdwp) << "failed to get boolean local " << reg;
+        return kFailureErrorCode;
       }
-      // TODO: check that the tag is compatible with the actual type of the slot!
-      // TODO: check slot is valid for this method or return INVALID_SLOT error.
-      mirror::ArtMethod* m = GetMethod();
-      if (m->IsNative()) {
-        // We can't read local value from native method.
-        error_ = JDWP::ERR_OPAQUE_FRAME;
-        return false;
-      }
-      uint16_t reg = DemangleSlot(slot_, m);
-      constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
-      switch (tag_) {
-        case JDWP::JT_BOOLEAN:
-        case JDWP::JT_BYTE:
-          CHECK_EQ(width_, 1U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
-            VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_SHORT:
-        case JDWP::JT_CHAR:
-          CHECK_EQ(width_, 2U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
-            VLOG(jdwp) << "failed to set short/char local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_INT:
-          CHECK_EQ(width_, 4U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
-            VLOG(jdwp) << "failed to set int local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_FLOAT:
-          CHECK_EQ(width_, 4U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kFloatVReg)) {
-            VLOG(jdwp) << "failed to set float local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_ARRAY:
-        case JDWP::JT_CLASS_LOADER:
-        case JDWP::JT_CLASS_OBJECT:
-        case JDWP::JT_OBJECT:
-        case JDWP::JT_STRING:
-        case JDWP::JT_THREAD:
-        case JDWP::JT_THREAD_GROUP: {
-          CHECK_EQ(width_, sizeof(JDWP::ObjectId));
-          mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value_));
-          if (o == ObjectRegistry::kInvalidObject) {
-            VLOG(jdwp) << tag_ << " object " << o << " is an invalid object";
-            error_ = JDWP::ERR_INVALID_OBJECT;
-          } else if (!SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
-                              kReferenceVReg)) {
-            VLOG(jdwp) << "failed to set " << tag_ << " object local " << reg << " = " << o;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_DOUBLE: {
-          CHECK_EQ(width_, 8U);
-          bool success = SetVRegPair(m, reg, value_, kDoubleLoVReg, kDoubleHiVReg);
-          if (!success) {
-            VLOG(jdwp) << "failed to set double local " << reg << " = " << value_;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_LONG: {
-          CHECK_EQ(width_, 8U);
-          bool success = SetVRegPair(m, reg, value_, kLongLoVReg, kLongHiVReg);
-          if (!success) {
-            VLOG(jdwp) << "failed to set double local " << reg << " = " << value_;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        default:
-          LOG(FATAL) << "Unknown tag " << tag_;
-          break;
-      }
-      return false;
+      break;
     }
+    case JDWP::JT_BYTE: {
+      CHECK_EQ(width, 1U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
+        JDWP::Set1(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get byte local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_SHORT:
+    case JDWP::JT_CHAR: {
+      CHECK_EQ(width, 2U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
+        JDWP::Set2BE(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get short/char local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_INT: {
+      CHECK_EQ(width, 4U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get int local " << reg << " = " << intVal;
+        JDWP::Set4BE(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get int local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_FLOAT: {
+      CHECK_EQ(width, 4U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kFloatVReg, &intVal)) {
+        VLOG(jdwp) << "get float local " << reg << " = " << intVal;
+        JDWP::Set4BE(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get float local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_ARRAY:
+    case JDWP::JT_CLASS_LOADER:
+    case JDWP::JT_CLASS_OBJECT:
+    case JDWP::JT_OBJECT:
+    case JDWP::JT_STRING:
+    case JDWP::JT_THREAD:
+    case JDWP::JT_THREAD_GROUP: {
+      CHECK_EQ(width, sizeof(JDWP::ObjectId));
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kReferenceVReg, &intVal)) {
+        mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
+        VLOG(jdwp) << "get " << tag << " object local " << reg << " = " << o;
+        if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+          LOG(FATAL) << "Register " << reg << " expected to hold " << tag << " object: " << o;
+        }
+        tag = TagFromObject(soa, o);
+        JDWP::SetObjectId(buf + 1, gRegistry->Add(o));
+      } else {
+        VLOG(jdwp) << "failed to get " << tag << " object local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_DOUBLE: {
+      CHECK_EQ(width, 8U);
+      uint64_t longVal;
+      if (visitor.GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
+        VLOG(jdwp) << "get double local " << reg << " = " << longVal;
+        JDWP::Set8BE(buf + 1, longVal);
+      } else {
+        VLOG(jdwp) << "failed to get double local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_LONG: {
+      CHECK_EQ(width, 8U);
+      uint64_t longVal;
+      if (visitor.GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) {
+        VLOG(jdwp) << "get long local " << reg << " = " << longVal;
+        JDWP::Set8BE(buf + 1, longVal);
+      } else {
+        VLOG(jdwp) << "failed to get long local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unknown tag " << tag;
+      break;
+  }
 
-    const JDWP::FrameId frame_id_;
-    const int slot_;
-    const JDWP::JdwpTag tag_;
-    const uint64_t value_;
-    const size_t width_;
-    JDWP::JdwpError error_;
-  };
+  // Prepend tag, which may have been updated.
+  JDWP::Set1(buf, tag);
+  return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::SetLocalValues(JDWP::Request* request) {
+  JDWP::ObjectId thread_id = request->ReadThreadId();
+  JDWP::FrameId frame_id = request->ReadFrameId();
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
-  if (error != JDWP::ERR_NONE) {
-    return error;
+  {
+    MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+    JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
   }
-  // TODO check thread is suspended by the debugger ?
+  // Find the frame with the given frame_id.
   std::unique_ptr<Context> context(Context::Create());
-  SetLocalVisitor visitor(thread, context.get(), frame_id, slot, tag, value, width);
+  FindFrameVisitor visitor(thread, context.get(), frame_id);
   visitor.WalkStack();
-  return visitor.error_;
+  if (visitor.GetError() != JDWP::ERR_NONE) {
+    return visitor.GetError();
+  }
+
+  // Writes the values into visitor's context.
+  int32_t slot_count = request->ReadSigned32("slot count");
+  for (int32_t i = 0; i < slot_count; ++i) {
+    uint32_t slot = request->ReadUnsigned32("slot");
+    JDWP::JdwpTag sigByte = request->ReadTag();
+    size_t width = Dbg::GetTagWidth(sigByte);
+    uint64_t value = request->ReadValue(width);
+
+    VLOG(jdwp) << "    --> slot " << slot << " " << sigByte << " " << value;
+    JDWP::JdwpError error = Dbg::SetLocalValue(visitor, slot, sigByte, value, width);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
+  }
+  return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag,
+                                   uint64_t value, size_t width) {
+  mirror::ArtMethod* m = visitor.GetMethod();
+  uint16_t reg = DemangleSlot(slot, m);
+  // TODO: check that the tag is compatible with the actual type of the slot!
+  // TODO: check slot is valid for this method or return INVALID_SLOT error.
+  constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
+  switch (tag) {
+    case JDWP::JT_BOOLEAN:
+    case JDWP::JT_BYTE:
+      CHECK_EQ(width, 1U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+        VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_SHORT:
+    case JDWP::JT_CHAR:
+      CHECK_EQ(width, 2U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+        VLOG(jdwp) << "failed to set short/char local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_INT:
+      CHECK_EQ(width, 4U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+        VLOG(jdwp) << "failed to set int local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_FLOAT:
+      CHECK_EQ(width, 4U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kFloatVReg)) {
+        VLOG(jdwp) << "failed to set float local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_ARRAY:
+    case JDWP::JT_CLASS_LOADER:
+    case JDWP::JT_CLASS_OBJECT:
+    case JDWP::JT_OBJECT:
+    case JDWP::JT_STRING:
+    case JDWP::JT_THREAD:
+    case JDWP::JT_THREAD_GROUP: {
+      CHECK_EQ(width, sizeof(JDWP::ObjectId));
+      mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value));
+      if (o == ObjectRegistry::kInvalidObject) {
+        VLOG(jdwp) << tag << " object " << o << " is an invalid object";
+        return JDWP::ERR_INVALID_OBJECT;
+      } else if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
+                          kReferenceVReg)) {
+        VLOG(jdwp) << "failed to set " << tag << " object local " << reg << " = " << o;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_DOUBLE: {
+      CHECK_EQ(width, 8U);
+      if (!visitor.SetVRegPair(m, reg, value, kDoubleLoVReg, kDoubleHiVReg)) {
+        VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_LONG: {
+      CHECK_EQ(width, 8U);
+      if (!visitor.SetVRegPair(m, reg, value, kLongLoVReg, kLongHiVReg)) {
+        VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unknown tag " << tag;
+      break;
+  }
+  return JDWP::ERR_NONE;
 }
 
 static void SetEventLocation(JDWP::EventLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
@@ -2972,9 +3011,11 @@
 }
 
 void Dbg::DelayFullUndeoptimization() {
-  MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
-  ++delayed_full_undeoptimization_count_;
-  DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_);
+  if (RequiresDeoptimization()) {
+    MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
+    ++delayed_full_undeoptimization_count_;
+    DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_);
+  }
 }
 
 void Dbg::ProcessDelayedFullUndeoptimizations() {
@@ -3132,20 +3173,92 @@
 }
 
 // Sanity checks all existing breakpoints on the same method.
-static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, bool need_full_deoptimization)
+static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m,
+                                           DeoptimizationRequest::Kind deoptimization_kind)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::breakpoint_lock_) {
   for (const Breakpoint& breakpoint : gBreakpoints) {
-    CHECK_EQ(need_full_deoptimization, breakpoint.NeedFullDeoptimization());
+    if (breakpoint.Method() == m) {
+      CHECK_EQ(deoptimization_kind, breakpoint.GetDeoptimizationKind());
+    }
   }
-  if (need_full_deoptimization) {
+  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) {
     // We should have deoptimized everything but not "selectively" deoptimized this method.
-    CHECK(Runtime::Current()->GetInstrumentation()->AreAllMethodsDeoptimized());
-    CHECK(!Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
-  } else {
+    CHECK(instrumentation->AreAllMethodsDeoptimized());
+    CHECK(!instrumentation->IsDeoptimized(m));
+  } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
     // We should have "selectively" deoptimized this method.
     // Note: while we have not deoptimized everything for this method, we may have done it for
     // another event.
-    CHECK(Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
+    CHECK(instrumentation->IsDeoptimized(m));
+  } else {
+    // This method does not require deoptimization.
+    CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing);
+    CHECK(!instrumentation->IsDeoptimized(m));
+  }
+}
+
+// Returns the deoptimization kind required to set a breakpoint in a method.
+// If a breakpoint has already been set, we also return the first breakpoint
+// through the given 'existing_brkpt' pointer.
+static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self,
+                                                                 mirror::ArtMethod* m,
+                                                                 const Breakpoint** existing_brkpt)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (!Dbg::RequiresDeoptimization()) {
+    // We already run in interpreter-only mode so we don't need to deoptimize anything.
+    VLOG(jdwp) << "No need for deoptimization when fully running with interpreter for method "
+               << PrettyMethod(m);
+    return DeoptimizationRequest::kNothing;
+  }
+  const Breakpoint* first_breakpoint;
+  {
+    ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
+    first_breakpoint = FindFirstBreakpointForMethod(m);
+    *existing_brkpt = first_breakpoint;
+  }
+
+  if (first_breakpoint == nullptr) {
+    // There is no breakpoint on this method yet: we need to deoptimize. If this method may be
+    // inlined, we deoptimize everything; otherwise we deoptimize only this method.
+    // Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension.
+    // Therefore we must not hold any lock when we call it.
+    bool need_full_deoptimization = IsMethodPossiblyInlined(self, m);
+    if (need_full_deoptimization) {
+      VLOG(jdwp) << "Need full deoptimization because of possible inlining of method "
+                 << PrettyMethod(m);
+      return DeoptimizationRequest::kFullDeoptimization;
+    } else {
+      // We don't need to deoptimize if the method has not been compiled.
+      ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+      const bool is_compiled = class_linker->GetOatMethodQuickCodeFor(m) != nullptr;
+      if (is_compiled) {
+        // If the method may be called through its direct code pointer (without loading
+        // its updated entrypoint), we need full deoptimization to not miss the breakpoint.
+        if (class_linker->MayBeCalledWithDirectCodePointer(m)) {
+          VLOG(jdwp) << "Need full deoptimization because of possible direct code call "
+                     << "into image for compiled method " << PrettyMethod(m);
+          return DeoptimizationRequest::kFullDeoptimization;
+        } else {
+          VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
+          return DeoptimizationRequest::kSelectiveDeoptimization;
+        }
+      } else {
+        // Method is not compiled: we don't need to deoptimize.
+        VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m);
+        return DeoptimizationRequest::kNothing;
+      }
+    }
+  } else {
+    // There is at least one breakpoint for this method: we don't need to deoptimize.
+    // Let's check that all breakpoints are configured the same way for deoptimization.
+    VLOG(jdwp) << "Breakpoint already set: no deoptimization is required";
+    DeoptimizationRequest::Kind deoptimization_kind = first_breakpoint->GetDeoptimizationKind();
+    if (kIsDebugBuild) {
+      ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
+      SanityCheckExistingBreakpoints(m, deoptimization_kind);
+    }
+    return DeoptimizationRequest::kNothing;
   }
 }
 
@@ -3156,40 +3269,29 @@
   mirror::ArtMethod* m = FromMethodId(location->method_id);
   DCHECK(m != nullptr) << "No method for method id " << location->method_id;
 
-  const Breakpoint* existing_breakpoint;
-  {
-    ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
-    existing_breakpoint = FindFirstBreakpointForMethod(m);
-  }
-  bool need_full_deoptimization;
-  if (existing_breakpoint == nullptr) {
-    // There is no breakpoint on this method yet: we need to deoptimize. If this method may be
-    // inlined, we deoptimize everything; otherwise we deoptimize only this method.
-    // Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension.
-    // Therefore we must not hold any lock when we call it.
-    need_full_deoptimization = IsMethodPossiblyInlined(self, m);
-    if (need_full_deoptimization) {
-      req->SetKind(DeoptimizationRequest::kFullDeoptimization);
-      req->SetMethod(nullptr);
-    } else {
-      req->SetKind(DeoptimizationRequest::kSelectiveDeoptimization);
-      req->SetMethod(m);
-    }
+  const Breakpoint* existing_breakpoint = nullptr;
+  const DeoptimizationRequest::Kind deoptimization_kind =
+      GetRequiredDeoptimizationKind(self, m, &existing_breakpoint);
+  req->SetKind(deoptimization_kind);
+  if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
+    req->SetMethod(m);
   } else {
-    // There is at least one breakpoint for this method: we don't need to deoptimize.
-    req->SetKind(DeoptimizationRequest::kNothing);
+    CHECK(deoptimization_kind == DeoptimizationRequest::kNothing ||
+          deoptimization_kind == DeoptimizationRequest::kFullDeoptimization);
     req->SetMethod(nullptr);
-
-    need_full_deoptimization = existing_breakpoint->NeedFullDeoptimization();
-    if (kIsDebugBuild) {
-      ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
-      SanityCheckExistingBreakpoints(m, need_full_deoptimization);
-    }
   }
 
   {
     WriterMutexLock mu(self, *Locks::breakpoint_lock_);
-    gBreakpoints.push_back(Breakpoint(m, location->dex_pc, need_full_deoptimization));
+    // If there is at least one existing breakpoint on the same method, the new breakpoint
+    // must have the same deoptimization kind than the existing breakpoint(s).
+    DeoptimizationRequest::Kind breakpoint_deoptimization_kind;
+    if (existing_breakpoint != nullptr) {
+      breakpoint_deoptimization_kind = existing_breakpoint->GetDeoptimizationKind();
+    } else {
+      breakpoint_deoptimization_kind = deoptimization_kind;
+    }
+    gBreakpoints.push_back(Breakpoint(m, location->dex_pc, breakpoint_deoptimization_kind));
     VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": "
                << gBreakpoints[gBreakpoints.size() - 1];
   }
@@ -3201,12 +3303,13 @@
   WriterMutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
   mirror::ArtMethod* m = FromMethodId(location->method_id);
   DCHECK(m != nullptr) << "No method for method id " << location->method_id;
-  bool need_full_deoptimization = false;
+  DeoptimizationRequest::Kind deoptimization_kind = DeoptimizationRequest::kNothing;
   for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) {
     if (gBreakpoints[i].DexPc() == location->dex_pc && gBreakpoints[i].Method() == m) {
       VLOG(jdwp) << "Removed breakpoint #" << i << ": " << gBreakpoints[i];
-      need_full_deoptimization = gBreakpoints[i].NeedFullDeoptimization();
-      DCHECK_NE(need_full_deoptimization, Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
+      deoptimization_kind = gBreakpoints[i].GetDeoptimizationKind();
+      DCHECK_EQ(deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization,
+                Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
       gBreakpoints.erase(gBreakpoints.begin() + i);
       break;
     }
@@ -3214,21 +3317,26 @@
   const Breakpoint* const existing_breakpoint = FindFirstBreakpointForMethod(m);
   if (existing_breakpoint == nullptr) {
     // There is no more breakpoint on this method: we need to undeoptimize.
-    if (need_full_deoptimization) {
+    if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) {
       // This method required full deoptimization: we need to undeoptimize everything.
       req->SetKind(DeoptimizationRequest::kFullUndeoptimization);
       req->SetMethod(nullptr);
-    } else {
+    } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
       // This method required selective deoptimization: we need to undeoptimize only that method.
       req->SetKind(DeoptimizationRequest::kSelectiveUndeoptimization);
       req->SetMethod(m);
+    } else {
+      // This method had no need for deoptimization: do nothing.
+      CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing);
+      req->SetKind(DeoptimizationRequest::kNothing);
+      req->SetMethod(nullptr);
     }
   } else {
     // There is at least one breakpoint for this method: we don't need to undeoptimize.
     req->SetKind(DeoptimizationRequest::kNothing);
     req->SetMethod(nullptr);
     if (kIsDebugBuild) {
-      SanityCheckExistingBreakpoints(m, need_full_deoptimization);
+      SanityCheckExistingBreakpoints(m, deoptimization_kind);
     }
   }
 }
@@ -4242,6 +4350,11 @@
       return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
     }
 
+    if (c->GetClass() == nullptr) {
+      LOG(ERROR) << "Null class of class " << c << " for object " << o;
+      return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+    }
+
     if (c->IsClassClass()) {
       return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
     }
@@ -4305,11 +4418,7 @@
 
   Thread* self = Thread::Current();
 
-  // To allow the Walk/InspectAll() below to exclusively-lock the
-  // mutator lock, temporarily release the shared access to the
-  // mutator lock here by transitioning to the suspended state.
   Locks::mutator_lock_->AssertSharedHeld(self);
-  self->TransitionFromRunnableToSuspended(kSuspended);
 
   // Send a series of heap segment chunks.
   HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
@@ -4323,32 +4432,39 @@
     gc::Heap* heap = Runtime::Current()->GetHeap();
     for (const auto& space : heap->GetContinuousSpaces()) {
       if (space->IsDlMallocSpace()) {
+        ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
         // dlmalloc's chunk header is 2 * sizeof(size_t), but if the previous chunk is in use for an
         // allocation then the first sizeof(size_t) may belong to it.
         context.SetChunkOverhead(sizeof(size_t));
         space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
       } else if (space->IsRosAllocSpace()) {
         context.SetChunkOverhead(0);
-        space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+        // Need to acquire the mutator lock before the heap bitmap lock with exclusive access since
+        // RosAlloc's internal logic doesn't know to release and reacquire the heap bitmap lock.
+        self->TransitionFromRunnableToSuspended(kSuspended);
+        ThreadList* tl = Runtime::Current()->GetThreadList();
+        tl->SuspendAll();
+        {
+          ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+          space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+        }
+        tl->ResumeAll();
+        self->TransitionFromSuspendedToRunnable();
       } else if (space->IsBumpPointerSpace()) {
+        ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
         context.SetChunkOverhead(0);
-        ReaderMutexLock mu(self, *Locks::mutator_lock_);
-        WriterMutexLock mu2(self, *Locks::heap_bitmap_lock_);
         space->AsBumpPointerSpace()->Walk(BumpPointerSpaceCallback, &context);
       } else {
         UNIMPLEMENTED(WARNING) << "Not counting objects in space " << *space;
       }
       context.ResetStartOfNextChunk();
     }
+    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     // Walk the large objects, these are not in the AllocSpace.
     context.SetChunkOverhead(0);
     heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
   }
 
-  // Shared-lock the mutator lock back.
-  self->TransitionFromSuspendedToRunnable();
-  Locks::mutator_lock_->AssertSharedHeld(self);
-
   // Finally, send a heap end chunk.
   Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
 }
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 2c81c8d..ac9616e 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -28,6 +28,7 @@
 #include <string>
 #include <vector>
 
+#include "gc_root.h"
 #include "jdwp/jdwp.h"
 #include "jni.h"
 #include "jvalue.h"
@@ -45,6 +46,7 @@
 class AllocRecord;
 class ObjectRegistry;
 class ScopedObjectAccessUnchecked;
+class StackVisitor;
 class Thread;
 class ThrowLocation;
 
@@ -86,7 +88,7 @@
   Mutex lock DEFAULT_MUTEX_ACQUIRED_AFTER;
   ConditionVariable cond GUARDED_BY(lock);
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void Clear();
@@ -121,7 +123,7 @@
   // single-step depth.
   int stack_depth;
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool ContainsDexPc(uint32_t dex_pc) const;
@@ -245,7 +247,9 @@
    */
   static int64_t LastDebuggerActivity();
 
-  static void UndoDebuggerSuspensions();
+  static void UndoDebuggerSuspensions()
+    LOCKS_EXCLUDED(Locks::thread_list_lock_,
+                   Locks::thread_suspend_count_lock_);
 
   /*
    * Class, Object, Array
@@ -458,7 +462,9 @@
   static void SuspendVM()
       LOCKS_EXCLUDED(Locks::thread_list_lock_,
                      Locks::thread_suspend_count_lock_);
-  static void ResumeVM();
+  static void ResumeVM()
+      LOCKS_EXCLUDED(Locks::thread_list_lock_,
+                     Locks::thread_suspend_count_lock_);
   static JDWP::JdwpError SuspendThread(JDWP::ObjectId thread_id, bool request_suspension = true)
       LOCKS_EXCLUDED(Locks::mutator_lock_,
                      Locks::thread_list_lock_,
@@ -475,12 +481,10 @@
       LOCKS_EXCLUDED(Locks::thread_list_lock_,
                      Locks::thread_suspend_count_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                       JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen)
+  static JDWP::JdwpError GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pReply)
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                       JDWP::JdwpTag tag, uint64_t value, size_t width)
+  static JDWP::JdwpError SetLocalValues(JDWP::Request* request)
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -519,6 +523,9 @@
       LOCKS_EXCLUDED(Locks::breakpoint_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Indicates whether we need deoptimization for debugging.
+  static bool RequiresDeoptimization();
+
   // Records deoptimization request in the queue.
   static void RequestDeoptimization(const DeoptimizationRequest& req)
       LOCKS_EXCLUDED(Locks::deoptimization_lock_)
@@ -641,6 +648,16 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
+  static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor,
+                                       ScopedObjectAccessUnchecked& soa, int slot,
+                                       JDWP::JdwpTag tag, uint8_t* buf, size_t width)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static JDWP::JdwpError SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag,
+                                       uint64_t value, size_t width)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void PostThreadStartOrStop(Thread*, uint32_t)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index c783ee4..9d835f4 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -241,6 +241,7 @@
                     location,
                     location_checksum,
                     mem_map,
+                    nullptr,
                     error_msg);
 }
 
@@ -326,9 +327,12 @@
                                    size_t size,
                                    const std::string& location,
                                    uint32_t location_checksum,
-                                   MemMap* mem_map, std::string* error_msg) {
+                                   MemMap* mem_map,
+                                   const OatFile* oat_file,
+                                   std::string* error_msg) {
   CHECK_ALIGNED(base, 4);  // various dex file structures must be word aligned
-  std::unique_ptr<DexFile> dex_file(new DexFile(base, size, location, location_checksum, mem_map));
+  std::unique_ptr<DexFile> dex_file(
+      new DexFile(base, size, location, location_checksum, mem_map, oat_file));
   if (!dex_file->Init(error_msg)) {
     return nullptr;
   } else {
@@ -339,7 +343,8 @@
 DexFile::DexFile(const byte* base, size_t size,
                  const std::string& location,
                  uint32_t location_checksum,
-                 MemMap* mem_map)
+                 MemMap* mem_map,
+                 const OatFile* oat_file)
     : begin_(base),
       size_(size),
       location_(location),
@@ -354,7 +359,8 @@
       class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
       find_class_def_misses_(0),
       class_def_index_(nullptr),
-      build_class_def_index_mutex_("DexFile index creation mutex") {
+      build_class_def_index_mutex_("DexFile index creation mutex"),
+      oat_file_(oat_file) {
   CHECK(begin_ != NULL) << GetLocation();
   CHECK_GT(size_, 0U) << GetLocation();
 }
@@ -413,11 +419,12 @@
   return atoi(version);
 }
 
-const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor) const {
+const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor, size_t hash) const {
+  DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash);
   // If we have an index lookup the descriptor via that as its constant time to search.
   Index* index = class_def_index_.LoadSequentiallyConsistent();
   if (index != nullptr) {
-    auto it = index->find(descriptor);
+    auto it = index->FindWithHash(descriptor, hash);
     return (it == index->end()) ? nullptr : it->second;
   }
   // Fast path for rate no class defs case.
@@ -449,11 +456,11 @@
     MutexLock mu(Thread::Current(), build_class_def_index_mutex_);
     // Are we the first ones building the index?
     if (class_def_index_.LoadSequentiallyConsistent() == nullptr) {
-      index = new Index(num_class_defs);
+      index = new Index;
       for (uint32_t i = 0; i < num_class_defs;  ++i) {
         const ClassDef& class_def = GetClassDef(i);
         const char* descriptor = GetClassDescriptor(class_def);
-        index->insert(std::make_pair(descriptor, &class_def));
+        index->Insert(std::make_pair(descriptor, &class_def));
       }
       class_def_index_.StoreSequentiallyConsistent(index);
     }
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 1306f11..060562b 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -22,6 +22,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "base/hash_map.h"
 #include "base/logging.h"
 #include "base/mutex.h"  // For Locks::mutator_lock_.
 #include "globals.h"
@@ -42,6 +43,7 @@
 }  // namespace mirror
 class ClassLinker;
 class MemMap;
+class OatFile;
 class Signature;
 template<class T> class Handle;
 class StringPiece;
@@ -389,8 +391,9 @@
   static const DexFile* Open(const uint8_t* base, size_t size,
                              const std::string& location,
                              uint32_t location_checksum,
+                             const OatFile* oat_file,
                              std::string* error_msg) {
-    return OpenMemory(base, size, location, location_checksum, NULL, error_msg);
+    return OpenMemory(base, size, location, location_checksum, NULL, oat_file, error_msg);
   }
 
   // Open all classesXXX.dex files from a zip archive.
@@ -648,8 +651,9 @@
     return StringByTypeIdx(class_def.class_idx_);
   }
 
-  // Looks up a class definition by its class descriptor.
-  const ClassDef* FindClassDef(const char* descriptor) const;
+  // Looks up a class definition by its class descriptor. Hash must be
+  // ComputeModifiedUtf8Hash(descriptor).
+  const ClassDef* FindClassDef(const char* descriptor, size_t hash) const;
 
   // Looks up a class definition by its type index.
   const ClassDef* FindClassDef(uint16_t type_idx) const;
@@ -887,6 +891,10 @@
   //     the dex_location where it's file name part has been made canonical.
   static std::string GetDexCanonicalLocation(const char* dex_location);
 
+  const OatFile* GetOatFile() const {
+    return oat_file_;
+  }
+
  private:
   // Opens a .dex file
   static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg);
@@ -922,12 +930,14 @@
                                    const std::string& location,
                                    uint32_t location_checksum,
                                    MemMap* mem_map,
+                                   const OatFile* oat_file,
                                    std::string* error_msg);
 
   DexFile(const byte* base, size_t size,
           const std::string& location,
           uint32_t location_checksum,
-          MemMap* mem_map);
+          MemMap* mem_map,
+          const OatFile* oat_file);
 
   // Top-level initializer that calls other Init methods.
   bool Init(std::string* error_msg);
@@ -985,19 +995,36 @@
   // Number of misses finding a class def from a descriptor.
   mutable Atomic<uint32_t> find_class_def_misses_;
 
+  struct UTF16EmptyFn {
+    void MakeEmpty(std::pair<const char*, const ClassDef*>& pair) const {
+      pair.first = nullptr;
+      pair.second = nullptr;
+    }
+    bool IsEmpty(const std::pair<const char*, const ClassDef*>& pair) const {
+      if (pair.first == nullptr) {
+        DCHECK(pair.second == nullptr);
+        return true;
+      }
+      return false;
+    }
+  };
   struct UTF16HashCmp {
     // Hash function.
     size_t operator()(const char* key) const {
-      return ComputeUtf8Hash(key);
+      return ComputeModifiedUtf8Hash(key);
     }
     // std::equal function.
     bool operator()(const char* a, const char* b) const {
       return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(a, b) == 0;
     }
   };
-  typedef std::unordered_map<const char*, const ClassDef*, UTF16HashCmp, UTF16HashCmp> Index;
+  typedef HashMap<const char*, const ClassDef*, UTF16EmptyFn, UTF16HashCmp, UTF16HashCmp> Index;
   mutable Atomic<Index*> class_def_index_;
   mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+
+  // The oat file this dex file was loaded from. May be null in case the dex file is not coming
+  // from an oat file, e.g., directly from an apk.
+  const OatFile* oat_file_;
 };
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
 
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index d0c5603..d5304e7 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -146,6 +146,9 @@
   if (!file->WriteFully(dex_bytes.get(), length)) {
     PLOG(FATAL) << "Failed to write base64 as dex file";
   }
+  if (file->FlushCloseOrErase() != 0) {
+    PLOG(FATAL) << "Could not flush and close test file.";
+  }
   file.reset();
 
   // read dex file
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index d475d42..7aff7dc 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -115,6 +115,9 @@
   if (!file->WriteFully(dex_bytes.get(), length)) {
     PLOG(FATAL) << "Failed to write base64 as dex file";
   }
+  if (file->FlushCloseOrErase() != 0) {
+    PLOG(FATAL) << "Could not flush and close test file.";
+  }
   file.reset();
 
   // read dex file
@@ -177,6 +180,9 @@
   if (!file->WriteFully(bytes, length)) {
     PLOG(FATAL) << "Failed to write base64 as dex file";
   }
+  if (file->FlushCloseOrErase() != 0) {
+    PLOG(FATAL) << "Could not flush and close test file.";
+  }
   file.reset();
 
   // read dex file
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 6ec0712..29628f1 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -432,6 +432,20 @@
     return false;
   }
 
+  // We'd also like to confirm a shstrtab in program_header_only_ mode (else Open() does this for
+  // us). This is usually the last in an oat file, and a good indicator of whether writing was
+  // successful (or the process crashed and left garbage).
+  if (program_header_only_) {
+    // It might not be mapped, but we can compare against the file size.
+    int64_t offset = static_cast<int64_t>(GetHeader().e_shoff +
+                                          (GetHeader().e_shstrndx * GetHeader().e_shentsize));
+    if (offset >= file_->GetLength()) {
+      *error_msg = StringPrintf("Shstrtab is not in the mapped ELF file: '%s'",
+                                file_->GetPath().c_str());
+      return false;
+    }
+  }
+
   return true;
 }
 
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index ccbedc0..c0b7b06 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -429,7 +429,14 @@
     case kInterface: {
       uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
       mirror::ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(imt_index);
-      if (!imt_method->IsImtConflictMethod()) {
+      if (!imt_method->IsImtConflictMethod() && !imt_method->IsImtUnimplementedMethod()) {
+        if (kIsDebugBuild) {
+          mirror::Class* klass = (*this_object)->GetClass();
+          mirror::ArtMethod* method = klass->FindVirtualMethodForInterface(resolved_method);
+          CHECK_EQ(imt_method, method) << PrettyMethod(resolved_method) << " / " <<
+              PrettyMethod(imt_method) << " / " << PrettyMethod(method) << " / " <<
+              PrettyClass(klass);
+        }
         return imt_method;
       } else {
         mirror::ArtMethod* interface_method =
@@ -529,8 +536,7 @@
                                                 mirror::Object* this_object,
                                                 mirror::ArtMethod* referrer,
                                                 bool access_check, InvokeType type) {
-  bool is_direct = type == kStatic || type == kDirect;
-  if (UNLIKELY(this_object == NULL && !is_direct)) {
+  if (UNLIKELY(this_object == NULL && type != kStatic)) {
     return NULL;
   }
   mirror::ArtMethod* resolved_method =
@@ -555,7 +561,7 @@
   }
   if (type == kInterface) {  // Most common form of slow path dispatch.
     return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
-  } else if (is_direct) {
+  } else if (type == kStatic || type == kDirect) {
     return resolved_method;
   } else if (type == kSuper) {
     return referrer->GetDeclaringClass()->GetSuperClass()
diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
index 23e1c36..46dfa3a 100644
--- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
@@ -35,7 +35,7 @@
       uint32_t dex_pc = cur_frame->GetDexPC();
       ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, method, dex_pc);
 
-      const uint8_t* gc_map = method->GetNativeGcMap();
+      const uint8_t* gc_map = method->GetNativeGcMap(sizeof(void*));
       verifier::DexPcToReferenceMap dex_gc_map(gc_map);
       const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc);
       for (size_t reg = 0; reg < num_regs; ++reg) {
diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 49df62d..771a280 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -36,7 +36,7 @@
   if (instrumentation->IsDeoptimized(method)) {
     result = GetQuickToInterpreterBridge();
   } else {
-    result = instrumentation->GetQuickCodeFor(method);
+    result = instrumentation->GetQuickCodeFor(method, sizeof(void*));
   }
   DCHECK((result != Runtime::Current()->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline())
          || !Runtime::Current()->GetHeap()->HasImageSpace());
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index dfd2e11..0651899 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -199,6 +199,22 @@
 #endif
 
  public:
+  // Special handling for proxy methods. Proxy methods are instance methods so the
+  // 'this' object is the 1st argument. They also have the same frame layout as the
+  // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the
+  // 1st GPR.
+  static mirror::Object* GetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CHECK(sp->AsMirrorPtr()->IsProxyMethod());
+    CHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize, sp->AsMirrorPtr()->GetFrameSizeInBytes());
+    CHECK_GT(kNumQuickGprArgs, 0u);
+    constexpr uint32_t kThisGprIndex = 0u;  // 'this' is in the 1st GPR.
+    size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
+        GprIndexToGprOffset(kThisGprIndex);
+    uint8_t* this_arg_address = reinterpret_cast<uint8_t*>(sp) + this_arg_offset;
+    return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address)->AsMirrorPtr();
+  }
+
   static mirror::ArtMethod* GetCallingMethod(StackReference<mirror::ArtMethod>* sp)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
@@ -410,6 +426,13 @@
   bool is_split_long_or_double_;
 };
 
+// Returns the 'this' object of a proxy method. This function is only used by StackVisitor. It
+// allows to use the QuickArgumentVisitor constants without moving all the code in its own module.
+extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  return QuickArgumentVisitor::GetProxyThisObject(sp);
+}
+
 // Visits arguments on the stack placing them into the shadow frame.
 class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor {
  public:
@@ -804,6 +827,15 @@
           caller->SetDexCacheResolvedMethod(method_index, called);
         }
       }
+    } else if (invoke_type == kStatic) {
+      const auto called_dex_method_idx = called->GetDexMethodIndex();
+      // For static invokes, we may dispatch to the static method in the superclass but resolve
+      // using the subclass. To prevent getting slow paths on each invoke, we force set the
+      // resolved method for the super class dex method index if we are in the same dex file.
+      // b/19175856
+      if (called->GetDexFile() == dex_file && dex_method_idx != called_dex_method_idx) {
+        called->GetDexCache()->SetResolvedMethod(called_dex_method_idx, called);
+      }
     }
     // Ensure that the called method's class is initialized.
     StackHandleScope<1> hs(soa.Self());
@@ -1637,7 +1669,7 @@
   *(sp32 - 1) = cookie;
 
   // Retrieve the stored native code.
-  const void* nativeCode = called->GetNativeMethod();
+  void* nativeCode = called->GetEntryPointFromJni();
 
   // There are two cases for the content of nativeCode:
   // 1) Pointer to the native function.
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 99633a3..98a7ed2 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -76,7 +76,8 @@
     const std::vector<uint8_t>& fake_mapping_data = fake_mapping_data_.GetData();
     uint32_t vmap_table_offset = sizeof(OatQuickMethodHeader) + fake_vmap_table_data.size();
     uint32_t mapping_table_offset = vmap_table_offset + fake_mapping_data.size();
-    OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset,
+    uint32_t gc_map_offset = mapping_table_offset + fake_gc_map_.size();
+    OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
                                        4 * kPointerSize, 0u, 0u, code_size);
     fake_header_code_and_maps_.resize(sizeof(method_header));
     memcpy(&fake_header_code_and_maps_[0], &method_header, sizeof(method_header));
@@ -84,23 +85,23 @@
                                       fake_vmap_table_data.begin(), fake_vmap_table_data.end());
     fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(),
                                       fake_mapping_data.begin(), fake_mapping_data.end());
+    fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(),
+                                      fake_gc_map_.begin(), fake_gc_map_.end());
     fake_header_code_and_maps_.insert(fake_header_code_and_maps_.end(),
                                       fake_code_.begin(), fake_code_.end());
 
     // NOTE: Don't align the code (it will not be executed) but check that the Thumb2
     // adjustment will be a NOP, see ArtMethod::EntryPointToCodePointer().
     CHECK_EQ(mapping_table_offset & 1u, 0u);
-    const uint8_t* code_ptr = &fake_header_code_and_maps_[mapping_table_offset];
+    const uint8_t* code_ptr = &fake_header_code_and_maps_[gc_map_offset];
 
     method_f_ = my_klass_->FindVirtualMethod("f", "()I");
     ASSERT_TRUE(method_f_ != NULL);
     method_f_->SetEntryPointFromQuickCompiledCode(code_ptr);
-    method_f_->SetNativeGcMap(&fake_gc_map_[0]);
 
     method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
     ASSERT_TRUE(method_g_ != NULL);
     method_g_->SetEntryPointFromQuickCompiledCode(code_ptr);
-    method_g_->SetNativeGcMap(&fake_gc_map_[0]);
   }
 
   const DexFile* dex_;
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 56d709a..63eef51 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -268,7 +268,8 @@
   // at the return PC address.
   if (true || kIsDebugBuild) {
     VLOG(signals) << "looking for dex pc for return pc " << std::hex << return_pc;
-    const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj);
+    const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj,
+                                                                                 sizeof(void*));
     uint32_t sought_offset = return_pc - reinterpret_cast<uintptr_t>(code);
     VLOG(signals) << "pc offset: " << std::hex << sought_offset;
   }
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index fbeea85..6d44d89 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -54,9 +54,8 @@
   static CardTable* Create(const byte* heap_begin, size_t heap_capacity);
 
   // Set the card associated with the given address to GC_CARD_DIRTY.
-  void MarkCard(const void *addr) {
-    byte* card_addr = CardFromAddr(addr);
-    *card_addr = kCardDirty;
+  ALWAYS_INLINE void MarkCard(const void *addr) {
+    *CardFromAddr(addr) = kCardDirty;
   }
 
   // Is the object on a dirty card?
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index ad22a2e..7e88c41 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1772,7 +1772,7 @@
     if (i < 4) {
       numOfPages[i] = 1;
     } else if (i < 8) {
-      numOfPages[i] = 2;
+      numOfPages[i] = 1;
     } else if (i < 16) {
       numOfPages[i] = 4;
     } else if (i < 32) {
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index b2a5a3c..5daac92 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -403,7 +403,7 @@
 
   // We use thread-local runs for the size Brackets whose indexes
   // are less than this index. We use shared (current) runs for the rest.
-  static const size_t kNumThreadLocalSizeBrackets = 11;
+  static const size_t kNumThreadLocalSizeBrackets = 8;
 
  private:
   // The base address of the memory region that's managed by this allocator.
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 4044852..c63fec4 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -300,22 +300,20 @@
 }
 
 void MarkCompact::MarkHeapReferenceCallback(mirror::HeapReference<mirror::Object>* obj_ptr,
-                                          void* arg) {
+                                            void* arg) {
   reinterpret_cast<MarkCompact*>(arg)->MarkObject(obj_ptr->AsMirrorPtr());
 }
 
 void MarkCompact::DelayReferenceReferentCallback(mirror::Class* klass, mirror::Reference* ref,
-                                               void* arg) {
+                                                 void* arg) {
   reinterpret_cast<MarkCompact*>(arg)->DelayReferenceReferent(klass, ref);
 }
 
-void MarkCompact::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                   RootType /*root_type*/) {
+void MarkCompact::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkCompact*>(arg)->MarkObject(*root);
 }
 
-void MarkCompact::UpdateRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                     RootType /*root_type*/) {
+void MarkCompact::UpdateRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   mirror::Object* obj = *root;
   mirror::Object* new_obj = reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj);
   if (obj != new_obj) {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index bb85fa0..cbdd19e 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "lock_word.h"
@@ -113,8 +114,7 @@
   void SweepSystemWeaks()
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
-                               RootType /*root_type*/)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
@@ -180,8 +180,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
   // Update the references of objects by using the forwarding addresses.
   void UpdateReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
-  static void UpdateRootCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/)
+  static void UpdateRootCallback(mirror::Object** root, void* arg, const RootInfo& /*root_info*/)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
   // Move objects and restore lock words.
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 95530be..07f04cb 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -456,42 +456,35 @@
   }
 }
 
-void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                         RootType /*root_type*/) {
+void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root);
 }
 
-void MarkSweep::VerifyRootMarked(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void MarkSweep::VerifyRootMarked(Object** root, void* arg, const RootInfo& /*root_info*/) {
   CHECK(reinterpret_cast<MarkSweep*>(arg)->IsMarked(*root));
 }
 
-void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void MarkSweep::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root);
 }
 
-void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
-                                   const StackVisitor* visitor, RootType root_type) {
-  reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(root, vreg, visitor, root_type);
+void MarkSweep::VerifyRootCallback(Object** root, void* arg, const RootInfo& root_info) {
+  reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(*root, root_info);
 }
 
-void MarkSweep::VerifyRoot(const Object* root, size_t vreg, const StackVisitor* visitor,
-                           RootType root_type) {
+void MarkSweep::VerifyRoot(const Object* root, const RootInfo& root_info) {
   // See if the root is on any space bitmap.
   if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
     space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
     if (!large_object_space->Contains(root)) {
-      LOG(ERROR) << "Found invalid root: " << root << " with type " << root_type;
-      if (visitor != NULL) {
-        LOG(ERROR) << visitor->DescribeLocation() << " in VReg: " << vreg;
-      }
+      LOG(ERROR) << "Found invalid root: " << root << " ";
+      root_info.Describe(LOG(ERROR));
     }
   }
 }
 
 void MarkSweep::VerifyRoots() {
-  Runtime::Current()->GetThreadList()->VerifyRoots(VerifyRootCallback, this);
+  Runtime::Current()->GetThreadList()->VisitRoots(VerifyRootCallback, this);
 }
 
 void MarkSweep::MarkRoots(Thread* self) {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 2780099..d101456 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "object_callbacks.h"
@@ -182,13 +183,11 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                               RootType root_type)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  static void VerifyRootMarked(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                               RootType /*root_type*/)
+  static void VerifyRootMarked(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
@@ -196,8 +195,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                                       RootType root_type)
+  static void MarkRootParallelCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Marks an object.
@@ -247,11 +245,9 @@
   // whether or not we care about pauses.
   size_t GetThreadCount(bool paused) const;
 
-  static void VerifyRootCallback(const mirror::Object* root, void* arg, size_t vreg,
-                                 const StackVisitor *visitor, RootType root_type);
+  static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info);
 
-  void VerifyRoot(const mirror::Object* root, size_t vreg, const StackVisitor* visitor,
-                  RootType root_type) NO_THREAD_SAFETY_ANALYSIS;
+  void VerifyRoot(const mirror::Object* root, const RootInfo& root_info) NO_THREAD_SAFETY_ANALYSIS;
 
   // Push a single reference on a mark stack.
   void PushOnMarkStack(mirror::Object* obj);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 8fb33ce..144e6a8 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -590,8 +590,7 @@
   reinterpret_cast<SemiSpace*>(arg)->DelayReferenceReferent(klass, ref);
 }
 
-void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void SemiSpace::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   auto ref = StackReference<mirror::Object>::FromMirrorPtr(*root);
   reinterpret_cast<SemiSpace*>(arg)->MarkObject(&ref);
   if (*root != ref.AsMirrorPtr()) {
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 71a83f2..17da367 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -23,6 +23,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "mirror/object_reference.h"
@@ -132,8 +133,7 @@
   void SweepSystemWeaks()
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
-                               RootType /*root_type*/)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index d1fb600..15d55d0 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -48,11 +48,20 @@
   }
   // Need to check that we arent the large object allocator since the large object allocation code
   // path this function. If we didn't check we would have an infinite loop.
-  if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
-    return AllocLargeObject<kInstrumented, PreFenceVisitor>(self, klass, byte_count,
-                                                            pre_fence_visitor);
-  }
   mirror::Object* obj;
+  if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
+    obj = AllocLargeObject<kInstrumented, PreFenceVisitor>(self, &klass, byte_count,
+                                                           pre_fence_visitor);
+    if (obj != nullptr) {
+      return obj;
+    } else {
+      // There should be an OOM exception, since we are retrying, clear it.
+      self->ClearException();
+    }
+    // If the large object allocation failed, try to use the normal spaces (main space,
+    // non moving space). This can happen if there is significant virtual address space
+    // fragmentation.
+  }
   AllocationTimer alloc_timer(this, &obj);
   size_t bytes_allocated;
   size_t usable_size;
@@ -171,10 +180,13 @@
 }
 
 template <bool kInstrumented, typename PreFenceVisitor>
-inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class* klass,
+inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class** klass,
                                               size_t byte_count,
                                               const PreFenceVisitor& pre_fence_visitor) {
-  return AllocObjectWithAllocator<kInstrumented, false, PreFenceVisitor>(self, klass, byte_count,
+  // Save and restore the class in case it moves.
+  StackHandleScope<1> hs(self);
+  auto klass_wrapper = hs.NewHandleWrapper(klass);
+  return AllocObjectWithAllocator<kInstrumented, false, PreFenceVisitor>(self, *klass, byte_count,
                                                                          kAllocatorTypeLOS,
                                                                          pre_fence_visitor);
 }
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 6645303..bad8de3 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -53,6 +53,7 @@
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "heap-inl.h"
 #include "image.h"
+#include "intern_table.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object.h"
@@ -74,8 +75,6 @@
 
 static constexpr size_t kCollectorTransitionStressIterations = 0;
 static constexpr size_t kCollectorTransitionStressWait = 10 * 1000;  // Microseconds
-static constexpr bool kGCALotMode = false;
-static constexpr size_t kGcAlotInterval = KB;
 // Minimum amount of remaining bytes before a concurrent GC is triggered.
 static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB;
 static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB;
@@ -101,7 +100,18 @@
 static const char* kDlMallocSpaceName[2] = {"main dlmalloc space", "main dlmalloc space 1"};
 static const char* kRosAllocSpaceName[2] = {"main rosalloc space", "main rosalloc space 1"};
 static const char* kMemMapSpaceName[2] = {"main space", "main space 1"};
+static const char* kNonMovingSpaceName = "non moving space";
+static const char* kZygoteSpaceName = "zygote space";
 static constexpr size_t kGSSBumpPointerSpaceCapacity = 32 * MB;
+static constexpr bool kGCALotMode = false;
+// GC alot mode uses a small allocation stack to stress test a lot of GC.
+static constexpr size_t kGcAlotAllocationStackSize = 4 * KB /
+    sizeof(mirror::HeapReference<mirror::Object>);
+// Verify objet has a small allocation stack size since searching the allocation stack is slow.
+static constexpr size_t kVerifyObjectAllocationStackSize = 16 * KB /
+    sizeof(mirror::HeapReference<mirror::Object>);
+static constexpr size_t kDefaultAllocationStackSize = 8 * MB /
+    sizeof(mirror::HeapReference<mirror::Object>);
 
 Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
            double target_utilization, double foreground_heap_growth_multiplier,
@@ -167,8 +177,9 @@
        * verification is enabled, we limit the size of allocation stacks to speed up their
        * searching.
        */
-      max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval
-          : (kVerifyObjectSupport > kVerifyObjectModeFast) ? KB : MB),
+      max_allocation_stack_size_(kGCALotMode ? kGcAlotAllocationStackSize
+          : (kVerifyObjectSupport > kVerifyObjectModeFast) ? kVerifyObjectAllocationStackSize :
+          kDefaultAllocationStackSize),
       current_allocator_(kAllocatorTypeDlMalloc),
       current_non_moving_allocator_(kAllocatorTypeNonMoving),
       bump_pointer_space_(nullptr),
@@ -237,9 +248,13 @@
                                      +-main alloc space2 / bump space 2 (capacity_)+-
                                      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
   */
+  // We don't have hspace compaction enabled with GSS.
+  if (foreground_collector_type_ == kCollectorTypeGSS) {
+    use_homogeneous_space_compaction_for_oom_ = false;
+  }
   bool support_homogeneous_space_compaction =
       background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact ||
-      use_homogeneous_space_compaction_for_oom;
+      use_homogeneous_space_compaction_for_oom_;
   // We may use the same space the main space for the non moving space if we don't need to compact
   // from the main space.
   // This is not the case if we support homogeneous compaction or have a moving background
@@ -259,10 +274,14 @@
   std::string error_str;
   std::unique_ptr<MemMap> non_moving_space_mem_map;
   if (separate_non_moving_space) {
+    // If we are the zygote, the non moving space becomes the zygote space when we run
+    // PreZygoteFork the first time. In this case, call the map "zygote space" since we can't
+    // rename the mem map later.
+    const char* space_name = is_zygote ? kZygoteSpaceName: kNonMovingSpaceName;
     // Reserve the non moving mem map before the other two since it needs to be at a specific
     // address.
     non_moving_space_mem_map.reset(
-        MemMap::MapAnonymous("non moving space", requested_alloc_space_begin,
+        MemMap::MapAnonymous(space_name, requested_alloc_space_begin,
                              non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str));
     CHECK(non_moving_space_mem_map != nullptr) << error_str;
     // Try to reserve virtual memory at a lower address if we have a separate non moving space.
@@ -353,6 +372,7 @@
   byte* heap_end = continuous_spaces_.back()->Limit();
   size_t heap_capacity = heap_end - heap_begin;
   // Remove the main backup space since it slows down the GC to have unused extra spaces.
+  // TODO: Avoid needing to do this.
   if (main_space_backup_.get() != nullptr) {
     RemoveSpace(main_space_backup_.get());
   }
@@ -980,6 +1000,22 @@
   Trim();
 }
 
+class TrimIndirectReferenceTableClosure : public Closure {
+ public:
+  explicit TrimIndirectReferenceTableClosure(Barrier* barrier) : barrier_(barrier) {
+  }
+  virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
+    ATRACE_BEGIN("Trimming reference table");
+    thread->GetJniEnv()->locals.Trim();
+    ATRACE_END();
+    barrier_->Pass(Thread::Current());
+  }
+
+ private:
+  Barrier* const barrier_;
+};
+
+
 void Heap::Trim() {
   Thread* self = Thread::Current();
   {
@@ -1001,6 +1037,22 @@
     WaitForGcToCompleteLocked(kGcCauseTrim, self);
     collector_type_running_ = kCollectorTypeHeapTrim;
   }
+  // Trim reference tables.
+  {
+    ScopedObjectAccess soa(self);
+    JavaVMExt* vm = soa.Vm();
+    // Trim globals indirect reference table.
+    {
+      WriterMutexLock mu(self, vm->globals_lock);
+      vm->globals.Trim();
+    }
+    // Trim locals indirect reference tables.
+    Barrier barrier(0);
+    TrimIndirectReferenceTableClosure closure(&barrier);
+    ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+    size_t barrier_count = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
+    barrier.Increment(self, barrier_count);
+  }
   uint64_t start_ns = NanoTime();
   // Trim the managed spaces.
   uint64_t total_alloc_space_allocated = 0;
@@ -1574,6 +1626,8 @@
   to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
   const uint64_t space_size_before_compaction = from_space->Size();
   AddSpace(to_space);
+  // Make sure that we will have enough room to copy.
+  CHECK_GE(to_space->GetFootprintLimit(), from_space->GetFootprintLimit());
   Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact);
   // Leave as prot read so that we can still run ROSAlloc verification on this space.
   from_space->GetMemMap()->Protect(PROT_READ);
@@ -1692,8 +1746,8 @@
         RemoveSpace(temp_space_);
         temp_space_ = nullptr;
         mem_map->Protect(PROT_READ | PROT_WRITE);
-        CreateMainMallocSpace(mem_map.get(), kDefaultInitialSize, mem_map->Size(),
-                              mem_map->Size());
+        CreateMainMallocSpace(mem_map.get(), kDefaultInitialSize,
+                              std::min(mem_map->Size(), growth_limit_), mem_map->Size());
         mem_map.release();
         // Compact to the main space from the bump pointer space, don't need to swap semispaces.
         AddSpace(main_space_);
@@ -1706,9 +1760,9 @@
         if (kIsDebugBuild && kUseRosAlloc) {
           mem_map->Protect(PROT_READ | PROT_WRITE);
         }
-        main_space_backup_.reset(CreateMallocSpaceFromMemMap(mem_map.get(), kDefaultInitialSize,
-                                                             mem_map->Size(), mem_map->Size(),
-                                                             name, true));
+        main_space_backup_.reset(CreateMallocSpaceFromMemMap(
+            mem_map.get(), kDefaultInitialSize, std::min(mem_map->Size(), growth_limit_),
+            mem_map->Size(), name, true));
         if (kIsDebugBuild && kUseRosAlloc) {
           mem_map->Protect(PROT_NONE);
         }
@@ -1908,6 +1962,8 @@
   if (have_zygote_space_) {
     return;
   }
+  Runtime::Current()->GetInternTable()->SwapPostZygoteWithPreZygote();
+  Runtime::Current()->GetClassLinker()->MoveClassTableToPreZygote();
   VLOG(heap) << "Starting PreZygoteFork";
   // Trim the pages at the end of the non moving space.
   non_moving_space_->Trim();
@@ -1945,7 +2001,8 @@
       MemMap* mem_map = main_space_->ReleaseMemMap();
       RemoveSpace(main_space_);
       space::Space* old_main_space = main_space_;
-      CreateMainMallocSpace(mem_map, kDefaultInitialSize, mem_map->Size(), mem_map->Size());
+      CreateMainMallocSpace(mem_map, kDefaultInitialSize, std::min(mem_map->Size(), growth_limit_),
+                            mem_map->Size());
       delete old_main_space;
       AddSpace(main_space_);
     } else {
@@ -1980,7 +2037,8 @@
     // from this point on.
     RemoveRememberedSet(old_alloc_space);
   }
-  space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space",
+  // Remaining space becomes the new non moving space.
+  space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace(kNonMovingSpaceName,
                                                                         low_memory_mode_,
                                                                         &non_moving_space_);
   CHECK(!non_moving_space_->CanMoveObjects());
@@ -2219,8 +2277,8 @@
   gc_complete_cond_->Broadcast(self);
 }
 
-static void RootMatchesObjectVisitor(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                     RootType /*root_type*/) {
+static void RootMatchesObjectVisitor(mirror::Object** root, void* arg,
+                                     const RootInfo& /*root_info*/) {
   mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg);
   if (*root == obj) {
     LOG(INFO) << "Object " << obj << " is a root";
@@ -2261,12 +2319,12 @@
     return heap_->IsLiveObjectLocked(obj, true, false, true);
   }
 
-  static void VerifyRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                                 RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
     if (!visitor->VerifyReference(nullptr, *root, MemberOffset(0))) {
       LOG(ERROR) << "Root " << *root << " is dead with type " << PrettyTypeOf(*root)
-          << " thread_id= " << thread_id << " root_type= " << root_type;
+          << " thread_id= " << root_info.GetThreadId() << " root_type= " << root_info.GetType();
     }
   }
 
@@ -2941,7 +2999,18 @@
 
 void Heap::ClearGrowthLimit() {
   growth_limit_ = capacity_;
-  non_moving_space_->ClearGrowthLimit();
+  for (const auto& space : continuous_spaces_) {
+    if (space->IsMallocSpace()) {
+      gc::space::MallocSpace* malloc_space = space->AsMallocSpace();
+      malloc_space->ClearGrowthLimit();
+      malloc_space->SetFootprintLimit(malloc_space->Capacity());
+    }
+  }
+  // This space isn't added for performance reasons.
+  if (main_space_backup_.get() != nullptr) {
+    main_space_backup_->ClearGrowthLimit();
+    main_space_backup_->SetFootprintLimit(main_space_backup_->Capacity());
+  }
 }
 
 void Heap::AddFinalizerReference(Thread* self, mirror::Object** object) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index cf297bd..530ec18 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -372,18 +372,18 @@
 
   // Must be called if a field of an Object in the heap changes, and before any GC safe-point.
   // The call is not needed if NULL is stored in the field.
-  void WriteBarrierField(const mirror::Object* dst, MemberOffset /*offset*/,
-                         const mirror::Object* /*new_value*/) {
+  ALWAYS_INLINE void WriteBarrierField(const mirror::Object* dst, MemberOffset /*offset*/,
+                                       const mirror::Object* /*new_value*/) {
     card_table_->MarkCard(dst);
   }
 
   // Write barrier for array operations that update many field positions
-  void WriteBarrierArray(const mirror::Object* dst, int /*start_offset*/,
-                         size_t /*length TODO: element_count or byte_count?*/) {
+  ALWAYS_INLINE void WriteBarrierArray(const mirror::Object* dst, int /*start_offset*/,
+                                       size_t /*length TODO: element_count or byte_count?*/) {
     card_table_->MarkCard(dst);
   }
 
-  void WriteBarrierEveryFieldOf(const mirror::Object* obj) {
+  ALWAYS_INLINE void WriteBarrierEveryFieldOf(const mirror::Object* obj) {
     card_table_->MarkCard(obj);
   }
 
@@ -643,7 +643,7 @@
 
   // We don't force this to be inlined since it is a slow path.
   template <bool kInstrumented, typename PreFenceVisitor>
-  mirror::Object* AllocLargeObject(Thread* self, mirror::Class* klass, size_t byte_count,
+  mirror::Object* AllocLargeObject(Thread* self, mirror::Class** klass, size_t byte_count,
                                    const PreFenceVisitor& pre_fence_visitor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 525d437..f765f0e 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -17,6 +17,7 @@
 #include "image_space.h"
 
 #include <dirent.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
 
 #include <random>
@@ -72,17 +73,17 @@
 // out-of-date. We also don't really care if this fails since it is just a convenience.
 // Adapted from prune_dex_cache(const char* subdir) in frameworks/native/cmds/installd/commands.c
 // Note this should only be used during first boot.
-static void RealPruneDexCache(const std::string& cache_dir_path);
+static void RealPruneDalvikCache(const std::string& cache_dir_path);
 
-static void PruneDexCache(InstructionSet isa) {
+static void PruneDalvikCache(InstructionSet isa) {
   CHECK_NE(isa, kNone);
   // Prune the base /data/dalvik-cache.
-  RealPruneDexCache(GetDalvikCacheOrDie(".", false));
+  RealPruneDalvikCache(GetDalvikCacheOrDie(".", false));
   // Prune /data/dalvik-cache/<isa>.
-  RealPruneDexCache(GetDalvikCacheOrDie(GetInstructionSetString(isa), false));
+  RealPruneDalvikCache(GetDalvikCacheOrDie(GetInstructionSetString(isa), false));
 }
 
-static void RealPruneDexCache(const std::string& cache_dir_path) {
+static void RealPruneDalvikCache(const std::string& cache_dir_path) {
   if (!OS::DirectoryExists(cache_dir_path.c_str())) {
     return;
   }
@@ -116,6 +117,28 @@
   CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(cache_dir))) << "Unable to close directory.";
 }
 
+// We write out an empty file to the zygote's ISA specific cache dir at the start of
+// every zygote boot and delete it when the boot completes. If we find a file already
+// present, it usually means the boot didn't complete. We wipe the entire dalvik
+// cache if that's the case.
+static void MarkZygoteStart(const InstructionSet isa) {
+  const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
+  const std::string boot_marker = isa_subdir + "/.booting";
+
+  if (OS::FileExists(boot_marker.c_str())) {
+    LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
+    RealPruneDalvikCache(isa_subdir);
+  }
+
+  VLOG(startup) << "Creating boot start marker: " << boot_marker;
+  std::unique_ptr<File> f(OS::CreateEmptyFile(boot_marker.c_str()));
+  if (f.get() != nullptr) {
+    if (f->FlushCloseOrErase() != 0) {
+      PLOG(WARNING) << "Failed to write boot marker.";
+    }
+  }
+}
+
 static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa,
                           std::string* error_msg) {
   const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
@@ -128,7 +151,7 @@
   // We should clean up so we are more likely to have room for the image.
   if (Runtime::Current()->IsZygote()) {
     LOG(INFO) << "Pruning dalvik-cache since we are generating an image and will need to recompile";
-    PruneDexCache(image_isa);
+    PruneDalvikCache(image_isa);
   }
 
   std::vector<std::string> arg_vector;
@@ -230,7 +253,7 @@
   // We should clean up so we are more likely to have room for the image.
   if (Runtime::Current()->IsZygote()) {
     LOG(INFO) << "Pruning dalvik-cache since we are relocating an image and will need to recompile";
-    PruneDexCache(isa);
+    PruneDalvikCache(isa);
   }
 
   std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
@@ -377,6 +400,41 @@
   return false;
 }
 
+static constexpr uint64_t kLowSpaceValue = 50 * MB;
+static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
+
+// Read the free space of the cache partition and make a decision whether to keep the generated
+// image. This is to try to mitigate situations where the system might run out of space later.
+static bool CheckSpace(const std::string& cache_filename, std::string* error_msg) {
+  // Using statvfs vs statvfs64 because of b/18207376, and it is enough for all practical purposes.
+  struct statvfs buf;
+
+  int res = TEMP_FAILURE_RETRY(statvfs(cache_filename.c_str(), &buf));
+  if (res != 0) {
+    // Could not stat. Conservatively tell the system to delete the image.
+    *error_msg = "Could not stat the filesystem, assuming low-memory situation.";
+    return false;
+  }
+
+  uint64_t fs_overall_size = buf.f_bsize * static_cast<uint64_t>(buf.f_blocks);
+  // Zygote is privileged, but other things are not. Use bavail.
+  uint64_t fs_free_size = buf.f_bsize * static_cast<uint64_t>(buf.f_bavail);
+
+  // Take the overall size as an indicator for a tmpfs, which is being used for the decryption
+  // environment. We do not want to fail quickening the boot image there, as it is beneficial
+  // for time-to-UI.
+  if (fs_overall_size > kTmpFsSentinelValue) {
+    if (fs_free_size < kLowSpaceValue) {
+      *error_msg = StringPrintf("Low-memory situation: only %4.2f megabytes available after image"
+                                " generation, need at least %" PRIu64 ".",
+                                static_cast<double>(fs_free_size) / MB,
+                                kLowSpaceValue / MB);
+      return false;
+    }
+  }
+  return true;
+}
+
 ImageSpace* ImageSpace::Create(const char* image_location,
                                const InstructionSet image_isa,
                                std::string* error_msg) {
@@ -390,6 +448,10 @@
                                              &has_system, &cache_filename, &dalvik_cache_exists,
                                              &has_cache, &is_global_cache);
 
+  if (Runtime::Current()->IsZygote()) {
+    MarkZygoteStart(image_isa);
+  }
+
   ImageSpace* space;
   bool relocate = Runtime::Current()->ShouldRelocate();
   bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();
@@ -438,7 +500,7 @@
             // Since ImageCreationAllowed was true above, we are the zygote
             // and therefore the only process expected to generate these for
             // the device.
-            PruneDexCache(image_isa);
+            PruneDalvikCache(image_isa);
             return nullptr;
           }
         }
@@ -493,7 +555,7 @@
                                 "but image failed to load: %s",
                                 image_location, cache_filename.c_str(), system_filename.c_str(),
                                 error_msg->c_str());
-      PruneDexCache(image_isa);
+      PruneDalvikCache(image_isa);
       return nullptr;
     } else if (is_system) {
       // If the /system file exists, it should be up-to-date, don't try to generate it.
@@ -521,9 +583,16 @@
     // Since ImageCreationAllowed was true above, we are the zygote
     // and therefore the only process expected to generate these for
     // the device.
-    PruneDexCache(image_isa);
+    PruneDalvikCache(image_isa);
     return nullptr;
   } else {
+    // Check whether there is enough space left over after we have generated the image.
+    if (!CheckSpace(cache_filename, error_msg)) {
+      // No. Delete the generated image and try to run out of the dex files.
+      PruneDalvikCache(image_isa);
+      return nullptr;
+    }
+
     // Note that we must not use the file descriptor associated with
     // ScopedFlock::GetFile to Init the image file. We want the file
     // descriptor (and the associated exclusive lock) to be released when
@@ -643,6 +712,9 @@
   runtime->SetResolutionMethod(down_cast<mirror::ArtMethod*>(resolution_method));
   mirror::Object* imt_conflict_method = image_header.GetImageRoot(ImageHeader::kImtConflictMethod);
   runtime->SetImtConflictMethod(down_cast<mirror::ArtMethod*>(imt_conflict_method));
+  mirror::Object* imt_unimplemented_method =
+      image_header.GetImageRoot(ImageHeader::kImtUnimplementedMethod);
+  runtime->SetImtUnimplementedMethod(down_cast<mirror::ArtMethod*>(imt_unimplemented_method));
   mirror::Object* default_imt = image_header.GetImageRoot(ImageHeader::kDefaultImt);
   runtime->SetDefaultImt(down_cast<mirror::ObjectArray<mirror::ArtMethod>*>(default_imt));
 
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 92c6f53..518e8c0 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -351,8 +351,8 @@
   mark_bitmap_->Clear();
   SetEnd(begin_ + starting_size_);
   delete rosalloc_;
-  rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_, Capacity(),
-                             low_memory_mode_);
+  rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_,
+                             NonGrowthLimitCapacity(), low_memory_mode_);
   SetFootprintLimit(footprint_limit);
 }
 
diff --git a/runtime/gc_root-inl.h b/runtime/gc_root-inl.h
index 2661e54..a42ec08 100644
--- a/runtime/gc_root-inl.h
+++ b/runtime/gc_root-inl.h
@@ -25,14 +25,9 @@
 
 template<class MirrorType>
 template<ReadBarrierOption kReadBarrierOption>
-inline MirrorType* GcRoot<MirrorType>::Read() {
+inline MirrorType* GcRoot<MirrorType>::Read() const {
   return ReadBarrier::BarrierForRoot<MirrorType, kReadBarrierOption>(&root_);
 }
 
-template<class MirrorType>
-inline void GcRoot<MirrorType>::Assign(MirrorType* value) {
-  root_ = value;
-}
-
 }  // namespace art
 #endif  // ART_RUNTIME_GC_ROOT_INL_H_
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 3928f5d..7e0be64 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -19,19 +19,73 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"       // For Locks::mutator_lock_.
-#include "object_callbacks.h"
 
 namespace art {
 
+namespace mirror {
+class Object;
+}  // namespace mirror
+
+enum RootType {
+  kRootUnknown = 0,
+  kRootJNIGlobal,
+  kRootJNILocal,
+  kRootJavaFrame,
+  kRootNativeStack,
+  kRootStickyClass,
+  kRootThreadBlock,
+  kRootMonitorUsed,
+  kRootThreadObject,
+  kRootInternedString,
+  kRootDebugger,
+  kRootVMInternal,
+  kRootJNIMonitor,
+};
+std::ostream& operator<<(std::ostream& os, const RootType& root_type);
+
+class RootInfo {
+ public:
+  // Thread id 0 is for non thread roots.
+  explicit RootInfo(RootType type, uint32_t thread_id = 0)
+     : type_(type), thread_id_(thread_id) {
+  }
+  virtual ~RootInfo() {
+  }
+  RootType GetType() const {
+    return type_;
+  }
+  uint32_t GetThreadId() const {
+    return thread_id_;
+  }
+  virtual void Describe(std::ostream& os) const {
+    os << "Type=" << type_ << " thread_id=" << thread_id_;
+  }
+
+ private:
+  const RootType type_;
+  const uint32_t thread_id_;
+};
+
+// Returns the new address of the object, returns root if it has not moved. tid and root_type are
+// only used by hprof.
+typedef void (RootCallback)(mirror::Object** root, void* arg, const RootInfo& root_info);
+
 template<class MirrorType>
 class PACKED(4) GcRoot {
  public:
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-  ALWAYS_INLINE MirrorType* Read() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ALWAYS_INLINE void Assign(MirrorType* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE MirrorType* Read() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) {
-    callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
+  void VisitRoot(RootCallback* callback, void* arg, const RootInfo& info) const {
+    DCHECK(!IsNull());
+    callback(reinterpret_cast<mirror::Object**>(&root_), arg, info);
+    DCHECK(!IsNull());
+  }
+
+  void VisitRootIfNonNull(RootCallback* callback, void* arg, const RootInfo& info) const {
+    if (!IsNull()) {
+      VisitRoot(callback, arg, info);
+    }
   }
 
   // This is only used by IrtIterator.
@@ -52,7 +106,7 @@
   }
 
  private:
-  MirrorType* root_;
+  mutable MirrorType* root_;
 };
 
 }  // namespace art
diff --git a/runtime/handle.h b/runtime/handle.h
index f70faf4..f9864dc 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -58,7 +58,7 @@
   }
 
   ALWAYS_INLINE T* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return reference_->AsMirrorPtr();
+    return down_cast<T*>(reference_->AsMirrorPtr());
   }
 
   ALWAYS_INLINE jobject ToJObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -70,25 +70,25 @@
   }
 
  protected:
-  StackReference<T>* reference_;
-
   template<typename S>
   explicit ConstHandle(StackReference<S>* reference)
-      : reference_(reinterpret_cast<StackReference<T>*>(reference)) {
+      : reference_(reference) {
   }
   template<typename S>
   explicit ConstHandle(const ConstHandle<S>& handle)
-      : reference_(reinterpret_cast<StackReference<T>*>(handle.reference_)) {
+      : reference_(handle.reference_) {
   }
 
-  StackReference<T>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+  StackReference<mirror::Object>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
     return reference_;
   }
-  ALWAYS_INLINE const StackReference<T>* GetReference() const
+  ALWAYS_INLINE const StackReference<mirror::Object>* GetReference() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return reference_;
   }
 
+  StackReference<mirror::Object>* reference_;
+
  private:
   friend class BuildGenericJniFrameVisitor;
   template<class S> friend class ConstHandle;
@@ -120,8 +120,8 @@
   }
 
   ALWAYS_INLINE T* Assign(T* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    StackReference<T>* ref = ConstHandle<T>::GetReference();
-    T* const old = ref->AsMirrorPtr();
+    StackReference<mirror::Object>* ref = Handle<T>::GetReference();
+    T* old = down_cast<T*>(ref->AsMirrorPtr());
     ref->Assign(reference);
     return old;
   }
@@ -131,7 +131,6 @@
       : ConstHandle<T>(handle) {
   }
 
- protected:
   template<typename S>
   explicit Handle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : ConstHandle<T>(reference) {
@@ -152,7 +151,7 @@
   }
 
  private:
-  StackReference<T> null_ref_;
+  StackReference<mirror::Object> null_ref_;
 };
 
 }  // namespace art
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index 7bc811d..da28ed7 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -25,12 +25,12 @@
 namespace art {
 
 template<size_t kNumReferences>
-inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self)
+inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
     : HandleScope(kNumReferences), self_(self), pos_(0) {
   // TODO: Figure out how to use a compile assert.
   DCHECK_EQ(&references_[0], &references_storage_[0]);
   for (size_t i = 0; i < kNumReferences; ++i) {
-    SetReference(i, nullptr);
+    SetReference(i, fill_value);
   }
   self_->PushHandleScope(this);
 }
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 42ef779..5050872 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -120,6 +120,12 @@
   }
 
  protected:
+  // Return backing storage used for references.
+  ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const {
+    uintptr_t address = reinterpret_cast<uintptr_t>(this) + ReferencesOffset(sizeof(void*));
+    return reinterpret_cast<StackReference<mirror::Object>*>(address);
+  }
+
   explicit HandleScope(size_t number_of_references) :
       link_(nullptr), number_of_references_(number_of_references) {
   }
@@ -150,56 +156,48 @@
   }
 
  private:
-  T** obj_;
+  T** const obj_;
 };
 
 // Scoped handle storage of a fixed size that is usually stack allocated.
 template<size_t kNumReferences>
 class PACKED(4) StackHandleScope FINAL : public HandleScope {
  public:
-  explicit StackHandleScope(Thread* self);
-  ~StackHandleScope();
-
-  // Currently unused, using this GetReference instead of the one in HandleScope is preferred to
-  // avoid compiler optimizations incorrectly optimizing out of bound array accesses.
-  // TODO: Remove this when it is un-necessary.
-  mirror::Object* GetReference(size_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      ALWAYS_INLINE {
-    DCHECK_LT(i, number_of_references_);
-    return references_storage_[i].AsMirrorPtr();
-  }
-
-  Handle<mirror::Object> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      ALWAYS_INLINE {
-    DCHECK_LT(i, number_of_references_);
-    return Handle<mirror::Object>(&references_storage_[i]);
-  }
-
-  void SetReference(size_t i, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      ALWAYS_INLINE {
-    DCHECK_LT(i, number_of_references_);
-    references_storage_[i].Assign(object);
-  }
+  explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr);
+  ALWAYS_INLINE ~StackHandleScope();
 
   template<class T>
-  Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     SetReference(pos_, object);
-    Handle<T> h(GetHandle(pos_));
+    Handle<T> h(GetHandle<T>(pos_));
     pos_++;
     return h;
   }
 
   template<class T>
-  HandleWrapper<T> NewHandleWrapper(T** object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE HandleWrapper<T> NewHandleWrapper(T** object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     SetReference(pos_, *object);
-    Handle<T> h(GetHandle(pos_));
+    Handle<T> h(GetHandle<T>(pos_));
     pos_++;
     return HandleWrapper<T>(object, h);
   }
 
+  ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK_LT(i, kNumReferences);
+    GetReferences()[i].Assign(object);
+  }
+
  private:
-  // References_storage_ needs to be first so that it appears in the same location as
-  // HandleScope::references_.
+  template<class T>
+  ALWAYS_INLINE Handle<T> GetHandle(size_t i)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK_LT(i, kNumReferences);
+    return Handle<T>(&GetReferences()[i]);
+  }
+
+  // Reference storage needs to be first as expected by the HandleScope layout.
   StackReference<mirror::Object> references_storage_[kNumReferences];
 
   // The thread that the stack handle scope is a linked list upon. The stack handle scope will
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index fd67197..174b350 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -44,6 +44,7 @@
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "gc/heap.h"
 #include "gc/space/space.h"
@@ -475,9 +476,14 @@
         }
       }
 
-      std::unique_ptr<File> file(new File(out_fd, filename_));
+      std::unique_ptr<File> file(new File(out_fd, filename_, true));
       okay = file->WriteFully(header_data_ptr_, header_data_size_) &&
-          file->WriteFully(body_data_ptr_, body_data_size_);
+             file->WriteFully(body_data_ptr_, body_data_size_);
+      if (okay) {
+        okay = file->FlushCloseOrErase() == 0;
+      } else {
+        file->Erase();
+      }
       if (!okay) {
         std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
                                      filename_.c_str(), strerror(errno)));
@@ -496,12 +502,12 @@
   }
 
  private:
-  static void RootVisitor(mirror::Object** obj, void* arg, uint32_t thread_id, RootType root_type)
+  static void RootVisitor(mirror::Object** obj, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(arg != nullptr);
     DCHECK(obj != nullptr);
     DCHECK(*obj != nullptr);
-    reinterpret_cast<Hprof*>(arg)->VisitRoot(*obj, thread_id, root_type);
+    reinterpret_cast<Hprof*>(arg)->VisitRoot(*obj, root_info);
   }
 
   static void VisitObjectCallback(mirror::Object* obj, void* arg)
@@ -511,7 +517,7 @@
     reinterpret_cast<Hprof*>(arg)->DumpHeapObject(obj);
   }
 
-  void VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type)
+  void VisitRoot(const mirror::Object* obj, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   int DumpHeapObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -1056,7 +1062,7 @@
   return 0;
 }
 
-void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type) {
+void Hprof::VisitRoot(const mirror::Object* obj, const RootInfo& root_info) {
   static const HprofHeapTag xlate[] = {
     HPROF_ROOT_UNKNOWN,
     HPROF_ROOT_JNI_GLOBAL,
@@ -1074,12 +1080,12 @@
     HPROF_ROOT_VM_INTERNAL,
     HPROF_ROOT_JNI_MONITOR,
   };
-  CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
+  CHECK_LT(root_info.GetType(), sizeof(xlate) / sizeof(HprofHeapTag));
   if (obj == NULL) {
     return;
   }
-  gc_scan_state_ = xlate[type];
-  gc_thread_serial_number_ = thread_id;
+  gc_scan_state_ = xlate[root_info.GetType()];
+  gc_thread_serial_number_ = root_info.GetThreadId();
   MarkRootObject(obj, 0);
   gc_scan_state_ = 0;
   gc_thread_serial_number_ = 0;
diff --git a/runtime/image.cc b/runtime/image.cc
index 90ca985..b4496e3 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
 namespace art {
 
 const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const byte ImageHeader::kImageVersion[] = { '0', '0', '9', '\0' };
+const byte ImageHeader::kImageVersion[] = { '0', '1', '2', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index 42d9c0d..a77aec4 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -106,6 +106,7 @@
   enum ImageRoot {
     kResolutionMethod,
     kImtConflictMethod,
+    kImtUnimplementedMethod,
     kDefaultImt,
     kCalleeSaveMethod,
     kRefsOnlySaveMethod,
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index c826716..2bf6ab9 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -46,7 +46,7 @@
     AbortIfNoCheckJNI();
     return false;
   }
-  if (UNLIKELY(table_[idx].IsNull())) {
+  if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
     LOG(ERROR) << "JNI ERROR (app bug): accessed deleted " << kind_ << " " << iref;
     AbortIfNoCheckJNI();
     return false;
@@ -76,10 +76,10 @@
     return kInvalidIndirectRefObject;
   }
   uint32_t idx = ExtractIndex(iref);
-  mirror::Object* obj = table_[idx].Read<kWithoutReadBarrier>();
+  mirror::Object* obj = table_[idx].GetReference()->Read<kWithoutReadBarrier>();
   if (LIKELY(obj != kClearedJniWeakGlobal)) {
     // The read barrier or VerifyObject won't handle kClearedJniWeakGlobal.
-    obj = table_[idx].Read();
+    obj = table_[idx].GetReference()->Read();
     VerifyObject(obj);
   }
   return obj;
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 9b2b82e..fe924fb 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -63,34 +63,22 @@
 }
 
 IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
-                                               size_t maxCount, IndirectRefKind desiredKind) {
+                                               size_t maxCount, IndirectRefKind desiredKind)
+    : kind_(desiredKind),
+      max_entries_(maxCount) {
   CHECK_GT(initialCount, 0U);
   CHECK_LE(initialCount, maxCount);
   CHECK_NE(desiredKind, kHandleScopeOrInvalid);
 
   std::string error_str;
-  const size_t initial_bytes = initialCount * sizeof(const mirror::Object*);
-  const size_t table_bytes = maxCount * sizeof(const mirror::Object*);
+  const size_t table_bytes = maxCount * sizeof(IrtEntry);
   table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
                                             PROT_READ | PROT_WRITE, false, &error_str));
   CHECK(table_mem_map_.get() != nullptr) << error_str;
   CHECK_EQ(table_mem_map_->Size(), table_bytes);
-
-  table_ = reinterpret_cast<GcRoot<mirror::Object>*>(table_mem_map_->Begin());
+  table_ = reinterpret_cast<IrtEntry*>(table_mem_map_->Begin());
   CHECK(table_ != nullptr);
-  memset(table_, 0xd1, initial_bytes);
-
-  const size_t slot_bytes = maxCount * sizeof(IndirectRefSlot);
-  slot_mem_map_.reset(MemMap::MapAnonymous("indirect ref table slots", nullptr, slot_bytes,
-                                           PROT_READ | PROT_WRITE, false, &error_str));
-  CHECK(slot_mem_map_.get() != nullptr) << error_str;
-  slot_data_ = reinterpret_cast<IndirectRefSlot*>(slot_mem_map_->Begin());
-  CHECK(slot_data_ != nullptr);
-
   segment_state_.all = IRT_FIRST_SEGMENT;
-  alloc_entries_ = initialCount;
-  max_entries_ = maxCount;
-  kind_ = desiredKind;
 }
 
 IndirectReferenceTable::~IndirectReferenceTable() {
@@ -104,24 +92,12 @@
   CHECK(obj != NULL);
   VerifyObject(obj);
   DCHECK(table_ != NULL);
-  DCHECK_LE(alloc_entries_, max_entries_);
   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
 
-  if (topIndex == alloc_entries_) {
-    // reached end of allocated space; did we hit buffer max?
-    if (topIndex == max_entries_) {
-      LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
-                 << "(max=" << max_entries_ << ")\n"
-                 << MutatorLockedDumpable<IndirectReferenceTable>(*this);
-    }
-
-    size_t newSize = alloc_entries_ * 2;
-    if (newSize > max_entries_) {
-      newSize = max_entries_;
-    }
-    DCHECK_GT(newSize, alloc_entries_);
-
-    alloc_entries_ = newSize;
+  if (topIndex == max_entries_) {
+    LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
+               << "(max=" << max_entries_ << ")\n"
+               << MutatorLockedDumpable<IndirectReferenceTable>(*this);
   }
 
   // We know there's enough room in the table.  Now we just need to find
@@ -129,27 +105,26 @@
   // add to the end of the list.
   IndirectRef result;
   int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
+  size_t index;
   if (numHoles > 0) {
     DCHECK_GT(topIndex, 1U);
     // Find the first hole; likely to be near the end of the list.
-    GcRoot<mirror::Object>* pScan = &table_[topIndex - 1];
-    DCHECK(!pScan->IsNull());
+    IrtEntry* pScan = &table_[topIndex - 1];
+    DCHECK(!pScan->GetReference()->IsNull());
     --pScan;
-    while (!pScan->IsNull()) {
+    while (!pScan->GetReference()->IsNull()) {
       DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
       --pScan;
     }
-    UpdateSlotAdd(obj, pScan - table_);
-    result = ToIndirectRef(pScan - table_);
-    *pScan = GcRoot<mirror::Object>(obj);
+    index = pScan - table_;
     segment_state_.parts.numHoles--;
   } else {
     // Add to the end.
-    UpdateSlotAdd(obj, topIndex);
-    result = ToIndirectRef(topIndex);
-    table_[topIndex++] = GcRoot<mirror::Object>(obj);
+    index = topIndex++;
     segment_state_.parts.topIndex = topIndex;
   }
+  table_[index].Add(obj);
+  result = ToIndirectRef(index);
   if (false) {
     LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex
               << " holes=" << segment_state_.parts.numHoles;
@@ -182,17 +157,14 @@
   int bottomIndex = prevState.parts.topIndex;
 
   DCHECK(table_ != NULL);
-  DCHECK_LE(alloc_entries_, max_entries_);
   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
 
-  int idx = ExtractIndex(iref);
-
   if (GetIndirectRefKind(iref) == kHandleScopeOrInvalid &&
       Thread::Current()->HandleScopeContains(reinterpret_cast<jobject>(iref))) {
     LOG(WARNING) << "Attempt to remove local handle scope entry from IRT, ignoring";
     return true;
   }
-
+  const int idx = ExtractIndex(iref);
   if (idx < bottomIndex) {
     // Wrong segment.
     LOG(WARNING) << "Attempt to remove index outside index area (" << idx
@@ -206,23 +178,23 @@
     return false;
   }
 
-  if (idx == topIndex-1) {
+  if (idx == topIndex - 1) {
     // Top-most entry.  Scan up and consume holes.
 
     if (!CheckEntry("remove", iref, idx)) {
       return false;
     }
 
-    table_[idx] = GcRoot<mirror::Object>(nullptr);
+    *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
     int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
     if (numHoles != 0) {
       while (--topIndex > bottomIndex && numHoles != 0) {
         if (false) {
-          LOG(INFO) << "+++ checking for hole at " << topIndex-1
+          LOG(INFO) << "+++ checking for hole at " << topIndex - 1
                     << " (cookie=" << cookie << ") val="
-                    << table_[topIndex - 1].Read<kWithoutReadBarrier>();
+                    << table_[topIndex - 1].GetReference()->Read<kWithoutReadBarrier>();
         }
-        if (!table_[topIndex-1].IsNull()) {
+        if (!table_[topIndex - 1].GetReference()->IsNull()) {
           break;
         }
         if (false) {
@@ -242,7 +214,7 @@
     // Not the top-most entry.  This creates a hole.  We NULL out the
     // entry to prevent somebody from deleting it twice and screwing up
     // the hole count.
-    if (table_[idx].IsNull()) {
+    if (table_[idx].GetReference()->IsNull()) {
       LOG(INFO) << "--- WEIRD: removing null entry " << idx;
       return false;
     }
@@ -250,7 +222,7 @@
       return false;
     }
 
-    table_[idx] = GcRoot<mirror::Object>(nullptr);
+    *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
     segment_state_.parts.numHoles++;
     if (false) {
       LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles;
@@ -260,10 +232,17 @@
   return true;
 }
 
-void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                        RootType root_type) {
+void IndirectReferenceTable::Trim() {
+  const size_t top_index = Capacity();
+  auto* release_start = AlignUp(reinterpret_cast<uint8_t*>(&table_[top_index]), kPageSize);
+  uint8_t* release_end = table_mem_map_->End();
+  madvise(release_start, release_end - release_start, MADV_DONTNEED);
+}
+
+void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg,
+                                        const RootInfo& root_info) {
   for (auto ref : *this) {
-    callback(ref, arg, tid, root_type);
+    callback(ref, arg, root_info);
     DCHECK(*ref != nullptr);
   }
 }
@@ -272,7 +251,7 @@
   os << kind_ << " table dump:\n";
   ReferenceTable::Table entries;
   for (size_t i = 0; i < Capacity(); ++i) {
-    mirror::Object* obj = table_[i].Read<kWithoutReadBarrier>();
+    mirror::Object* obj = table_[i].GetReference()->Read<kWithoutReadBarrier>();
     if (UNLIKELY(obj == nullptr)) {
       // Remove NULLs.
     } else if (UNLIKELY(obj == kClearedJniWeakGlobal)) {
@@ -280,7 +259,7 @@
       // while the read barrier won't.
       entries.push_back(GcRoot<mirror::Object>(obj));
     } else {
-      obj = table_[i].Read();
+      obj = table_[i].GetReference()->Read();
       entries.push_back(GcRoot<mirror::Object>(obj));
     }
   }
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fb910e2..0202347 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -31,6 +31,9 @@
 #include "read_barrier_option.h"
 
 namespace art {
+
+class RootInfo;
+
 namespace mirror {
 class Object;
 }  // namespace mirror
@@ -127,16 +130,6 @@
   return static_cast<IndirectRefKind>(reinterpret_cast<uintptr_t>(iref) & 0x03);
 }
 
-/*
- * Extended debugging structure.  We keep a parallel array of these, one
- * per slot in the table.
- */
-static const size_t kIRTPrevCount = 4;
-struct IndirectRefSlot {
-  uint32_t serial;
-  const mirror::Object* previous[kIRTPrevCount];
-};
-
 /* use as initial value for "cookie", and when table has only one segment */
 static const uint32_t IRT_FIRST_SEGMENT = 0;
 
@@ -203,9 +196,35 @@
   } parts;
 };
 
+// Try to choose kIRTPrevCount so that sizeof(IrtEntry) is a power of 2.
+// Contains multiple entries but only one active one, this helps us detect use after free errors
+// since the serial stored in the indirect ref wont match.
+static const size_t kIRTPrevCount = kIsDebugBuild ? 7 : 3;
+class PACKED(4) IrtEntry {
+ public:
+  void Add(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ++serial_;
+    if (serial_ == kIRTPrevCount) {
+      serial_ = 0;
+    }
+    references_[serial_] = GcRoot<mirror::Object>(obj);
+  }
+  GcRoot<mirror::Object>* GetReference() {
+    DCHECK_LT(serial_, kIRTPrevCount);
+    return &references_[serial_];
+  }
+  uint32_t GetSerial() const {
+    return serial_;
+  }
+
+ private:
+  uint32_t serial_;
+  GcRoot<mirror::Object> references_[kIRTPrevCount];
+};
+
 class IrtIterator {
  public:
-  explicit IrtIterator(GcRoot<mirror::Object>* table, size_t i, size_t capacity)
+  explicit IrtIterator(IrtEntry* table, size_t i, size_t capacity)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : table_(table), i_(i), capacity_(capacity) {
     SkipNullsAndTombstones();
@@ -219,7 +238,7 @@
 
   mirror::Object** operator*() {
     // This does not have a read barrier as this is used to visit roots.
-    return table_[i_].AddressWithoutBarrier();
+    return table_[i_].GetReference()->AddressWithoutBarrier();
   }
 
   bool equals(const IrtIterator& rhs) const {
@@ -230,13 +249,13 @@
   void SkipNullsAndTombstones() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // We skip NULLs and tombstones. Clients don't want to see implementation details.
     while (i_ < capacity_ &&
-           (table_[i_].IsNull() ||
-            table_[i_].Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal)) {
+           (table_[i_].GetReference()->IsNull() ||
+            table_[i_].GetReference()->Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal)) {
       ++i_;
     }
   }
 
-  GcRoot<mirror::Object>* const table_;
+  IrtEntry* const table_;
   size_t i_;
   size_t capacity_;
 };
@@ -313,7 +332,7 @@
     return IrtIterator(table_, Capacity(), Capacity());
   }
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uint32_t GetSegmentState() const {
@@ -328,10 +347,11 @@
     return Offset(OFFSETOF_MEMBER(IndirectReferenceTable, segment_state_));
   }
 
+  // Release pages past the end of the table that may have previously held references.
+  void Trim() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
-  /*
-   * Extract the table index from an indirect reference.
-   */
+  // Extract the table index from an indirect reference.
   static uint32_t ExtractIndex(IndirectRef iref) {
     uintptr_t uref = reinterpret_cast<uintptr_t>(iref);
     return (uref >> 2) & 0xffff;
@@ -343,25 +363,11 @@
    */
   IndirectRef ToIndirectRef(uint32_t tableIndex) const {
     DCHECK_LT(tableIndex, 65536U);
-    uint32_t serialChunk = slot_data_[tableIndex].serial;
-    uintptr_t uref = serialChunk << 20 | (tableIndex << 2) | kind_;
+    uint32_t serialChunk = table_[tableIndex].GetSerial();
+    uintptr_t uref = (serialChunk << 20) | (tableIndex << 2) | kind_;
     return reinterpret_cast<IndirectRef>(uref);
   }
 
-  /*
-   * Update extended debug info when an entry is added.
-   *
-   * We advance the serial number, invalidating any outstanding references to
-   * this slot.
-   */
-  void UpdateSlotAdd(const mirror::Object* obj, int slot) {
-    if (slot_data_ != NULL) {
-      IndirectRefSlot* pSlot = &slot_data_[slot];
-      pSlot->serial++;
-      pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
-    }
-  }
-
   // Abort if check_jni is not enabled.
   static void AbortIfNoCheckJNI();
 
@@ -374,19 +380,13 @@
 
   // Mem map where we store the indirect refs.
   std::unique_ptr<MemMap> table_mem_map_;
-  // Mem map where we store the extended debugging info.
-  std::unique_ptr<MemMap> slot_mem_map_;
   // bottom of the stack. Do not directly access the object references
   // in this as they are roots. Use Get() that has a read barrier.
-  GcRoot<mirror::Object>* table_;
+  IrtEntry* table_;
   /* bit mask, ORed into all irefs */
-  IndirectRefKind kind_;
-  /* extended debugging info */
-  IndirectRefSlot* slot_data_;
-  /* #of entries we have space for */
-  size_t alloc_entries_;
+  const IndirectRefKind kind_;
   /* max #of entries allowed */
-  size_t max_entries_;
+  const size_t max_entries_;
 };
 
 }  // namespace art
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index ae8eeac..da7d153 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -125,6 +125,10 @@
   }
 }
 
+static inline size_t InstructionSetPointerSize(InstructionSet isa) {
+  return Is64BitInstructionSet(isa) ? 8U : 4U;
+}
+
 static inline size_t GetBytesPerGprSpillLocation(InstructionSet isa) {
   switch (isa) {
     case kArm:
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 652d29b..d06a67f 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -918,10 +918,10 @@
   ConfigureStubs(false, false);
 }
 
-const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method) const {
+const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const {
   Runtime* runtime = Runtime::Current();
   if (LIKELY(!instrumentation_stubs_installed_)) {
-    const void* code = method->GetEntryPointFromQuickCompiledCode();
+    const void* code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
     DCHECK(code != nullptr);
     ClassLinker* class_linker = runtime->GetClassLinker();
     if (LIKELY(code != class_linker->GetQuickResolutionTrampoline()) &&
@@ -1088,15 +1088,14 @@
   // back to an upcall.
   NthCallerVisitor visitor(self, 1, true);
   visitor.WalkStack(true);
-  bool deoptimize = (visitor.caller != NULL) &&
+  bool deoptimize = (visitor.caller != nullptr) &&
                     (interpreter_stubs_installed_ || IsDeoptimized(visitor.caller));
-  if (deoptimize && kVerboseInstrumentation) {
-    LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller);
-  }
   if (deoptimize) {
     if (kVerboseInstrumentation) {
-      LOG(INFO) << "Deoptimizing from " << PrettyMethod(method)
-                << " result is " << std::hex << return_value.GetJ();
+      LOG(INFO) << StringPrintf("Deoptimizing %s by returning from %s with result %#" PRIx64 " in ",
+                                PrettyMethod(visitor.caller).c_str(),
+                                PrettyMethod(method).c_str(),
+                                return_value.GetJ()) << *self;
     }
     self->SetDeoptimizationReturnValue(return_value);
     return GetTwoWordSuccessValue(*return_pc,
@@ -1142,7 +1141,7 @@
     return;
   }
   for (auto pair : deoptimized_methods_) {
-    pair.second.VisitRoot(callback, arg, 0, kRootVMInternal);
+    pair.second.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   }
 }
 
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 3017bf6..c261a1a 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -200,7 +200,7 @@
   // Get the quick code for the given method. More efficient than asking the class linker as it
   // will short-cut to GetCode if instrumentation and static method resolution stubs aren't
   // installed.
-  const void* GetQuickCodeFor(mirror::ArtMethod* method) const
+  const void* GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void ForceInterpretOnly() {
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index c66f99e..144b2c5 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -29,56 +29,48 @@
 namespace art {
 
 InternTable::InternTable()
-    : log_new_roots_(false), allow_new_interns_(true),
+    : image_added_to_intern_table_(false), log_new_roots_(false),
+      allow_new_interns_(true),
       new_intern_condition_("New intern condition", *Locks::intern_table_lock_) {
 }
 
 size_t InternTable::Size() const {
   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
-  return strong_interns_.size() + weak_interns_.size();
+  return strong_interns_.Size() + weak_interns_.Size();
 }
 
 size_t InternTable::StrongSize() const {
   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
-  return strong_interns_.size();
+  return strong_interns_.Size();
 }
 
 size_t InternTable::WeakSize() const {
   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
-  return weak_interns_.size();
+  return weak_interns_.Size();
 }
 
 void InternTable::DumpForSigQuit(std::ostream& os) const {
-  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
-  os << "Intern table: " << strong_interns_.size() << " strong; "
-     << weak_interns_.size() << " weak\n";
+  os << "Intern table: " << StrongSize() << " strong; " << WeakSize() << " weak\n";
 }
 
 void InternTable::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
   if ((flags & kVisitRootFlagAllRoots) != 0) {
-    for (auto& strong_intern : strong_interns_) {
-      const_cast<GcRoot<mirror::String>&>(strong_intern).
-          VisitRoot(callback, arg, 0, kRootInternedString);
-      DCHECK(!strong_intern.IsNull());
-    }
+    strong_interns_.VisitRoots(callback, arg);
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_strong_intern_roots_) {
       mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
-      root.VisitRoot(callback, arg, 0, kRootInternedString);
+      root.VisitRoot(callback, arg, RootInfo(kRootInternedString));
       mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
-      if (UNLIKELY(new_ref != old_ref)) {
+      if (new_ref != old_ref) {
         // The GC moved a root in the log. Need to search the strong interns and update the
         // corresponding object. This is slow, but luckily for us, this may only happen with a
         // concurrent moving GC.
-        auto it = strong_interns_.find(GcRoot<mirror::String>(old_ref));
-        DCHECK(it != strong_interns_.end());
-        strong_interns_.erase(it);
-        strong_interns_.insert(GcRoot<mirror::String>(new_ref));
+        strong_interns_.Remove(old_ref);
+        strong_interns_.Insert(new_ref);
       }
     }
   }
-
   if ((flags & kVisitRootFlagClearRootLog) != 0) {
     new_strong_intern_roots_.clear();
   }
@@ -91,21 +83,17 @@
 }
 
 mirror::String* InternTable::LookupStrong(mirror::String* s) {
-  return Lookup(&strong_interns_, s);
+  return strong_interns_.Find(s);
 }
 
 mirror::String* InternTable::LookupWeak(mirror::String* s) {
-  // Weak interns need a read barrier because they are weak roots.
-  return Lookup(&weak_interns_, s);
+  return weak_interns_.Find(s);
 }
 
-mirror::String* InternTable::Lookup(Table* table, mirror::String* s) {
-  Locks::intern_table_lock_->AssertHeld(Thread::Current());
-  auto it = table->find(GcRoot<mirror::String>(s));
-  if (LIKELY(it != table->end())) {
-    return const_cast<GcRoot<mirror::String>&>(*it).Read<kWithReadBarrier>();
-  }
-  return nullptr;
+void InternTable::SwapPostZygoteWithPreZygote() {
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+  weak_interns_.SwapPostZygoteWithPreZygote();
+  strong_interns_.SwapPostZygoteWithPreZygote();
 }
 
 mirror::String* InternTable::InsertStrong(mirror::String* s) {
@@ -116,7 +104,7 @@
   if (log_new_roots_) {
     new_strong_intern_roots_.push_back(GcRoot<mirror::String>(s));
   }
-  strong_interns_.insert(GcRoot<mirror::String>(s));
+  strong_interns_.Insert(s);
   return s;
 }
 
@@ -125,12 +113,12 @@
   if (runtime->IsActiveTransaction()) {
     runtime->RecordWeakStringInsertion(s);
   }
-  weak_interns_.insert(GcRoot<mirror::String>(s));
+  weak_interns_.Insert(s);
   return s;
 }
 
 void InternTable::RemoveStrong(mirror::String* s) {
-  Remove(&strong_interns_, s);
+  strong_interns_.Remove(s);
 }
 
 void InternTable::RemoveWeak(mirror::String* s) {
@@ -138,13 +126,7 @@
   if (runtime->IsActiveTransaction()) {
     runtime->RecordWeakStringRemoval(s);
   }
-  Remove(&weak_interns_, s);
-}
-
-void InternTable::Remove(Table* table, mirror::String* s) {
-  auto it = table->find(GcRoot<mirror::String>(s));
-  DCHECK(it != table->end());
-  table->erase(it);
+  weak_interns_.Remove(s);
 }
 
 // Insert/remove methods used to undo changes made during an aborted transaction.
@@ -165,11 +147,40 @@
   RemoveWeak(s);
 }
 
-static mirror::String* LookupStringFromImage(mirror::String* s)
+void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space) {
+  CHECK(image_space != nullptr);
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+  if (!image_added_to_intern_table_) {
+    mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+    mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
+    for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+      mirror::DexCache* dex_cache = dex_caches->Get(i);
+      const DexFile* dex_file = dex_cache->GetDexFile();
+      const size_t num_strings = dex_file->NumStringIds();
+      for (size_t j = 0; j < num_strings; ++j) {
+        mirror::String* image_string = dex_cache->GetResolvedString(j);
+        if (image_string != nullptr) {
+          mirror::String* found = LookupStrong(image_string);
+          if (found == nullptr) {
+            InsertStrong(image_string);
+          } else {
+            DCHECK_EQ(found, image_string);
+          }
+        }
+      }
+    }
+    image_added_to_intern_table_ = true;
+  }
+}
+
+mirror::String* InternTable::LookupStringFromImage(mirror::String* s)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (image_added_to_intern_table_) {
+    return nullptr;
+  }
   gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
-  if (image == NULL) {
-    return NULL;  // No image present.
+  if (image == nullptr) {
+    return nullptr;  // No image present.
   }
   mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
   mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
@@ -179,15 +190,15 @@
     const DexFile* dex_file = dex_cache->GetDexFile();
     // Binary search the dex file for the string index.
     const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
-    if (string_id != NULL) {
+    if (string_id != nullptr) {
       uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
       mirror::String* image = dex_cache->GetResolvedString(string_idx);
-      if (image != NULL) {
+      if (image != nullptr) {
         return image;
       }
     }
   }
-  return NULL;
+  return nullptr;
 }
 
 void InternTable::AllowNewInterns() {
@@ -204,58 +215,36 @@
 }
 
 mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
+  if (s == nullptr) {
+    return nullptr;
+  }
   Thread* self = Thread::Current();
   MutexLock mu(self, *Locks::intern_table_lock_);
-
-  DCHECK(s != NULL);
-
   while (UNLIKELY(!allow_new_interns_)) {
     new_intern_condition_.WaitHoldingLocks(self);
   }
-
-  if (is_strong) {
-    // Check the strong table for a match.
-    mirror::String* strong = LookupStrong(s);
-    if (strong != NULL) {
-      return strong;
-    }
-
-    // Check the image for a match.
-    mirror::String* image = LookupStringFromImage(s);
-    if (image != NULL) {
-      return InsertStrong(image);
-    }
-
-    // There is no match in the strong table, check the weak table.
-    mirror::String* weak = LookupWeak(s);
-    if (weak != NULL) {
-      // A match was found in the weak table. Promote to the strong table.
-      RemoveWeak(weak);
-      return InsertStrong(weak);
-    }
-
-    // No match in the strong table or the weak table. Insert into the strong
-    // table.
-    return InsertStrong(s);
-  }
-
   // Check the strong table for a match.
   mirror::String* strong = LookupStrong(s);
-  if (strong != NULL) {
+  if (strong != nullptr) {
     return strong;
   }
   // Check the image for a match.
   mirror::String* image = LookupStringFromImage(s);
-  if (image != NULL) {
-    return InsertWeak(image);
+  if (image != nullptr) {
+    return is_strong ? InsertStrong(image) : InsertWeak(image);
   }
-  // Check the weak table for a match.
+  // There is no match in the strong table, check the weak table.
   mirror::String* weak = LookupWeak(s);
-  if (weak != NULL) {
+  if (weak != nullptr) {
+    if (is_strong) {
+      // A match was found in the weak table. Promote to the strong table.
+      RemoveWeak(weak);
+      return InsertStrong(weak);
+    }
     return weak;
   }
-  // Insert into the weak table.
-  return InsertWeak(s);
+  // No match in the strong table or the weak table. Insert into the strong / weak table.
+  return is_strong ? InsertStrong(s) : InsertWeak(s);
 }
 
 mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
@@ -270,56 +259,104 @@
 }
 
 mirror::String* InternTable::InternStrong(mirror::String* s) {
-  if (s == nullptr) {
-    return nullptr;
-  }
   return Insert(s, true);
 }
 
 mirror::String* InternTable::InternWeak(mirror::String* s) {
-  if (s == nullptr) {
-    return nullptr;
-  }
   return Insert(s, false);
 }
 
 bool InternTable::ContainsWeak(mirror::String* s) {
   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
-  const mirror::String* found = LookupWeak(s);
-  return found == s;
+  return LookupWeak(s) == s;
 }
 
 void InternTable::SweepInternTableWeaks(IsMarkedCallback* callback, void* arg) {
   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
-  for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) {
+  weak_interns_.SweepWeaks(callback, arg);
+}
+
+std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& root) const {
+  if (kIsDebugBuild) {
+    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+  }
+  return static_cast<size_t>(root.Read()->GetHashCode());
+}
+
+bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
+                                               const GcRoot<mirror::String>& b) const {
+  if (kIsDebugBuild) {
+    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+  }
+  return a.Read()->Equals(b.Read());
+}
+
+void InternTable::Table::Remove(mirror::String* s) {
+  auto it = post_zygote_table_.Find(GcRoot<mirror::String>(s));
+  if (it != post_zygote_table_.end()) {
+    post_zygote_table_.Erase(it);
+  } else {
+    it = pre_zygote_table_.Find(GcRoot<mirror::String>(s));
+    DCHECK(it != pre_zygote_table_.end());
+    pre_zygote_table_.Erase(it);
+  }
+}
+
+mirror::String* InternTable::Table::Find(mirror::String* s) {
+  Locks::intern_table_lock_->AssertHeld(Thread::Current());
+  auto it = pre_zygote_table_.Find(GcRoot<mirror::String>(s));
+  if (it != pre_zygote_table_.end()) {
+    return it->Read();
+  }
+  it = post_zygote_table_.Find(GcRoot<mirror::String>(s));
+  if (it != post_zygote_table_.end()) {
+    return it->Read();
+  }
+  return nullptr;
+}
+
+void InternTable::Table::SwapPostZygoteWithPreZygote() {
+  CHECK(pre_zygote_table_.Empty());
+  std::swap(pre_zygote_table_, post_zygote_table_);
+  VLOG(heap) << "Swapping " << pre_zygote_table_.Size() << " interns to the pre zygote table";
+}
+
+void InternTable::Table::Insert(mirror::String* s) {
+  // Always insert the post zygote table, this gets swapped when we create the zygote to be the
+  // pre zygote table.
+  post_zygote_table_.Insert(GcRoot<mirror::String>(s));
+}
+
+void InternTable::Table::VisitRoots(RootCallback* callback, void* arg) {
+  for (auto& intern : pre_zygote_table_) {
+    intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
+  }
+  for (auto& intern : post_zygote_table_) {
+    intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
+  }
+}
+
+void InternTable::Table::SweepWeaks(IsMarkedCallback* callback, void* arg) {
+  SweepWeaks(&pre_zygote_table_, callback, arg);
+  SweepWeaks(&post_zygote_table_, callback, arg);
+}
+
+void InternTable::Table::SweepWeaks(UnorderedSet* set, IsMarkedCallback* callback, void* arg) {
+  for (auto it = set->begin(), end = set->end(); it != end;) {
     // This does not need a read barrier because this is called by GC.
-    GcRoot<mirror::String>& root = const_cast<GcRoot<mirror::String>&>(*it);
-    mirror::Object* object = root.Read<kWithoutReadBarrier>();
+    mirror::Object* object = it->Read<kWithoutReadBarrier>();
     mirror::Object* new_object = callback(object, arg);
     if (new_object == nullptr) {
-      it = weak_interns_.erase(it);
+      it = set->Erase(it);
     } else {
-      root.Assign(down_cast<mirror::String*>(new_object));
+      *it = GcRoot<mirror::String>(new_object->AsString());
       ++it;
     }
   }
 }
 
-std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& root) {
-  if (kIsDebugBuild) {
-    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
-  }
-  return static_cast<size_t>(
-      const_cast<GcRoot<mirror::String>&>(root).Read<kWithoutReadBarrier>()->GetHashCode());
-}
-
-bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
-                                               const GcRoot<mirror::String>& b) {
-  if (kIsDebugBuild) {
-    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
-  }
-  return const_cast<GcRoot<mirror::String>&>(a).Read<kWithoutReadBarrier>()->Equals(
-      const_cast<GcRoot<mirror::String>&>(b).Read<kWithoutReadBarrier>());
+size_t InternTable::Table::Size() const {
+  return pre_zygote_table_.Size() + post_zygote_table_.Size();
 }
 
 }  // namespace art
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index e3223c8..371d3f7 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -20,12 +20,19 @@
 #include <unordered_set>
 
 #include "base/allocator.h"
+#include "base/hash_set.h"
 #include "base/mutex.h"
 #include "gc_root.h"
 #include "object_callbacks.h"
 
 namespace art {
 
+namespace gc {
+namespace space {
+class ImageSpace;
+}  // namespace space
+}  // namespace gc
+
 enum VisitRootFlags : uint8_t;
 
 namespace mirror {
@@ -66,9 +73,12 @@
 
   bool ContainsWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  size_t Size() const;
-  size_t StrongSize() const;
-  size_t WeakSize() const;
+  // Total number of interned strings.
+  size_t Size() const LOCKS_EXCLUDED(Locks::intern_table_lock_);
+  // Total number of weakly live interned strings.
+  size_t StrongSize() const LOCKS_EXCLUDED(Locks::intern_table_lock_);
+  // Total number of strongly live interned strings.
+  size_t WeakSize() const LOCKS_EXCLUDED(Locks::intern_table_lock_);
 
   void VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -78,29 +88,83 @@
   void DisallowNewInterns() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
   void AllowNewInterns() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Adds all of the resolved image strings from the image space into the intern table. The
+  // advantage of doing this is preventing expensive DexFile::FindStringId calls.
+  void AddImageStringsToTable(gc::space::ImageSpace* image_space)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::intern_table_lock_);
+  // Copy the post zygote tables to pre zygote to save memory by preventing dirty pages.
+  void SwapPostZygoteWithPreZygote()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::intern_table_lock_);
+
  private:
   class StringHashEquals {
    public:
-    std::size_t operator()(const GcRoot<mirror::String>& root) NO_THREAD_SAFETY_ANALYSIS;
-    bool operator()(const GcRoot<mirror::String>& a, const GcRoot<mirror::String>& b)
+    std::size_t operator()(const GcRoot<mirror::String>& root) const NO_THREAD_SAFETY_ANALYSIS;
+    bool operator()(const GcRoot<mirror::String>& a, const GcRoot<mirror::String>& b) const
         NO_THREAD_SAFETY_ANALYSIS;
   };
-  typedef std::unordered_set<GcRoot<mirror::String>, StringHashEquals, StringHashEquals,
-      TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>> Table;
+  class GcRootEmptyFn {
+   public:
+    void MakeEmpty(GcRoot<mirror::String>& item) const {
+      item = GcRoot<mirror::String>();
+    }
+    bool IsEmpty(const GcRoot<mirror::String>& item) const {
+      return item.IsNull();
+    }
+  };
 
+  // Table which holds pre zygote and post zygote interned strings. There is one instance for
+  // weak interns and strong interns.
+  class Table {
+   public:
+    mirror::String* Find(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+    void Insert(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+    void Remove(mirror::String* s)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+    void VisitRoots(RootCallback* callback, void* arg)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+    void SweepWeaks(IsMarkedCallback* callback, void* arg)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+    void SwapPostZygoteWithPreZygote() EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+    size_t Size() const EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+
+   private:
+    typedef HashSet<GcRoot<mirror::String>, GcRootEmptyFn, StringHashEquals, StringHashEquals,
+        TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>> UnorderedSet;
+
+    void SweepWeaks(UnorderedSet* set, IsMarkedCallback* callback, void* arg)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+
+    // We call SwapPostZygoteWithPreZygote when we create the zygote to reduce private dirty pages
+    // caused by modifying the zygote intern table hash table. The pre zygote table are the
+    // interned strings which were interned before we created the zygote space. Post zygote is self
+    // explanatory.
+    UnorderedSet pre_zygote_table_;
+    UnorderedSet post_zygote_table_;
+  };
+
+  // Insert if non null, otherwise return nullptr.
   mirror::String* Insert(mirror::String* s, bool is_strong)
       LOCKS_EXCLUDED(Locks::intern_table_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   mirror::String* LookupStrong(mirror::String* s)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   mirror::String* LookupWeak(mirror::String* s)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  mirror::String* Lookup(Table* table, mirror::String* s)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   mirror::String* InsertStrong(mirror::String* s)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   mirror::String* InsertWeak(mirror::String* s)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   void RemoveStrong(mirror::String* s)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -108,14 +172,16 @@
   void RemoveWeak(mirror::String* s)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
-  void Remove(Table* table, mirror::String* s)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
 
   // Transaction rollback access.
+  mirror::String* LookupStringFromImage(mirror::String* s)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   mirror::String* InsertStrongFromTransaction(mirror::String* s)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   mirror::String* InsertWeakFromTransaction(mirror::String* s)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   void RemoveStrongFromTransaction(mirror::String* s)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -125,6 +191,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
   friend class Transaction;
 
+  bool image_added_to_intern_table_ GUARDED_BY(Locks::intern_table_lock_);
   bool log_new_roots_ GUARDED_BY(Locks::intern_table_lock_);
   bool allow_new_interns_ GUARDED_BY(Locks::intern_table_lock_);
   ConditionVariable new_intern_condition_ GUARDED_BY(Locks::intern_table_lock_);
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 47a7f0d..d2a3d09 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -138,7 +138,7 @@
   if (method->IsStatic()) {
     if (shorty == "L") {
       typedef jobject (fntype)(JNIEnv*, jclass);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       jobject jresult;
@@ -149,35 +149,35 @@
       result->SetL(soa.Decode<Object*>(jresult));
     } else if (shorty == "V") {
       typedef void (fntype)(JNIEnv*, jclass);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       fn(soa.Env(), klass.get());
     } else if (shorty == "Z") {
       typedef jboolean (fntype)(JNIEnv*, jclass);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetZ(fn(soa.Env(), klass.get()));
     } else if (shorty == "BI") {
       typedef jbyte (fntype)(JNIEnv*, jclass, jint);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetB(fn(soa.Env(), klass.get(), args[0]));
     } else if (shorty == "II") {
       typedef jint (fntype)(JNIEnv*, jclass, jint);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetI(fn(soa.Env(), klass.get(), args[0]));
     } else if (shorty == "LL") {
       typedef jobject (fntype)(JNIEnv*, jclass, jobject);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -190,14 +190,15 @@
       result->SetL(soa.Decode<Object*>(jresult));
     } else if (shorty == "IIZ") {
       typedef jint (fntype)(JNIEnv*, jclass, jint, jboolean);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetI(fn(soa.Env(), klass.get(), args[0], args[1]));
     } else if (shorty == "ILI") {
       typedef jint (fntype)(JNIEnv*, jclass, jobject, jint);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(
+          method->GetEntryPointFromJni()));
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -206,21 +207,21 @@
       result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1]));
     } else if (shorty == "SIZ") {
       typedef jshort (fntype)(JNIEnv*, jclass, jint, jboolean);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetEntryPointFromJni()));
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       result->SetS(fn(soa.Env(), klass.get(), args[0], args[1]));
     } else if (shorty == "VIZ") {
       typedef void (fntype)(JNIEnv*, jclass, jint, jboolean);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedThreadStateChange tsc(self, kNative);
       fn(soa.Env(), klass.get(), args[0], args[1]);
     } else if (shorty == "ZLL") {
       typedef jboolean (fntype)(JNIEnv*, jclass, jobject, jobject);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -231,7 +232,7 @@
       result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get()));
     } else if (shorty == "ZILL") {
       typedef jboolean (fntype)(JNIEnv*, jclass, jint, jobject, jobject);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedLocalRef<jobject> arg1(soa.Env(),
@@ -242,7 +243,7 @@
       result->SetZ(fn(soa.Env(), klass.get(), args[0], arg1.get(), arg2.get()));
     } else if (shorty == "VILII") {
       typedef void (fntype)(JNIEnv*, jclass, jint, jobject, jint, jint);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedLocalRef<jobject> arg1(soa.Env(),
@@ -251,7 +252,7 @@
       fn(soa.Env(), klass.get(), args[0], arg1.get(), args[2], args[3]);
     } else if (shorty == "VLILII") {
       typedef void (fntype)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jclass> klass(soa.Env(),
                                    soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
       ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -267,7 +268,7 @@
   } else {
     if (shorty == "L") {
       typedef jobject (fntype)(JNIEnv*, jobject);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
       jobject jresult;
@@ -278,14 +279,14 @@
       result->SetL(soa.Decode<Object*>(jresult));
     } else if (shorty == "V") {
       typedef void (fntype)(JNIEnv*, jobject);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
       ScopedThreadStateChange tsc(self, kNative);
       fn(soa.Env(), rcvr.get());
     } else if (shorty == "LL") {
       typedef jobject (fntype)(JNIEnv*, jobject, jobject);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
       ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -299,7 +300,7 @@
       ScopedThreadStateChange tsc(self, kNative);
     } else if (shorty == "III") {
       typedef jint (fntype)(JNIEnv*, jobject, jint, jint);
-      fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+      fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
       ScopedLocalRef<jobject> rcvr(soa.Env(),
                                    soa.AddLocalReference<jobject>(receiver));
       ScopedThreadStateChange tsc(self, kNative);
@@ -499,7 +500,23 @@
     StackHandleScope<1> hs(self);
     MethodHelper mh(hs.NewHandle(shadow_frame->GetMethod()));
     const DexFile::CodeItem* code_item = mh.GetMethod()->GetCodeItem();
-    value = Execute(self, mh, code_item, *shadow_frame, value);
+    const uint32_t dex_pc = shadow_frame->GetDexPC();
+    uint32_t new_dex_pc;
+    if (UNLIKELY(self->IsExceptionPending())) {
+      const instrumentation::Instrumentation* const instrumentation =
+          Runtime::Current()->GetInstrumentation();
+      uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, dex_pc,
+                                                                    instrumentation);
+      new_dex_pc = found_dex_pc;  // the dex pc of a matching catch handler
+                                  // or DexFile::kDexNoIndex if there is none.
+    } else {
+      const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+      new_dex_pc = dex_pc + instr->SizeInCodeUnits();  // the dex pc of the next instruction.
+    }
+    if (new_dex_pc != DexFile::kDexNoIndex) {
+      shadow_frame->SetDexPC(new_dex_pc);
+      value = Execute(self, mh, code_item, *shadow_frame, value);
+    }
     ShadowFrame* old_frame = shadow_frame;
     shadow_frame = shadow_frame->GetLink();
     delete old_frame;
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index e098ac8..f9c4978 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -147,7 +147,10 @@
   const void* const* currentHandlersTable;
   bool notified_method_entry_event = false;
   UPDATE_HANDLER_TABLE();
-  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing..
+  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing.
+    if (kIsDebugBuild) {
+      self->AssertNoPendingException();
+    }
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
       instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -235,6 +238,7 @@
 
   HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) {
     Throwable* exception = self->GetException(nullptr);
+    DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
     shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
     self->ClearException();
     ADVANCE(1);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 5401495..5888180 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -70,7 +70,10 @@
   uint32_t dex_pc = shadow_frame.GetDexPC();
   bool notified_method_entry_event = false;
   const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
-  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing..
+  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing.
+    if (kIsDebugBuild) {
+        self->AssertNoPendingException();
+    }
     if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
       instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                         shadow_frame.GetMethod(), 0);
@@ -162,6 +165,7 @@
       case Instruction::MOVE_EXCEPTION: {
         PREAMBLE();
         Throwable* exception = self->GetException(nullptr);
+        DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
         shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
         self->ClearException();
         inst = inst->Next_1xx();
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index e49a408..2cdf272 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -125,6 +125,10 @@
 };
 
 static bool NeedsFullDeoptimization(JdwpEventKind eventKind) {
+  if (!Dbg::RequiresDeoptimization()) {
+    // We don't need deoptimization for debugging.
+    return false;
+  }
   switch (eventKind) {
       case EK_METHOD_ENTRY:
       case EK_METHOD_EXIT:
@@ -181,8 +185,14 @@
     for (int i = 0; i < pEvent->modCount; i++) {
       const JdwpEventMod* pMod = &pEvent->mods[i];
       if (pMod->modKind == MK_LOCATION_ONLY) {
-        /* should only be for Breakpoint, Step, and Exception */
-        Dbg::WatchLocation(&pMod->locationOnly.loc, &req);
+        // Should only concern breakpoint, field access, field modification, step, and exception
+        // events.
+        // However breakpoint requires specific handling. Field access, field modification and step
+        // events need full deoptimization to be reported while exception event is reported during
+        // exception handling.
+        if (pEvent->eventKind == EK_BREAKPOINT) {
+          Dbg::WatchLocation(&pMod->locationOnly.loc, &req);
+        }
       } else if (pMod->modKind == MK_STEP) {
         /* should only be for EK_SINGLE_STEP; should only be one */
         JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size);
@@ -258,8 +268,10 @@
     for (int i = 0; i < pEvent->modCount; i++) {
       JdwpEventMod* pMod = &pEvent->mods[i];
       if (pMod->modKind == MK_LOCATION_ONLY) {
-        /* should only be for Breakpoint, Step, and Exception */
-        Dbg::UnwatchLocation(&pMod->locationOnly.loc, &req);
+        // Like in RegisterEvent, we need specific handling for breakpoint only.
+        if (pEvent->eventKind == EK_BREAKPOINT) {
+          Dbg::UnwatchLocation(&pMod->locationOnly.loc, &req);
+        }
       }
       if (pMod->modKind == MK_STEP) {
         /* should only be for EK_SINGLE_STEP; should only be one */
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 6888241..2d0301f 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1344,8 +1344,10 @@
       }
       break;
     default:
-      LOG(WARNING) << "GLITCH: unsupported modKind=" << mod.modKind;
-      break;
+      LOG(WARNING) << "Unsupported modifier " << mod.modKind << " for event " << pEvent->eventKind;
+      // Free allocated event to avoid leak before leaving.
+      EventFree(pEvent);
+      return JDWP::ERR_NOT_IMPLEMENTED;
     }
   }
 
@@ -1385,26 +1387,7 @@
  */
 static JdwpError SF_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
-  FrameId frame_id = request.ReadFrameId();
-  int32_t slot_count = request.ReadSigned32("slot count");
-
-  expandBufAdd4BE(pReply, slot_count);     /* "int values" */
-  for (int32_t i = 0; i < slot_count; ++i) {
-    uint32_t slot = request.ReadUnsigned32("slot");
-    JDWP::JdwpTag reqSigByte = request.ReadTag();
-
-    VLOG(jdwp) << "    --> slot " << slot << " " << reqSigByte;
-
-    size_t width = Dbg::GetTagWidth(reqSigByte);
-    uint8_t* ptr = expandBufAddSpace(pReply, width+1);
-    JdwpError error = Dbg::GetLocalValue(thread_id, frame_id, slot, reqSigByte, ptr, width);
-    if (error != ERR_NONE) {
-      return error;
-    }
-  }
-
-  return ERR_NONE;
+  return Dbg::GetLocalValues(&request, pReply);
 }
 
 /*
@@ -1412,24 +1395,7 @@
  */
 static JdwpError SF_SetValues(JdwpState*, Request& request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
-  FrameId frame_id = request.ReadFrameId();
-  int32_t slot_count = request.ReadSigned32("slot count");
-
-  for (int32_t i = 0; i < slot_count; ++i) {
-    uint32_t slot = request.ReadUnsigned32("slot");
-    JDWP::JdwpTag sigByte = request.ReadTag();
-    size_t width = Dbg::GetTagWidth(sigByte);
-    uint64_t value = request.ReadValue(width);
-
-    VLOG(jdwp) << "    --> slot " << slot << " " << sigByte << " " << value;
-    JdwpError error = Dbg::SetLocalValue(thread_id, frame_id, slot, sigByte, value, width);
-    if (error != ERR_NONE) {
-      return error;
-    }
-  }
-
-  return ERR_NONE;
+  return Dbg::SetLocalValues(&request);
 }
 
 static JdwpError SF_ThisObject(JdwpState*, Request& request, ExpandBuf* reply)
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index ad18d8a..b5be3e3 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -16,6 +16,7 @@
 
 #include "object_registry.h"
 
+#include "handle_scope-inl.h"
 #include "mirror/class.h"
 #include "scoped_thread_state_change.h"
 
@@ -48,12 +49,17 @@
     return 0;
   }
 
+  Thread* const self = Thread::Current();
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Object> obj_h(hs.NewHandle(o));
+
   // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock.
-  int32_t identity_hash_code = o->IdentityHashCode();
-  ScopedObjectAccessUnchecked soa(Thread::Current());
+  int32_t identity_hash_code = obj_h->IdentityHashCode();
+
+  ScopedObjectAccessUnchecked soa(self);
   MutexLock mu(soa.Self(), lock_);
   ObjectRegistryEntry* entry = nullptr;
-  if (ContainsLocked(soa.Self(), o, identity_hash_code, &entry)) {
+  if (ContainsLocked(soa.Self(), obj_h.Get(), identity_hash_code, &entry)) {
     // This object was already in our map.
     ++entry->reference_count;
   } else {
@@ -68,7 +74,7 @@
     // This object isn't in the registry yet, so add it.
     JNIEnv* env = soa.Env();
 
-    jobject local_reference = soa.AddLocalReference<jobject>(o);
+    jobject local_reference = soa.AddLocalReference<jobject>(obj_h.Get());
 
     entry->jni_reference_type = JNIWeakGlobalRefType;
     entry->jni_reference = env->NewWeakGlobalRef(local_reference);
@@ -82,17 +88,6 @@
   return entry->id;
 }
 
-bool ObjectRegistry::Contains(mirror::Object* o, ObjectRegistryEntry** out_entry) {
-  if (o == nullptr) {
-    return false;
-  }
-  // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock.
-  int32_t identity_hash_code = o->IdentityHashCode();
-  Thread* self = Thread::Current();
-  MutexLock mu(self, lock_);
-  return ContainsLocked(self, o, identity_hash_code, out_entry);
-}
-
 bool ObjectRegistry::ContainsLocked(Thread* self, mirror::Object* o, int32_t identity_hash_code,
                                     ObjectRegistryEntry** out_entry) {
   DCHECK(o != nullptr);
diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h
index bc3530b..4770a7d 100644
--- a/runtime/jdwp/object_registry.h
+++ b/runtime/jdwp/object_registry.h
@@ -72,10 +72,6 @@
     return reinterpret_cast<T>(InternalGet(id));
   }
 
-  bool Contains(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return Contains(o, nullptr);
-  }
-
   void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void DisableCollection(JDWP::ObjectId id)
@@ -114,9 +110,6 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
-  bool Contains(mirror::Object* o, ObjectRegistryEntry** out_entry)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
-
   bool ContainsLocked(Thread* self, mirror::Object* o, int32_t identity_hash_code,
                       ObjectRegistryEntry** out_entry)
       EXCLUSIVE_LOCKS_REQUIRED(lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index bdda5c8..938bf2c 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -437,9 +437,7 @@
   }
 
   void VisitRoots(RootCallback* visitor, void* arg) {
-    if (!class_loader_.IsNull()) {
-      class_loader_.VisitRoot(visitor, arg, 0, kRootVMInternal);
-    }
+    class_loader_.VisitRootIfNonNull(visitor, arg, RootInfo(kRootVMInternal));
   }
 
  private:
@@ -622,7 +620,7 @@
     ScopedObjectAccess soa(env);
     mirror::ArtMethod* m = soa.DecodeMethod(mid);
     CHECK(!kMovingMethods);
-    jobject art_method = soa.AddLocalReference<jobject>(m);
+    ScopedLocalRef<jobject> art_method(env, soa.AddLocalReference<jobject>(m));
     jobject reflect_method;
     if (m->IsConstructor()) {
       reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor);
@@ -633,7 +631,7 @@
       return nullptr;
     }
     SetObjectField(env, reflect_method,
-                   WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, art_method);
+                   WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, art_method.get());
     return reflect_method;
   }
 
@@ -641,13 +639,13 @@
     CHECK_NON_NULL_ARGUMENT(fid);
     ScopedObjectAccess soa(env);
     mirror::ArtField* f = soa.DecodeField(fid);
-    jobject art_field = soa.AddLocalReference<jobject>(f);
+    ScopedLocalRef<jobject> art_field(env, soa.AddLocalReference<jobject>(f));
     jobject reflect_field = env->AllocObject(WellKnownClasses::java_lang_reflect_Field);
     if (env->ExceptionCheck()) {
       return nullptr;
     }
     SetObjectField(env, reflect_field,
-                   WellKnownClasses::java_lang_reflect_Field_artField, art_field);
+                   WellKnownClasses::java_lang_reflect_Field_artField, art_field.get());
     return reflect_field;
   }
 
@@ -2528,7 +2526,9 @@
   }
 
   static jobjectRefType GetObjectRefType(JNIEnv* env, jobject java_object) {
-    CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNIInvalidRefType);
+    if (java_object == nullptr) {
+      return JNIInvalidRefType;
+    }
 
     // Do we definitely know what kind of reference this is?
     IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
@@ -3414,7 +3414,7 @@
   Thread* self = Thread::Current();
   {
     ReaderMutexLock mu(self, globals_lock);
-    globals.VisitRoots(callback, arg, 0, kRootJNIGlobal);
+    globals.VisitRoots(callback, arg, RootInfo(kRootJNIGlobal));
   }
   {
     MutexLock mu(self, libraries_lock);
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index bb46321..6bf14ce 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -376,6 +376,11 @@
   ASSERT_NE(fid, nullptr);
   // Turn the fid into a java.lang.reflect.Field...
   jobject field = env_->ToReflectedField(c, fid, JNI_FALSE);
+  for (size_t i = 0; i <= 512; ++i) {
+    // Regression test for b/18396311, ToReflectedField leaking local refs causing a local
+    // reference table overflows with 512 references to ArtField
+    env_->DeleteLocalRef(env_->ToReflectedField(c, fid, JNI_FALSE));
+  }
   ASSERT_NE(c, nullptr);
   ASSERT_TRUE(env_->IsInstanceOf(field, jlrField));
   // ...and back again.
@@ -407,6 +412,11 @@
   ASSERT_NE(mid, nullptr);
   // Turn the mid into a java.lang.reflect.Constructor...
   jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE);
+  for (size_t i = 0; i <= 512; ++i) {
+    // Regression test for b/18396311, ToReflectedMethod leaking local refs causing a local
+    // reference table overflows with 512 references to ArtMethod
+    env_->DeleteLocalRef(env_->ToReflectedMethod(c, mid, JNI_FALSE));
+  }
   ASSERT_NE(method, nullptr);
   ASSERT_TRUE(env_->IsInstanceOf(method, jlrConstructor));
   // ...and back again.
@@ -1013,10 +1023,11 @@
 
   // TODO: invoke a native method and test that its arguments are considered local references.
 
-  // Null as object should fail.
-  CheckJniAbortCatcher jni_abort_catcher;
+  // Null as pointer should not fail and return invalid-ref. b/18820997
   EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(nullptr));
-  jni_abort_catcher.Check("java_object == null");
+
+  // TODO: Null as reference should return the original type.
+  // This requires running a GC so a non-null object gets freed.
 }
 
 TEST_F(JniInternalTest, StaleWeakGlobal) {
diff --git a/runtime/method_helper-inl.h b/runtime/method_helper-inl.h
index 9af835f..15340df 100644
--- a/runtime/method_helper-inl.h
+++ b/runtime/method_helper-inl.h
@@ -64,7 +64,8 @@
 
 inline mirror::String* MethodHelper::ResolveString(uint32_t string_idx) {
   mirror::ArtMethod* method = GetMethod();
-  mirror::String* s = method->GetDexCacheStrings()->Get(string_idx);
+  mirror::Class* declaringClass = method->GetDeclaringClass();
+  mirror::String* s = declaringClass->GetDexCacheStrings()->Get(string_idx);
   if (UNLIKELY(s == nullptr)) {
     StackHandleScope<1> hs(Thread::Current());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 2c0ea36..6445245 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -166,9 +166,7 @@
 
 template<class T>
 inline void PrimitiveArray<T>::VisitRoots(RootCallback* callback, void* arg) {
-  if (!array_class_.IsNull()) {
-    array_class_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  array_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 template<typename T>
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7af88d6..75d8d91 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -50,7 +50,7 @@
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Array, length_));
   }
 
@@ -90,7 +90,7 @@
   // Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
   // returns false.
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  bool CheckIsValidIndex(int32_t index) ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE bool CheckIsValidIndex(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  protected:
   void ThrowArrayStoreException(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h
index 00bed92..336d9ee 100644
--- a/runtime/mirror/art_field-inl.h
+++ b/runtime/mirror/art_field-inl.h
@@ -25,6 +25,8 @@
 #include "jvalue.h"
 #include "object-inl.h"
 #include "primitive.h"
+#include "scoped_thread_state_change.h"
+#include "well_known_classes.h"
 
 namespace art {
 namespace mirror {
@@ -284,6 +286,14 @@
   return GetDexCache()->GetDexFile();
 }
 
+inline ArtField* ArtField::FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
+                                              jobject jlr_field) {
+  mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_reflect_Field_artField);
+  mirror::ArtField* field = f->GetObject(soa.Decode<mirror::Object*>(jlr_field))->AsArtField();
+  DCHECK(field != nullptr);
+  return field;
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 3c7c6ce..5a4ebd1 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -31,14 +31,6 @@
 // TODO: Get global references for these
 GcRoot<Class> ArtField::java_lang_reflect_ArtField_;
 
-ArtField* ArtField::FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
-                                       jobject jlr_field) {
-  mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_reflect_Field_artField);
-  mirror::ArtField* field = f->GetObject(soa.Decode<mirror::Object*>(jlr_field))->AsArtField();
-  DCHECK(field != nullptr);
-  return field;
-}
-
 void ArtField::SetClass(Class* java_lang_reflect_ArtField) {
   CHECK(java_lang_reflect_ArtField_.IsNull());
   CHECK(java_lang_reflect_ArtField != NULL);
@@ -64,9 +56,7 @@
 }
 
 void ArtField::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_reflect_ArtField_.IsNull()) {
-    java_lang_reflect_ArtField_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_reflect_ArtField_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 // TODO: we could speed up the search if fields are ordered by offsets.
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index f3dfa15..82d0a64 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -47,8 +47,8 @@
     return sizeof(ArtField);
   }
 
-  static ArtField* FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
-                                      jobject jlr_field)
+  ALWAYS_INLINE static ArtField* FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
+                                                    jobject jlr_field)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   Class* GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 9782dde..b28ea4d 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -69,6 +69,10 @@
   return GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_));
 }
 
+inline uint16_t ArtMethod::GetMethodIndexDuringLinking() {
+  return GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_));
+}
+
 inline uint32_t ArtMethod::GetDexMethodIndex() {
 #ifdef ART_SEA_IR_MODE
   // TODO: Re-add this check for (PORTABLE + SMALL + ) SEA IR when PORTABLE IS fixed!
@@ -79,11 +83,6 @@
   return GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_));
 }
 
-inline ObjectArray<String>* ArtMethod::GetDexCacheStrings() {
-  return GetFieldObject<ObjectArray<String>>(
-      OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_));
-}
-
 inline ObjectArray<ArtMethod>* ArtMethod::GetDexCacheResolvedMethods() {
   return GetFieldObject<ObjectArray<ArtMethod>>(
       OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_));
@@ -227,16 +226,16 @@
 }
 #endif
 
-inline const void* ArtMethod::GetQuickOatEntryPoint() {
+inline const void* ArtMethod::GetQuickOatEntryPoint(size_t pointer_size) {
   if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
     return nullptr;
   }
   Runtime* runtime = Runtime::Current();
-  const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+  const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this, pointer_size);
   // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
   // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
   // for non-native methods.
-  DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
+  DCHECK_NE(entry_point, runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
   if (UNLIKELY(entry_point == GetQuickToInterpreterBridge()) ||
       UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) {
     return nullptr;
@@ -244,21 +243,21 @@
   return entry_point;
 }
 
-inline const void* ArtMethod::GetQuickOatCodePointer() {
-  return EntryPointToCodePointer(GetQuickOatEntryPoint());
+inline const void* ArtMethod::GetQuickOatCodePointer(size_t pointer_size) {
+  return EntryPointToCodePointer(GetQuickOatEntryPoint(pointer_size));
 }
 
-inline const uint8_t* ArtMethod::GetMappingTable() {
-  const void* code_pointer = GetQuickOatCodePointer();
+inline const uint8_t* ArtMethod::GetMappingTable(size_t pointer_size) {
+  const void* code_pointer = GetQuickOatCodePointer(pointer_size);
   if (code_pointer == nullptr) {
     return nullptr;
   }
-  return GetMappingTable(code_pointer);
+  return GetMappingTable(code_pointer, pointer_size);
 }
 
-inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer) {
+inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer, size_t pointer_size) {
   DCHECK(code_pointer != nullptr);
-  DCHECK(code_pointer == GetQuickOatCodePointer());
+  DCHECK_EQ(code_pointer, GetQuickOatCodePointer(pointer_size));
   uint32_t offset =
       reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].mapping_table_offset_;
   if (UNLIKELY(offset == 0u)) {
@@ -267,17 +266,17 @@
   return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
 }
 
-inline const uint8_t* ArtMethod::GetVmapTable() {
-  const void* code_pointer = GetQuickOatCodePointer();
+inline const uint8_t* ArtMethod::GetVmapTable(size_t pointer_size) {
+  const void* code_pointer = GetQuickOatCodePointer(pointer_size);
   if (code_pointer == nullptr) {
     return nullptr;
   }
-  return GetVmapTable(code_pointer);
+  return GetVmapTable(code_pointer, pointer_size);
 }
 
-inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) {
+inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer, size_t pointer_size) {
   DCHECK(code_pointer != nullptr);
-  DCHECK(code_pointer == GetQuickOatCodePointer());
+  DCHECK_EQ(code_pointer, GetQuickOatCodePointer(pointer_size));
   uint32_t offset =
       reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_;
   if (UNLIKELY(offset == 0u)) {
@@ -286,14 +285,23 @@
   return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
 }
 
-inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) {
-  DCHECK(!Runtime::Current()->IsStarted());
-  SetNativeGcMap(reinterpret_cast<uint8_t*>(gc_map_offset));
+inline const uint8_t* ArtMethod::GetNativeGcMap(size_t pointer_size) {
+  const void* code_pointer = GetQuickOatCodePointer(pointer_size);
+  if (code_pointer == nullptr) {
+    return nullptr;
+  }
+  return GetNativeGcMap(code_pointer, pointer_size);
 }
 
-inline uint32_t ArtMethod::GetOatNativeGcMapOffset() {
-  DCHECK(!Runtime::Current()->IsStarted());
-  return PointerToLowMemUInt32(GetNativeGcMap());
+inline const uint8_t* ArtMethod::GetNativeGcMap(const void* code_pointer, size_t pointer_size) {
+  DCHECK(code_pointer != nullptr);
+  DCHECK_EQ(code_pointer, GetQuickOatCodePointer(pointer_size));
+  uint32_t offset =
+      reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].gc_map_offset_;
+  if (UNLIKELY(offset == 0u)) {
+    return nullptr;
+  }
+  return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
 }
 
 inline bool ArtMethod::IsRuntimeMethod() {
@@ -329,23 +337,26 @@
   return result;
 }
 
+
+inline bool ArtMethod::IsImtUnimplementedMethod() {
+  bool result = this == Runtime::Current()->GetImtUnimplementedMethod();
+  // Check that if we do think it is phony it looks like the imt unimplemented method.
+  DCHECK(!result || IsRuntimeMethod());
+  return result;
+}
+
 inline uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc) {
-  const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
+  const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*));
   return pc - reinterpret_cast<uintptr_t>(code);
 }
 
 inline uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc, const void* quick_entry_point) {
   DCHECK(quick_entry_point != GetQuickToInterpreterBridge());
-  DCHECK(quick_entry_point == Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this));
+  DCHECK_EQ(quick_entry_point,
+            Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*)));
   return pc - reinterpret_cast<uintptr_t>(quick_entry_point);
 }
 
-template<VerifyObjectFlags kVerifyFlags>
-inline void ArtMethod::SetNativeMethod(const void* native_method) {
-  SetFieldPtr<false, true, kVerifyFlags>(
-      OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), native_method);
-}
-
 inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() {
   if (UNLIKELY(IsPortableCompiled())) {
     // Portable compiled dex bytecode or jni stub.
@@ -363,7 +374,7 @@
     return runtime->GetRuntimeMethodFrameInfo(this);
   }
 
-  const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+  const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*));
   // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
   // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
   // for non-native methods. And we really shouldn't see a failure for non-native methods here.
@@ -394,12 +405,12 @@
 
 inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo(const void* code_pointer) {
   DCHECK(code_pointer != nullptr);
-  DCHECK_EQ(code_pointer, GetQuickOatCodePointer());
+  DCHECK_EQ(code_pointer, GetQuickOatCodePointer(sizeof(void*)));
   return reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].frame_info_;
 }
 
 inline const DexFile* ArtMethod::GetDexFile() {
-  return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache()->GetDexFile();
+  return GetDexCache()->GetDexFile();
 }
 
 inline const char* ArtMethod::GetDeclaringClassDescriptor() {
@@ -519,11 +530,15 @@
   return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache();
 }
 
+inline bool ArtMethod::IsProxyMethod() {
+  return GetDeclaringClass()->IsProxyClass();
+}
+
 inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy() {
-  mirror::Class* klass = GetDeclaringClass();
-  if (LIKELY(!klass->IsProxyClass())) {
+  if (LIKELY(!IsProxyMethod())) {
     return this;
   }
+  mirror::Class* klass = GetDeclaringClass();
   mirror::ArtMethod* interface_method = GetDexCacheResolvedMethods()->Get(GetDexMethodIndex());
   DCHECK(interface_method != nullptr);
   DCHECK_EQ(interface_method,
@@ -531,6 +546,27 @@
   return interface_method;
 }
 
+inline void ArtMethod::SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods) {
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
+                        new_dex_cache_methods);
+}
+
+inline void ArtMethod::SetDexCacheResolvedTypes(ObjectArray<Class>* new_dex_cache_classes) {
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
+                        new_dex_cache_classes);
+}
+
+inline void ArtMethod::CheckObjectSizeEqualsMirrorSize() {
+  // Using the default, check the class object size to make sure it matches the size of the
+  // object.
+  size_t this_size = sizeof(*this);
+#ifdef ART_METHOD_HAS_PADDING_FIELD_ON_64_BIT
+  this_size += sizeof(void*) - sizeof(uint32_t);
+#endif
+  DCHECK_EQ(GetClass()->GetObjectSize(), this_size);
+}
+
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 27499c2..afc1079 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -60,9 +60,7 @@
 
 
 void ArtMethod::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_reflect_ArtMethod_.IsNull()) {
-    java_lang_reflect_ArtMethod_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_reflect_ArtMethod_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 InvokeType ArtMethod::GetInvokeType() {
@@ -89,21 +87,6 @@
   java_lang_reflect_ArtMethod_ = GcRoot<Class>(nullptr);
 }
 
-void ArtMethod::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
-  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
-                        new_dex_cache_strings);
-}
-
-void ArtMethod::SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods) {
-  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
-                        new_dex_cache_methods);
-}
-
-void ArtMethod::SetDexCacheResolvedTypes(ObjectArray<Class>* new_dex_cache_classes) {
-  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
-                        new_dex_cache_classes);
-}
-
 size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
   CHECK_LE(1, shorty.length());
   uint32_t num_registers = 0;
@@ -118,10 +101,6 @@
   return num_registers;
 }
 
-bool ArtMethod::IsProxyMethod() {
-  return GetDeclaringClass()->IsProxyClass();
-}
-
 ArtMethod* ArtMethod::FindOverriddenMethod() {
   if (IsStatic()) {
     return NULL;
@@ -171,9 +150,9 @@
     // Portable doesn't use the machine pc, we just use dex pc instead.
     return static_cast<uint32_t>(pc);
   }
-  const void* entry_point = GetQuickOatEntryPoint();
-  MappingTable table(
-      entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr);
+  const void* entry_point = GetQuickOatEntryPoint(sizeof(void*));
+  MappingTable table(entry_point != nullptr ?
+      GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
   if (table.TotalSize() == 0) {
     // NOTE: Special methods (see Mir2Lir::GenSpecialCase()) have an empty mapping
     // but they have no suspend checks and, consequently, we never call ToDexPc() for them.
@@ -204,9 +183,9 @@
 }
 
 uintptr_t ArtMethod::ToNativePc(const uint32_t dex_pc) {
-  const void* entry_point = GetQuickOatEntryPoint();
-  MappingTable table(
-      entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr);
+  const void* entry_point = GetQuickOatEntryPoint(sizeof(void*));
+  MappingTable table(entry_point != nullptr ?
+      GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
   if (table.TotalSize() == 0) {
     DCHECK_EQ(dex_pc, 0U);
     return 0;   // Special no mapping/pc == 0 case
@@ -379,7 +358,7 @@
   if (is_fast) {
     SetAccessFlags(GetAccessFlags() | kAccFastNative);
   }
-  SetNativeMethod(native_method);
+  SetEntryPointFromJni(native_method);
 }
 
 void ArtMethod::UnregisterNative(Thread* self) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index abfdd42..2e26d9c 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -25,6 +25,7 @@
 #include "object_callbacks.h"
 #include "quick/quick_method_frame_info.h"
 #include "read_barrier_option.h"
+#include "stack.h"
 
 namespace art {
 
@@ -41,17 +42,14 @@
 typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh,
     const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result);
 
+#define ART_METHOD_HAS_PADDING_FIELD_ON_64_BIT
+
 // C++ mirror of java.lang.reflect.ArtMethod.
 class MANAGED ArtMethod FINAL : public Object {
  public:
   // Size of java.lang.reflect.ArtMethod.class.
   static uint32_t ClassSize();
 
-  // Size of an instance of java.lang.reflect.ArtMethod not including its value array.
-  static constexpr uint32_t InstanceSize() {
-    return sizeof(ArtMethod);
-  }
-
   static ArtMethod* FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
                                         jobject jlr_method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -64,7 +62,7 @@
     return MemberOffset(OFFSETOF_MEMBER(ArtMethod, declaring_class_));
   }
 
-  uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Not called within a transaction.
@@ -168,6 +166,9 @@
 
   uint16_t GetMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Doesn't do erroneous / unresolved class checks.
+  uint16_t GetMethodIndexDuringLinking() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   size_t GetVtableIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return GetMethodIndex();
   }
@@ -193,21 +194,13 @@
   // Number of 32bit registers that would be required to hold all the arguments
   static size_t NumArgRegisters(const StringPiece& shorty);
 
-  uint32_t GetDexMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE uint32_t GetDexMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetDexMethodIndex(uint32_t new_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Not called within a transaction.
     SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_), new_idx);
   }
 
-  ObjectArray<String>* GetDexCacheStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static MemberOffset DexCacheStringsOffset() {
-    return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_);
-  }
-
   static MemberOffset DexCacheResolvedMethodsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_);
   }
@@ -216,11 +209,11 @@
     return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_);
   }
 
-  ArtMethod* GetDexCacheResolvedMethod(uint16_t method_idx)
+  ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method)
+  ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods)
+  ALWAYS_INLINE void SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool HasDexCacheResolvedMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool HasSameDexCacheResolvedMethods(ArtMethod* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -245,51 +238,94 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   EntryPointFromInterpreter* GetEntryPointFromInterpreter()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetFieldPtr<EntryPointFromInterpreter*, kVerifyFlags>(
-        OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_));
+    CheckObjectSizeEqualsMirrorSize();
+    return GetEntryPointFromInterpreterPtrSize(sizeof(void*));
+  }
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  EntryPointFromInterpreter* GetEntryPointFromInterpreterPtrSize(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetFieldPtrWithSize<EntryPointFromInterpreter*, kVerifyFlags>(
+        EntryPointFromInterpreterOffset(pointer_size), pointer_size);
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetFieldPtr<false, true, kVerifyFlags>(
-        OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
-        entry_point_from_interpreter);
+    CheckObjectSizeEqualsMirrorSize();
+    SetEntryPointFromInterpreterPtrSize(entry_point_from_interpreter, sizeof(void*));
+  }
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  void SetEntryPointFromInterpreterPtrSize(EntryPointFromInterpreter* entry_point_from_interpreter,
+                                           size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    SetFieldPtrWithSize<false, true, kVerifyFlags>(
+        EntryPointFromInterpreterOffset(pointer_size), entry_point_from_interpreter, pointer_size);
   }
 
 #if defined(ART_USE_PORTABLE_COMPILER)
-  static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_portable_compiled_code_));
+  ALWAYS_INLINE static MemberOffset EntryPointFromPortableCompiledCodeOffset(size_t pointer_size) {
+    return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER(
+        PtrSizedFields, entry_point_from_portable_compiled_code_) / sizeof(void*) * pointer_size);
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  const void* GetEntryPointFromPortableCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetFieldPtr<const void*, kVerifyFlags>(
-        EntryPointFromPortableCompiledCodeOffset());
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  const void* GetEntryPointFromPortableCompiledCode()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CheckObjectSizeEqualsMirrorSize();
+    return GetEntryPointFromPortableCompiledCodePtrSize(sizeof(void*));
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE const void* GetEntryPointFromPortableCompiledCodePtrSize(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetFieldPtrWithSize<const void*, kVerifyFlags>(
+        EntryPointFromPortableCompiledCodeOffset(pointer_size), pointer_size);
+  }
+
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetFieldPtr<false, true, kVerifyFlags>(
-        EntryPointFromPortableCompiledCodeOffset(), entry_point_from_portable_compiled_code);
+    CheckObjectSizeEqualsMirrorSize();
+    return SetEntryPointFromPortableCompiledCodePtrSize(entry_point_from_portable_compiled_code,
+                                                        sizeof(void*));
+  }
+
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  void SetEntryPointFromPortableCompiledCodePtrSize(
+      const void* entry_point_from_portable_compiled_code, size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    SetFieldPtrWithSize<false, true, kVerifyFlags>(
+        EntryPointFromPortableCompiledCodeOffset(pointer_size),
+        entry_point_from_portable_compiled_code, pointer_size);
   }
 #endif
 
-  static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_quick_compiled_code_));
-  }
-
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   const void* GetEntryPointFromQuickCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetFieldPtr<const void*, kVerifyFlags>(EntryPointFromQuickCompiledCodeOffset());
+    CheckObjectSizeEqualsMirrorSize();
+    return GetEntryPointFromQuickCompiledCodePtrSize(sizeof(void*));
+  }
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE const void* GetEntryPointFromQuickCompiledCodePtrSize(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetFieldPtrWithSize<const void*, kVerifyFlags>(
+        EntryPointFromQuickCompiledCodeOffset(pointer_size), pointer_size);
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetFieldPtr<false, true, kVerifyFlags>(
-        EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code);
+    CheckObjectSizeEqualsMirrorSize();
+    SetEntryPointFromQuickCompiledCodePtrSize(entry_point_from_quick_compiled_code,
+                                              sizeof(void*));
+  }
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize(
+      const void* entry_point_from_quick_compiled_code, size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    SetFieldPtrWithSize<false, true, kVerifyFlags>(
+        EntryPointFromQuickCompiledCodeOffset(pointer_size), entry_point_from_quick_compiled_code,
+        pointer_size);
   }
 
   uint32_t GetCodeSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -318,38 +354,36 @@
   uint32_t GetQuickOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void SetQuickOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static const void* EntryPointToCodePointer(const void* entry_point) ALWAYS_INLINE {
+  ALWAYS_INLINE static const void* EntryPointToCodePointer(const void* entry_point) {
     uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
     code &= ~0x1;  // TODO: Make this Thumb2 specific.
     return reinterpret_cast<const void*>(code);
   }
 
   // Actual entry point pointer to compiled oat code or nullptr.
-  const void* GetQuickOatEntryPoint() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const void* GetQuickOatEntryPoint(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   // Actual pointer to compiled oat code or nullptr.
-  const void* GetQuickOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const void* GetQuickOatCodePointer(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Callers should wrap the uint8_t* in a MappingTable instance for convenient access.
-  const uint8_t* GetMappingTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const uint8_t* GetMappingTable(const void* code_pointer)
+  const uint8_t* GetMappingTable(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const uint8_t* GetMappingTable(const void* code_pointer, size_t pointer_size)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Callers should wrap the uint8_t* in a VmapTable instance for convenient access.
-  const uint8_t* GetVmapTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const uint8_t* GetVmapTable(const void* code_pointer)
+  const uint8_t* GetVmapTable(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const uint8_t* GetVmapTable(const void* code_pointer, size_t pointer_size)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_));
-  }
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  void SetNativeGcMap(const uint8_t* data) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetFieldPtr<false, true, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data);
-  }
-
-  // When building the oat need a convenient place to stuff the offset of the native GC map.
-  void SetOatNativeGcMapOffset(uint32_t gc_map_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  uint32_t GetOatNativeGcMapOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // Callers should wrap the uint8_t* in a GcMap instance for convenient access.
+  const uint8_t* GetNativeGcMap(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const uint8_t* GetNativeGcMap(const void* code_pointer, size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template <bool kCheckFrameSize = true>
   uint32_t GetFrameSizeInBytes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -374,8 +408,10 @@
     return frame_size_in_bytes - kPointerSize;
   }
 
-  size_t GetHandleScopeOffsetInBytes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return kPointerSize;
+  FrameOffset GetHandleScopeOffsetInBytes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    constexpr size_t handle_scope_offset = sizeof(StackReference<mirror::ArtMethod>);
+    DCHECK_LT(handle_scope_offset, GetFrameSizeInBytes());
+    return FrameOffset(handle_scope_offset);
   }
 
   void RegisterNative(Thread* self, const void* native_method, bool is_fast)
@@ -383,16 +419,41 @@
 
   void UnregisterNative(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static MemberOffset NativeMethodOffset() {
-    return OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_);
+  static MemberOffset EntryPointFromInterpreterOffset(size_t pointer_size) {
+    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
+        PtrSizedFields, entry_point_from_interpreter_) / sizeof(void*) * pointer_size);
   }
 
-  const void* GetNativeMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetFieldPtr<const void*>(NativeMethodOffset());
+  static MemberOffset EntryPointFromJniOffset(size_t pointer_size) {
+    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
+        PtrSizedFields, entry_point_from_jni_) / sizeof(void*) * pointer_size);
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  void SetNativeMethod(const void*) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static MemberOffset EntryPointFromQuickCompiledCodeOffset(size_t pointer_size) {
+    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
+        PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size);
+  }
+
+  void* GetEntryPointFromJni() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CheckObjectSizeEqualsMirrorSize();
+    return GetEntryPointFromJniPtrSize(sizeof(void*));
+  }
+  ALWAYS_INLINE void* GetEntryPointFromJniPtrSize(size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetFieldPtrWithSize<void*>(EntryPointFromJniOffset(pointer_size), pointer_size);
+  }
+
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  void SetEntryPointFromJni(const void* entrypoint) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CheckObjectSizeEqualsMirrorSize();
+    SetEntryPointFromJniPtrSize<kVerifyFlags>(entrypoint, sizeof(void*));
+  }
+  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    SetFieldPtrWithSize<false, true, kVerifyFlags>(
+        EntryPointFromJniOffset(pointer_size), entrypoint, pointer_size);
+  }
 
   static MemberOffset GetMethodIndexOffset() {
     return OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_);
@@ -409,6 +470,8 @@
 
   bool IsImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  bool IsImtUnimplementedMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   uintptr_t NativePcOffset(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   uintptr_t NativePcOffset(const uintptr_t pc, const void* quick_entry_point)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -479,6 +542,21 @@
 
   ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static size_t SizeWithoutPointerFields(size_t pointer_size) {
+    size_t total = sizeof(ArtMethod) - sizeof(PtrSizedFields);
+#ifdef ART_METHOD_HAS_PADDING_FIELD_ON_64_BIT
+    // Add 4 bytes if 64 bit, otherwise 0.
+    total += pointer_size - sizeof(uint32_t);
+#endif
+    return total;
+  }
+
+  // Size of an instance of java.lang.reflect.ArtMethod not including its value array.
+  static size_t InstanceSize(size_t pointer_size) {
+    return SizeWithoutPointerFields(pointer_size) +
+        (sizeof(PtrSizedFields) / sizeof(void*)) * pointer_size;
+  }
+
  protected:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   // The class we are a part of.
@@ -490,31 +568,6 @@
   // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
   HeapReference<ObjectArray<Class>> dex_cache_resolved_types_;
 
-  // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
-  HeapReference<ObjectArray<String>> dex_cache_strings_;
-
-  // Method dispatch from the interpreter invokes this pointer which may cause a bridge into
-  // compiled code.
-  uint64_t entry_point_from_interpreter_;
-
-  // Pointer to JNI function registered to this method, or a function to resolve the JNI function.
-  uint64_t entry_point_from_jni_;
-
-  // Method dispatch from portable compiled code invokes this pointer which may cause bridging into
-  // quick compiled code or the interpreter.
-#if defined(ART_USE_PORTABLE_COMPILER)
-  uint64_t entry_point_from_portable_compiled_code_;
-#endif
-
-  // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
-  // portable compiled code or the interpreter.
-  uint64_t entry_point_from_quick_compiled_code_;
-
-  // Pointer to a data structure created by the compiler and used by the garbage collector to
-  // determine which registers hold live references to objects within the heap. Keyed by native PC
-  // offsets for the quick compiler and dex PCs for the portable.
-  uint64_t gc_map_;
-
   // Access flags; low 16 bits are defined by spec.
   uint32_t access_flags_;
 
@@ -533,12 +586,47 @@
   // ifTable.
   uint32_t method_index_;
 
+  // Fake padding field gets inserted here.
+
+  // Must be the last fields in the method.
+  struct PACKED(4) PtrSizedFields {
+    // Method dispatch from the interpreter invokes this pointer which may cause a bridge into
+    // compiled code.
+    void* entry_point_from_interpreter_;
+
+    // Pointer to JNI function registered to this method, or a function to resolve the JNI function.
+    void* entry_point_from_jni_;
+
+    // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
+    // portable compiled code or the interpreter.
+    void* entry_point_from_quick_compiled_code_;
+
+    // Method dispatch from portable compiled code invokes this pointer which may cause bridging
+    // into quick compiled code or the interpreter. Last to simplify entrypoint logic.
+#if defined(ART_USE_PORTABLE_COMPILER)
+    void* entry_point_from_portable_compiled_code_;
+#endif
+  } ptr_sized_fields_;
+
   static GcRoot<Class> java_lang_reflect_ArtMethod_;
 
  private:
-  ObjectArray<ArtMethod>* GetDexCacheResolvedMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE void CheckObjectSizeEqualsMirrorSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ObjectArray<Class>* GetDexCacheResolvedTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ObjectArray<ArtMethod>* GetDexCacheResolvedMethods()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  ALWAYS_INLINE ObjectArray<Class>* GetDexCacheResolvedTypes()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static size_t PtrSizedFieldsOffset(size_t pointer_size) {
+    size_t offset = OFFSETOF_MEMBER(ArtMethod, ptr_sized_fields_);
+#ifdef ART_METHOD_HAS_PADDING_FIELD_ON_64_BIT
+    // Add 4 bytes if 64 bit, otherwise 0.
+    offset += pointer_size - sizeof(uint32_t);
+#endif
+    return offset;
+  }
 
   friend struct art::ArtMethodOffsets;  // for verifying offset information
   DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 8e44471..beb2f72 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -113,19 +113,19 @@
 template<VerifyObjectFlags kVerifyFlags>
 inline ArtMethod* Class::GetVirtualMethod(uint32_t i) {
   DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>());
-  return GetVirtualMethods()->Get(i);
+  return GetVirtualMethods()->GetWithoutChecks(i);
 }
 
 inline ArtMethod* Class::GetVirtualMethodDuringLinking(uint32_t i) {
   DCHECK(IsLoaded() || IsErroneous());
-  return GetVirtualMethods()->Get(i);
+  return GetVirtualMethods()->GetWithoutChecks(i);
 }
 
 inline void Class::SetVirtualMethod(uint32_t i, ArtMethod* f)  // TODO: uint16_t
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ObjectArray<ArtMethod>* virtual_methods =
       GetFieldObject<ObjectArray<ArtMethod>>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_));
-  virtual_methods->Set<false>(i, f);
+  virtual_methods->SetWithoutChecks<false>(i, f);
 }
 
 inline ObjectArray<ArtMethod>* Class::GetVTable() {
@@ -142,14 +142,6 @@
   SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable);
 }
 
-inline ObjectArray<ArtMethod>* Class::GetImTable() {
-  return GetFieldObject<ObjectArray<ArtMethod>>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_));
-}
-
-inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) {
-  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable);
-}
-
 inline ArtMethod* Class::GetEmbeddedImTableEntry(uint32_t i) {
   uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry);
   return GetFieldObject<mirror::ArtMethod>(MemberOffset(offset));
@@ -158,7 +150,6 @@
 inline void Class::SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) {
   uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry);
   SetFieldObject<false>(MemberOffset(offset), method);
-  CHECK(method == GetImTable()->Get(i));
 }
 
 inline bool Class::HasVTable() {
@@ -410,6 +401,36 @@
   return GetFieldObject<ObjectArray<ArtField>>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_));
 }
 
+inline MemberOffset Class::GetFirstReferenceInstanceFieldOffset() {
+  Class* super_class = GetSuperClass();
+  return (super_class != nullptr)
+      ? MemberOffset(RoundUp(super_class->GetObjectSize(),
+                             sizeof(mirror::HeapReference<mirror::Object>)))
+      : ClassOffset();
+}
+
+inline MemberOffset Class::GetFirstReferenceStaticFieldOffset() {
+  DCHECK(IsResolved());
+  uint32_t base = sizeof(mirror::Class);  // Static fields come after the class.
+  if (ShouldHaveEmbeddedImtAndVTable()) {
+    // Static fields come after the embedded tables.
+    base = mirror::Class::ComputeClassSize(true, GetEmbeddedVTableLength(),
+                                           0, 0, 0);
+  }
+  return MemberOffset(base);
+}
+
+inline MemberOffset Class::GetFirstReferenceStaticFieldOffsetDuringLinking() {
+  DCHECK(IsLoaded());
+  uint32_t base = sizeof(mirror::Class);  // Static fields come after the class.
+  if (ShouldHaveEmbeddedImtAndVTable()) {
+    // Static fields come after the embedded tables.
+    base = mirror::Class::ComputeClassSize(true, GetVTableDuringLinking()->GetLength(),
+                                           0, 0, 0);
+  }
+  return MemberOffset(base);
+}
+
 inline void Class::SetIFields(ObjectArray<ArtField>* new_ifields)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK(NULL == GetFieldObject<ObjectArray<ArtField>>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_)));
@@ -733,6 +754,32 @@
   }
 }
 
+inline uint32_t Class::NumDirectInterfaces() {
+  if (IsPrimitive()) {
+    return 0;
+  } else if (IsArrayClass()) {
+    return 2;
+  } else if (IsProxyClass()) {
+    mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces();
+    return interfaces != nullptr ? interfaces->GetLength() : 0;
+  } else {
+    const DexFile::TypeList* interfaces = GetInterfaceTypeList();
+    if (interfaces == nullptr) {
+      return 0;
+    } else {
+      return interfaces->Size();
+    }
+  }
+}
+
+inline void Class::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
+  SetFieldObject<false>(DexCacheStringsOffset(), new_dex_cache_strings);
+}
+
+inline ObjectArray<String>* Class::GetDexCacheStrings() {
+  return GetFieldObject<ObjectArray<String>>(DexCacheStringsOffset());
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index c10e8b1..f9791fa 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -52,9 +52,7 @@
 }
 
 void Class::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_Class_.IsNull()) {
-    java_lang_Class_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_Class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 void Class::SetStatus(Status new_status, Thread* self) {
@@ -149,6 +147,7 @@
 
 void Class::SetDexCache(DexCache* new_dex_cache) {
   SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache);
+  SetDexCacheStrings(new_dex_cache != nullptr ? new_dex_cache->GetStrings() : nullptr);
 }
 
 void Class::SetClassSize(uint32_t new_class_size) {
@@ -500,8 +499,9 @@
     for (size_t i = 0; i < NumVirtualMethods(); ++i) {
       ArtMethod* method = GetVirtualMethod(i);
       if (method->GetDexMethodIndex() == dex_method_idx &&
-          // A miranda method may have a different DexCache.
-          method->GetDexCache() == dex_cache) {
+          // A miranda method may have a different DexCache and is always created by linking,
+          // never *declared* in the class.
+          !method->IsMiranda()) {
         return method;
       }
     }
@@ -750,24 +750,6 @@
   return &GetDexFile().GetClassDef(class_def_idx);
 }
 
-uint32_t Class::NumDirectInterfaces() {
-  if (IsPrimitive()) {
-    return 0;
-  } else if (IsArrayClass()) {
-    return 2;
-  } else if (IsProxyClass()) {
-    mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces();
-    return interfaces != nullptr ? interfaces->GetLength() : 0;
-  } else {
-    const DexFile::TypeList* interfaces = GetInterfaceTypeList();
-    if (interfaces == nullptr) {
-      return 0;
-    } else {
-      return interfaces->Size();
-    }
-  }
-}
-
 uint16_t Class::GetDirectInterfaceTypeIdx(uint32_t idx) {
   DCHECK(!IsPrimitive());
   DCHECK(!IsArrayClass());
@@ -828,22 +810,21 @@
   return GetDexFile().GetInterfacesList(*class_def);
 }
 
-void Class::PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectArray<ArtMethod>* table = GetImTable();
-  if (table != nullptr) {
-    for (uint32_t i = 0; i < kImtSize; i++) {
-      SetEmbeddedImTableEntry(i, table->Get(i));
-    }
+void Class::PopulateEmbeddedImtAndVTable(StackHandleScope<kImtSize>* imt_handle_scope) {
+  for (uint32_t i = 0; i < kImtSize; i++) {
+    // Replace null with conflict.
+    mirror::Object* obj = imt_handle_scope->GetReference(i);
+    DCHECK(obj != nullptr);
+    SetEmbeddedImTableEntry(i, obj->AsArtMethod());
   }
 
-  table = GetVTableDuringLinking();
+  ObjectArray<ArtMethod>* table = GetVTableDuringLinking();
   CHECK(table != nullptr) << PrettyClass(this);
   SetEmbeddedVTableLength(table->GetLength());
   for (int32_t i = 0; i < table->GetLength(); i++) {
-    SetEmbeddedVTableEntry(i, table->Get(i));
+    SetEmbeddedVTableEntry(i, table->GetWithoutChecks(i));
   }
 
-  SetImTable(nullptr);
   // Keep java.lang.Object class's vtable around for since it's easier
   // to be reused by array classes during their linking.
   if (!IsObjectClass()) {
@@ -855,9 +836,10 @@
 class CopyClassVisitor {
  public:
   explicit CopyClassVisitor(Thread* self, Handle<mirror::Class>* orig,
-                            size_t new_length, size_t copy_bytes)
+                            size_t new_length, size_t copy_bytes,
+                            StackHandleScope<mirror::Class::kImtSize>* imt_handle_scope)
       : self_(self), orig_(orig), new_length_(new_length),
-        copy_bytes_(copy_bytes) {
+        copy_bytes_(copy_bytes), imt_handle_scope_(imt_handle_scope) {
   }
 
   void operator()(Object* obj, size_t usable_size) const
@@ -866,7 +848,7 @@
     mirror::Class* new_class_obj = obj->AsClass();
     mirror::Object::CopyObject(self_, new_class_obj, orig_->Get(), copy_bytes_);
     new_class_obj->SetStatus(Class::kStatusResolving, self_);
-    new_class_obj->PopulateEmbeddedImtAndVTable();
+    new_class_obj->PopulateEmbeddedImtAndVTable(imt_handle_scope_);
     new_class_obj->SetClassSize(new_length_);
   }
 
@@ -875,10 +857,12 @@
   Handle<mirror::Class>* const orig_;
   const size_t new_length_;
   const size_t copy_bytes_;
+  StackHandleScope<mirror::Class::kImtSize>* const imt_handle_scope_;
   DISALLOW_COPY_AND_ASSIGN(CopyClassVisitor);
 };
 
-Class* Class::CopyOf(Thread* self, int32_t new_length) {
+Class* Class::CopyOf(Thread* self, int32_t new_length,
+                     StackHandleScope<kImtSize>* imt_handle_scope) {
   DCHECK_GE(new_length, static_cast<int32_t>(sizeof(Class)));
   // We may get copied by a compacting GC.
   StackHandleScope<1> hs(self);
@@ -886,17 +870,15 @@
   gc::Heap* heap = Runtime::Current()->GetHeap();
   // The num_bytes (3rd param) is sizeof(Class) as opposed to SizeOf()
   // to skip copying the tail part that we will overwrite here.
-  CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class));
-
+  CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class), imt_handle_scope);
   mirror::Object* new_class =
       kMovingClasses
          ? heap->AllocObject<true>(self, java_lang_Class_.Read(), new_length, visitor)
          : heap->AllocNonMovableObject<true>(self, java_lang_Class_.Read(), new_length, visitor);
   if (UNLIKELY(new_class == nullptr)) {
     CHECK(self->IsExceptionPending());  // Expect an OOME.
-    return NULL;
+    return nullptr;
   }
-
   return new_class->AsClass();
 }
 
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 43ac98d..23df6c0 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -62,11 +62,16 @@
 #define CLASS_OFFSET_FROM_CLZ(rshift) \
     MemberOffset((static_cast<int>(rshift) * CLASS_OFFSET_ALIGNMENT))
 
+#ifndef IMT_SIZE
+#error IMT_SIZE not defined
+#endif
+
 namespace art {
 
 struct ClassOffsets;
 class Signature;
 class StringPiece;
+template<size_t kNumReferences> class PACKED(4) StackHandleScope;
 
 namespace mirror {
 
@@ -82,7 +87,7 @@
   // Interface method table size. Increasing this value reduces the chance of two interface methods
   // colliding in the interface method table but increases the size of classes that implement
   // (non-marker) interfaces.
-  static constexpr size_t kImtSize = 64;
+  static constexpr size_t kImtSize = IMT_SIZE;
 
   // imtable entry embedded in class object.
   struct MANAGED ImTableEntry {
@@ -215,46 +220,46 @@
   }
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns true if the class is an interface.
-  bool IsInterface() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsInterface() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccInterface) != 0;
   }
 
   // Returns true if the class is declared public.
-  bool IsPublic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsPublic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccPublic) != 0;
   }
 
   // Returns true if the class is declared final.
-  bool IsFinal() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsFinal() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccFinal) != 0;
   }
 
-  bool IsFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
   }
 
-  void SetFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE void SetFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
     SetAccessFlags(flags | kAccClassIsFinalizable);
   }
 
   // Returns true if the class is abstract.
-  bool IsAbstract() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsAbstract() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccAbstract) != 0;
   }
 
   // Returns true if the class is an annotation.
-  bool IsAnnotation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsAnnotation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccAnnotation) != 0;
   }
 
   // Returns true if the class is synthetic.
-  bool IsSynthetic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsSynthetic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccSynthetic) != 0;
   }
 
@@ -498,7 +503,7 @@
   // The size of java.lang.Class.class.
   static uint32_t ClassClassSize() {
     // The number of vtable entries in java.lang.Class.
-    uint32_t vtable_entries = Object::kVTableLength + 64;
+    uint32_t vtable_entries = Object::kVTableLength + 66;
     return ComputeClassSize(true, vtable_entries, 0, 1, 0);
   }
 
@@ -517,6 +522,13 @@
     return SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size);
   }
 
+  void SetObjectSizeWithoutChecks(uint32_t new_object_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Not called within a transaction.
+    return SetField32<false, false, kVerifyNone>(
+        OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size);
+  }
+
   // Returns true if this class is in the same packages as that class.
   bool IsInSamePackage(Class* that) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -582,7 +594,7 @@
   // downcast would be necessary. Similarly for interfaces, a class that implements (or an interface
   // that extends) another can be assigned to its parent, but not vice-versa. All Classes may assign
   // to themselves. Classes for primitive types may not assign to each other.
-  inline bool IsAssignableFrom(Class* src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ALWAYS_INLINE bool IsAssignableFrom(Class* src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(src != NULL);
     if (this == src) {
       // Can always assign to things of the same type.
@@ -599,7 +611,7 @@
     }
   }
 
-  Class* GetSuperClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE Class* GetSuperClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetSuperClass(Class *new_super_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Super class is assigned once, except during class linker initialization.
@@ -636,14 +648,16 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Also updates the dex_cache_strings_ variable from new_dex_cache.
   void SetDexCache(DexCache* new_dex_cache) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ObjectArray<ArtMethod>* GetDirectMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ObjectArray<ArtMethod>* GetDirectMethods()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetDirectMethods(ObjectArray<ArtMethod>* new_direct_methods)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ArtMethod* GetDirectMethod(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ArtMethod* GetDirectMethod(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetDirectMethod(uint32_t i, ArtMethod* f)  // TODO: uint16_t
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -652,13 +666,14 @@
   uint32_t NumDirectMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  ObjectArray<ArtMethod>* GetVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ObjectArray<ArtMethod>* GetVirtualMethods()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods)
+  ALWAYS_INLINE void SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns the number of non-inherited virtual methods.
-  uint32_t NumVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE uint32_t NumVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ArtMethod* GetVirtualMethod(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -668,9 +683,10 @@
   void SetVirtualMethod(uint32_t i, ArtMethod* f)  // TODO: uint16_t
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ObjectArray<ArtMethod>* GetVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ObjectArray<ArtMethod>* GetVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ObjectArray<ArtMethod>* GetVTableDuringLinking() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ObjectArray<ArtMethod>* GetVTableDuringLinking()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetVTable(ObjectArray<ArtMethod>* new_vtable)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -679,13 +695,6 @@
     return OFFSET_OF_OBJECT_MEMBER(Class, vtable_);
   }
 
-  void SetImTable(ObjectArray<ArtMethod>* new_imtable)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static MemberOffset ImTableOffset() {
-    return OFFSET_OF_OBJECT_MEMBER(Class, imtable_);
-  }
-
   static MemberOffset EmbeddedImTableOffset() {
     return MemberOffset(sizeof(Class));
   }
@@ -695,7 +704,7 @@
   }
 
   static MemberOffset EmbeddedVTableOffset() {
-    return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry) + sizeof(int32_t));
+    return MemberOffset(sizeof(Class) + kImtSize * sizeof(ImTableEntry) + sizeof(int32_t));
   }
 
   bool ShouldHaveEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -720,7 +729,8 @@
 
   void SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void PopulateEmbeddedImtAndVTable(StackHandleScope<kImtSize>* imt_handle_scope)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Given a method implemented by this class but potentially from a super class, return the
   // specific implementation method for this class.
@@ -788,11 +798,11 @@
 
   ArtMethod* FindClassInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  int32_t GetIfTableCount() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE int32_t GetIfTableCount() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  IfTable* GetIfTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE IfTable* GetIfTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void SetIfTable(IfTable* new_iftable) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE void SetIfTable(IfTable* new_iftable) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get instance fields of the class (See also GetSFields).
   ObjectArray<ArtField>* GetIFields() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -829,6 +839,9 @@
   void SetReferenceInstanceOffsets(uint32_t new_reference_offsets)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Get the offset of the first reference instance field. Other reference instance fields follow.
+  MemberOffset GetFirstReferenceInstanceFieldOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Returns the number of static fields containing reference types.
   uint32_t NumReferenceStaticFields() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(IsResolved() || IsErroneous());
@@ -845,6 +858,13 @@
     SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num);
   }
 
+  // Get the offset of the first reference static field. Other reference static fields follow.
+  MemberOffset GetFirstReferenceStaticFieldOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Get the offset of the first reference static field. Other reference static fields follow.
+  MemberOffset GetFirstReferenceStaticFieldOffsetDuringLinking()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Gets the static fields of the class.
   ObjectArray<ArtField>* GetSFields() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -968,7 +988,7 @@
 
   const DexFile::ClassDef* GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  uint32_t NumDirectInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE uint32_t NumDirectInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -987,7 +1007,7 @@
   void AssertInitializedOrInitializingInThread(Thread* self)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  Class* CopyOf(Thread* self, int32_t new_length)
+  Class* CopyOf(Thread* self, int32_t new_length, StackHandleScope<kImtSize>* imt_handle_scope)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // For proxy class only.
@@ -1002,6 +1022,13 @@
   bool GetSlowPathEnabled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void SetSlowPath(bool enabled) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  ObjectArray<String>* GetDexCacheStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static MemberOffset DexCacheStringsOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_);
+  }
+
   // Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore
   // fence.
   class InitializeClassVisitor {
@@ -1018,6 +1045,11 @@
     DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor);
   };
 
+  // Returns true if the class loader is null, ie the class loader is the boot strap class loader.
+  bool IsBootStrapClassLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetClassLoader() == nullptr;
+  }
+
  private:
   void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -1036,8 +1068,6 @@
 
   void CheckObjectAlloc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ObjectArray<ArtMethod>* GetImTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // defining class loader, or NULL for the "bootstrap" system loader
   HeapReference<ClassLoader> class_loader_;
 
@@ -1049,6 +1079,9 @@
   // runtime such as arrays and primitive classes).
   HeapReference<DexCache> dex_cache_;
 
+  // Short cuts to dex_cache_ member for fast compiled code access.
+  HeapReference<ObjectArray<String>> dex_cache_strings_;
+
   // static, private, and <init> methods
   HeapReference<ObjectArray<ArtMethod>> direct_methods_;
 
@@ -1077,9 +1110,6 @@
   // methods for the methods in the interface.
   HeapReference<IfTable> iftable_;
 
-  // Interface method table (imt), for quick "invoke-interface".
-  HeapReference<ObjectArray<ArtMethod>> imtable_;
-
   // Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
   HeapReference<String> name_;
 
@@ -1150,11 +1180,11 @@
 
   // The following data exist in real class objects.
   // Embedded Imtable, for class object that's not an interface, fixed size.
-  ImTableEntry embedded_imtable_[0];
+  // ImTableEntry embedded_imtable_[0];
   // Embedded Vtable, for class object that's not an interface, variable size.
-  VTableEntry embedded_vtable_[0];
+  // VTableEntry embedded_vtable_[0];
   // Static fields, variable size.
-  uint32_t fields_[0];
+  // uint32_t fields_[0];
 
   // java.lang.Class
   static GcRoot<Class> java_lang_Class_;
diff --git a/runtime/mirror/iftable-inl.h b/runtime/mirror/iftable-inl.h
index 3f20bf4..d1309d2 100644
--- a/runtime/mirror/iftable-inl.h
+++ b/runtime/mirror/iftable-inl.h
@@ -27,7 +27,7 @@
   DCHECK(interface->IsInterface());
   const size_t idx = i * kMax + kInterface;
   DCHECK_EQ(Get(idx), static_cast<Object*>(nullptr));
-  Set<false>(idx, interface);
+  SetWithoutChecks<false>(idx, interface);
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h
index 5feb602..4d899d2 100644
--- a/runtime/mirror/iftable.h
+++ b/runtime/mirror/iftable.h
@@ -25,13 +25,14 @@
 
 class MANAGED IfTable FINAL : public ObjectArray<Object> {
  public:
-  Class* GetInterface(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    Class* interface = Get((i * kMax) + kInterface)->AsClass();
+  ALWAYS_INLINE Class* GetInterface(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Class* interface = GetWithoutChecks((i * kMax) + kInterface)->AsClass();
     DCHECK(interface != NULL);
     return interface;
   }
 
-  void SetInterface(int32_t i, Class* interface) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE void SetInterface(int32_t i, Class* interface)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ObjectArray<ArtMethod>* GetMethodArray(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ObjectArray<ArtMethod>* method_array =
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 9dbfb56..7a383e4 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -402,8 +402,7 @@
   }
   DCHECK_GE(result, sizeof(Object))
       << " class=" << PrettyTypeOf(GetClass<kNewFlags, kReadBarrierOption>());
-  DCHECK(!(IsArtField<kNewFlags, kReadBarrierOption>())  || result == sizeof(ArtField));
-  DCHECK(!(IsArtMethod<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtMethod));
+  DCHECK(!(IsArtField<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtField));
   return result;
 }
 
@@ -765,13 +764,19 @@
         klass = kIsStatic ? nullptr : klass->GetSuperClass()) {
       size_t num_reference_fields =
           kIsStatic ? klass->NumReferenceStaticFields() : klass->NumReferenceInstanceFields();
+      if (num_reference_fields == 0u) {
+        continue;
+      }
+      MemberOffset field_offset = kIsStatic
+          ? klass->GetFirstReferenceStaticFieldOffset()
+          : klass->GetFirstReferenceInstanceFieldOffset();
       for (size_t i = 0; i < num_reference_fields; ++i) {
-        mirror::ArtField* field = kIsStatic ? klass->GetStaticField(i) : klass->GetInstanceField(i);
-        MemberOffset field_offset = field->GetOffset();
         // TODO: Do a simpler check?
         if (kVisitClass || field_offset.Uint32Value() != ClassOffset().Uint32Value()) {
           visitor(this, field_offset, kIsStatic);
         }
+        field_offset = MemberOffset(field_offset.Uint32Value() +
+                                    sizeof(mirror::HeapReference<mirror::Object>));
       }
     }
   }
@@ -811,7 +816,6 @@
     }
   }
 }
-
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index a1177d6..d2cc367 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -39,6 +39,8 @@
 namespace art {
 namespace mirror {
 
+Atomic<uint32_t> Object::hash_code_seed(987654321U + std::time(nullptr));
+
 class CopyReferenceFieldsWithReadBarrierVisitor {
  public:
   explicit CopyReferenceFieldsWithReadBarrierVisitor(Object* dest_obj)
@@ -135,17 +137,20 @@
   return copy;
 }
 
-int32_t Object::GenerateIdentityHashCode() {
-  static AtomicInteger seed(987654321 + std::time(nullptr));
-  int32_t expected_value, new_value;
+uint32_t Object::GenerateIdentityHashCode() {
+  uint32_t expected_value, new_value;
   do {
-    expected_value = static_cast<uint32_t>(seed.LoadRelaxed());
+    expected_value = hash_code_seed.LoadRelaxed();
     new_value = expected_value * 1103515245 + 12345;
-  } while ((expected_value & LockWord::kHashMask) == 0 ||
-      !seed.CompareExchangeWeakRelaxed(expected_value, new_value));
+  } while (!hash_code_seed.CompareExchangeWeakRelaxed(expected_value, new_value) ||
+      (expected_value & LockWord::kHashMask) == 0);
   return expected_value & LockWord::kHashMask;
 }
 
+void Object::SetHashCodeSeed(uint32_t new_seed) {
+  hash_code_seed.StoreRelaxed(new_seed);
+}
+
 int32_t Object::IdentityHashCode() const {
   mirror::Object* current_this = const_cast<mirror::Object*>(this);
   while (true) {
@@ -201,10 +206,11 @@
   for (Class* cur = c; cur != NULL; cur = cur->GetSuperClass()) {
     ObjectArray<ArtField>* fields = cur->GetIFields();
     if (fields != NULL) {
-      size_t num_ref_ifields = cur->NumReferenceInstanceFields();
-      for (size_t i = 0; i < num_ref_ifields; ++i) {
+      size_t num_ifields = fields->GetLength();
+      for (size_t i = 0; i < num_ifields; ++i) {
         ArtField* field = fields->Get(i);
         if (field->GetOffset().Int32Value() == field_offset.Int32Value()) {
+          CHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
           StackHandleScope<1> hs(Thread::Current());
           FieldHelper fh(hs.NewHandle(field));
           CHECK(fh.GetType()->IsAssignableFrom(new_value->GetClass()));
@@ -220,10 +226,11 @@
   if (IsClass()) {
     ObjectArray<ArtField>* fields = AsClass()->GetSFields();
     if (fields != NULL) {
-      size_t num_ref_sfields = AsClass()->NumReferenceStaticFields();
-      for (size_t i = 0; i < num_ref_sfields; ++i) {
+      size_t num_sfields = fields->GetLength();
+      for (size_t i = 0; i < num_sfields; ++i) {
         ArtField* field = fields->Get(i);
         if (field->GetOffset().Int32Value() == field_offset.Int32Value()) {
+          CHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
           StackHandleScope<1> hs(Thread::Current());
           FieldHelper fh(hs.NewHandle(field));
           CHECK(fh.GetType()->IsAssignableFrom(new_value->GetClass()));
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index a6b6227..bf76c86 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -316,15 +316,26 @@
       VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T>
   void SetFieldPtr(MemberOffset field_offset, T new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#ifndef __LP64__
-    SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>(
-        field_offset, reinterpret_cast<int32_t>(new_value));
-#else
-    SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>(
-        field_offset, reinterpret_cast<int64_t>(new_value));
-#endif
+    SetFieldPtrWithSize<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+        field_offset, new_value, sizeof(void*));
   }
 
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T>
+  ALWAYS_INLINE void SetFieldPtrWithSize(MemberOffset field_offset, T new_value,
+                                         size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK(pointer_size == 4 || pointer_size == 8) << pointer_size;
+    if (pointer_size == 4) {
+      intptr_t ptr  = reinterpret_cast<intptr_t>(new_value);
+      DCHECK_EQ(static_cast<int32_t>(ptr), ptr);  // Check that we dont lose any non 0 bits.
+      SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+          field_offset, static_cast<int32_t>(ptr));
+    } else {
+      SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+          field_offset, static_cast<int64_t>(reinterpret_cast<intptr_t>(new_value)));
+    }
+  }
   // TODO fix thread safety analysis broken by the use of template. This should be
   // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
   template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -332,16 +343,31 @@
   void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor)
       NO_THREAD_SAFETY_ANALYSIS;
 
+  // Used by object_test.
+  static void SetHashCodeSeed(uint32_t new_seed);
+  // Generate an identity hash code. Public for object test.
+  static uint32_t GenerateIdentityHashCode();
+
  protected:
   // Accessors for non-Java type fields
   template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
   T GetFieldPtr(MemberOffset field_offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#ifndef __LP64__
-    return reinterpret_cast<T>(GetField32<kVerifyFlags, kIsVolatile>(field_offset));
-#else
-    return reinterpret_cast<T>(GetField64<kVerifyFlags, kIsVolatile>(field_offset));
-#endif
+    return GetFieldPtrWithSize<T, kVerifyFlags, kIsVolatile>(field_offset, sizeof(void*));
+  }
+
+  template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE T GetFieldPtrWithSize(MemberOffset field_offset, size_t pointer_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK(pointer_size == 4 || pointer_size == 8) << pointer_size;
+    if (pointer_size == 4) {
+      return reinterpret_cast<T>(GetField32<kVerifyFlags, kIsVolatile>(field_offset));
+    } else {
+      int64_t v = GetField64<kVerifyFlags, kIsVolatile>(field_offset);
+      // Check that we dont lose any non 0 bits.
+      DCHECK_EQ(reinterpret_cast<int64_t>(reinterpret_cast<T>(v)), v);
+      return reinterpret_cast<T>(v);
+    }
   }
 
   // TODO: Fixme when anotatalysis works with visitors.
@@ -367,9 +393,6 @@
     }
   }
 
-  // Generate an identity hash code.
-  static int32_t GenerateIdentityHashCode();
-
   // A utility function that copies an object in a read barrier and
   // write barrier-aware way. This is internally used by Clone() and
   // Class::CopyOf().
@@ -377,6 +400,8 @@
                             size_t num_bytes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static Atomic<uint32_t> hash_code_seed;
+
   // The Class representing the type of the object.
   HeapReference<Class> klass_;
   // Monitor and hash code information.
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 7012b19..6404faf 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -45,11 +45,11 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool CheckAssignable(T* object) NO_THREAD_SAFETY_ANALYSIS;
 
-  void Set(int32_t i, T* object) ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE void Set(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   // TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
   template<bool kTransactionActive, bool kCheckTransaction = true,
       VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  void Set(int32_t i, T* object) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS;
+  ALWAYS_INLINE void Set(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
 
   // Set element without bound and element type checks, to be used in limited
   // circumstances, such as during boot image writing.
@@ -57,15 +57,15 @@
   // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
   template<bool kTransactionActive, bool kCheckTransaction = true,
       VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  void SetWithoutChecks(int32_t i, T* object) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS;
+  ALWAYS_INLINE void SetWithoutChecks(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
   // TODO fix thread safety analysis broken by the use of template. This should be
   // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
   template<bool kTransactionActive, bool kCheckTransaction = true,
       VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) ALWAYS_INLINE
+  ALWAYS_INLINE void SetWithoutChecksAndWriteBarrier(int32_t i, T* object)
       NO_THREAD_SAFETY_ANALYSIS;
 
-  T* GetWithoutChecks(int32_t i) ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE T* GetWithoutChecks(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Copy src into this array (dealing with overlaps as memmove does) without assignability checks.
   void AssignableMemmove(int32_t dst_pos, ObjectArray<T>* src, int32_t src_pos,
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index ede3b64..1e6378f 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -89,11 +89,18 @@
   EXPECT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value());
   EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value());
 
-  EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+  EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET,
+            ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
 #if defined(ART_USE_PORTABLE_COMPILER)
-  EXPECT_EQ(METHOD_PORTABLE_CODE_OFFSET, ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value());
+  EXPECT_EQ(METHOD_PORTABLE_CODE_OFFSET_32,
+            ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value());
+  EXPECT_EQ(METHOD_PORTABLE_CODE_OFFSET_64,
+            ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value());
 #endif
-  EXPECT_EQ(METHOD_QUICK_CODE_OFFSET, ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+  EXPECT_EQ(METHOD_QUICK_CODE_OFFSET_32,
+            ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value());
+  EXPECT_EQ(METHOD_QUICK_CODE_OFFSET_64,
+            ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value());
 }
 
 TEST_F(ObjectTest, IsInSamePackage) {
@@ -709,5 +716,14 @@
   // TODO: test that interfaces trump superclasses.
 }
 
+TEST_F(ObjectTest, IdentityHashCode) {
+  // Regression test for b/19046417 which had an infinite loop if the
+  // (seed & LockWord::kHashMask) == 0. seed 0 triggered the infinite loop since we did the check
+  // before the CAS which resulted in the same seed the next loop iteration.
+  mirror::Object::SetHashCodeSeed(0);
+  int32_t hash_code = mirror::Object::GenerateIdentityHashCode();
+  EXPECT_NE(hash_code, 0);
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc
index c36bd98..35130e8 100644
--- a/runtime/mirror/reference.cc
+++ b/runtime/mirror/reference.cc
@@ -33,9 +33,7 @@
 }
 
 void Reference::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_ref_Reference_.IsNull()) {
-    java_lang_ref_Reference_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_ref_Reference_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 1eb20f7..c2a67e8 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -68,9 +68,7 @@
 }
 
 void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_StackTraceElement_.IsNull()) {
-    java_lang_StackTraceElement_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_StackTraceElement_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 01599ae..e199d0e 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -224,9 +224,7 @@
 }
 
 void String::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_String_.IsNull()) {
-    java_lang_String_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_String_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 1320ab7..631c19b 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -111,6 +111,14 @@
 
   int32_t CompareTo(String* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Offset is only used during testing so use non-transactional mode.
+    DCHECK_LE(0, new_offset);
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset);
+  }
+
+  void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static Class* GetJavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(!java_lang_String_.IsNull());
     return java_lang_String_.Read();
@@ -136,21 +144,12 @@
     SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count);
   }
 
-  void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // Offset is only used during testing so use non-transactional mode.
-    DCHECK_LE(0, new_offset);
-    DCHECK_GE(GetLength(), new_offset);
-    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset);
-  }
-
   static String* Alloc(Thread* self, int32_t utf16_length)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static String* Alloc(Thread* self, Handle<CharArray> array)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   HeapReference<CharArray> array_;
 
@@ -163,6 +162,7 @@
   static GcRoot<Class> java_lang_String_;
 
   friend struct art::StringOffsets;  // for verifying offset information
+
   FRIEND_TEST(ObjectTest, StringLength);  // for SetOffset and SetCount
   DISALLOW_IMPLICIT_CONSTRUCTORS(String);
 };
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index 93ed4d4..61d85e2 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -138,9 +138,7 @@
 }
 
 void Throwable::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_Throwable_.IsNull()) {
-    java_lang_Throwable_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_Throwable_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 2bb59e2..3298b46 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -179,9 +179,9 @@
     return NULL;
   }
   const std::string descriptor(DotToDescriptor(class_name.c_str()));
-
+  const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
   for (const DexFile* dex_file : *dex_files) {
-    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str());
+    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
     if (dex_class_def != nullptr) {
       ScopedObjectAccess soa(env);
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -189,8 +189,8 @@
       StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ClassLoader> class_loader(
           hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
-      mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file,
-                                                        *dex_class_def);
+      mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,
+                                                        class_loader, *dex_file, *dex_class_def);
       if (result != nullptr) {
         VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
         return soa.AddLocalReference<jclass>(result);
@@ -294,7 +294,10 @@
                                                         nullptr,
                                                         false, &error_msg));
   if (oat_file.get() == nullptr) {
-    if (kReasonLogging) {
+    // Note that even though this is kDexoptNeeded, we use
+    // kVerboseLogging instead of the usual kReasonLogging since it is
+    // the common case on first boot and very spammy.
+    if (kVerboseLogging) {
       LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename
           << "' for file location '" << filename << "': " << error_msg;
     }
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index fc1018a..9fbb938 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -216,7 +216,7 @@
 typedef std::map<std::string, mirror::String*> StringTable;
 
 static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg,
-                                            uint32_t /*thread_id*/, RootType /*root_type*/)
+                                            const RootInfo& /*root_info*/)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   StringTable& table = *reinterpret_cast<StringTable*>(arg);
   mirror::String* string = const_cast<mirror::Object*>(*root)->AsString();
@@ -254,7 +254,7 @@
   if (class_name[1] == '\0') {
     klass = linker->FindPrimitiveClass(class_name[0]);
   } else {
-    klass = linker->LookupClass(class_name, NULL);
+    klass = linker->LookupClass(class_name, ComputeModifiedUtf8Hash(class_name), NULL);
   }
   if (klass == NULL) {
     return;
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a4ef839..5b41d5f 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -83,9 +83,15 @@
   }
   debug_flags &= ~DEBUG_ENABLE_DEBUGGER;
 
-  // These two are for backwards compatibility with Dalvik.
+  if ((debug_flags & DEBUG_ENABLE_SAFEMODE) != 0) {
+    // Ensure that any (secondary) oat files will be interpreted.
+    Runtime* runtime = Runtime::Current();
+    runtime->AddCompilerOption("--compiler-filter=interpret-only");
+    debug_flags &= ~DEBUG_ENABLE_SAFEMODE;
+  }
+
+  // This is for backwards compatibility with Dalvik.
   debug_flags &= ~DEBUG_ENABLE_ASSERT;
-  debug_flags &= ~DEBUG_ENABLE_SAFEMODE;
 
   if (debug_flags != 0) {
     LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags);
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 761e800..36b02dc 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -28,26 +28,28 @@
   ScopedFastNativeObjectAccess soa(env);
   mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
   ScopedUtfChars name(env, javaName);
-  if (name.c_str() == NULL) {
-    return NULL;
+  if (name.c_str() == nullptr) {
+    return nullptr;
   }
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   std::string descriptor(DotToDescriptor(name.c_str()));
-  mirror::Class* c = cl->LookupClass(descriptor.c_str(), loader);
-  if (c != NULL && c->IsResolved()) {
+  const size_t descriptor_hash = ComputeModifiedUtf8Hash(descriptor.c_str());
+  mirror::Class* c = cl->LookupClass(descriptor.c_str(), descriptor_hash, loader);
+  if (c != nullptr && c->IsResolved()) {
     return soa.AddLocalReference<jclass>(c);
   }
   if (loader != nullptr) {
     // Try the common case.
     StackHandleScope<1> hs(soa.Self());
-    c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), hs.NewHandle(loader));
+    c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash,
+                                       hs.NewHandle(loader));
     if (c != nullptr) {
       return soa.AddLocalReference<jclass>(c);
     }
   }
   // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
   // the regular loadClass code.
-  return NULL;
+  return nullptr;
 }
 
 static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 3903ffc..8850df2 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -23,19 +23,21 @@
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
-#include "reflection.h"
+#include "reflection-inl.h"
 #include "scoped_fast_native_object_access.h"
 
 namespace art {
 
-static bool VerifyFieldAccess(mirror::ArtField* field, mirror::Object* obj, bool is_set)
+template<bool kIsSet>
+ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::ArtField* field,
+                                                   mirror::Object* obj)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (field->IsFinal() && is_set) {
+  if (kIsSet && field->IsFinal()) {
     ThrowIllegalAccessException(nullptr, StringPrintf("Cannot set final field: %s",
                                                       PrettyField(field).c_str()).c_str());
     return false;
   }
-  if (!VerifyAccess(obj, field->GetDeclaringClass(), field->GetAccessFlags())) {
+  if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags())) {
     ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access field: %s",
                                                       PrettyField(field).c_str()).c_str());
     return false;
@@ -43,9 +45,10 @@
   return true;
 }
 
-static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Object* o,
-                          mirror::ArtField* f, Primitive::Type field_type, bool allow_references,
-                          JValue* value)
+template<bool kAllowReferences>
+ALWAYS_INLINE inline static bool GetFieldValue(
+    const ScopedFastNativeObjectAccess& soa, mirror::Object* o, mirror::ArtField* f,
+    Primitive::Type field_type, JValue* value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK_EQ(value->GetJ(), INT64_C(0));
   switch (field_type) {
@@ -74,7 +77,7 @@
       value->SetS(f->GetShort(o));
       return true;
     case Primitive::kPrimNot:
-      if (allow_references) {
+      if (kAllowReferences) {
         value->SetL(f->GetObject(o));
         return true;
       }
@@ -89,28 +92,29 @@
   return false;
 }
 
-static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, jobject j_rcvr,
-                          mirror::ArtField** f, mirror::Object** class_or_rcvr)
+ALWAYS_INLINE inline static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa,
+                                               jobject j_rcvr, mirror::ArtField** f,
+                                               mirror::Object** class_or_rcvr)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   soa.Self()->AssertThreadSuspensionIsAllowable();
+  mirror::Class* declaringClass = (*f)->GetDeclaringClass();
   if ((*f)->IsStatic()) {
-    StackHandleScope<2> hs(soa.Self());
-    HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(f));
-    Handle<mirror::Class> h_klass(hs.NewHandle((*f)->GetDeclaringClass()));
-    if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true))) {
-      DCHECK(soa.Self()->IsExceptionPending());
-      *class_or_rcvr = nullptr;
-      return false;
+    if (UNLIKELY(!declaringClass->IsInitialized())) {
+      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+      StackHandleScope<2> hs(soa.Self());
+      HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(f));
+      HandleWrapper<mirror::Class> h_klass(hs.NewHandleWrapper(&declaringClass));
+      if (UNLIKELY(!class_linker->EnsureInitialized(h_klass, true, true))) {
+        DCHECK(soa.Self()->IsExceptionPending());
+        return false;
+      }
     }
-    *class_or_rcvr = h_klass.Get();
+    *class_or_rcvr = declaringClass;
     return true;
   }
-
   *class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr);
-  mirror::Class* declaringClass = (*f)->GetDeclaringClass();
   if (!VerifyObjectIsClass(*class_or_rcvr, declaringClass)) {
     DCHECK(soa.Self()->IsExceptionPending());
-    *class_or_rcvr = nullptr;
     return false;
   }
   return true;
@@ -125,7 +129,7 @@
     return nullptr;
   }
   // If field is not set to be accessible, verify it can be accessed by the caller.
-  if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, false)) {
+  if ((accessible == JNI_FALSE) && !VerifyFieldAccess<false>(soa.Self(), f, o)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
@@ -133,15 +137,16 @@
   // Get the field's value, boxing if necessary.
   Primitive::Type field_type = f->GetTypeAsPrimitiveType();
   JValue value;
-  if (!GetFieldValue(soa, o, f, field_type, true, &value)) {
+  if (!GetFieldValue<true>(soa, o, f, field_type, &value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
   return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value));
 }
 
-static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
-                                char dst_descriptor, jboolean accessible) {
+template<Primitive::Type kPrimitiveType>
+ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaField,
+                                                     jobject javaObj, jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
   mirror::Object* o = nullptr;
@@ -151,7 +156,7 @@
   }
 
   // If field is not set to be accessible, verify it can be accessed by the caller.
-  if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, false)) {
+  if (accessible == JNI_FALSE && !VerifyFieldAccess<false>(soa.Self(), f, o)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return JValue();
   }
@@ -160,15 +165,22 @@
   // Read the value.
   Primitive::Type field_type = f->GetTypeAsPrimitiveType();
   JValue field_value;
-  if (!GetFieldValue(soa, o, f, field_type, false, &field_value)) {
+  if (field_type == kPrimitiveType) {
+    // This if statement should get optimized out since we only pass in valid primitive types.
+    if (UNLIKELY(!GetFieldValue<false>(soa, o, f, kPrimitiveType, &field_value))) {
+      DCHECK(soa.Self()->IsExceptionPending());
+      return JValue();
+    }
+    return field_value;
+  }
+  if (!GetFieldValue<false>(soa, o, f, field_type, &field_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return JValue();
   }
-
   // Widen it if necessary (and possible).
   JValue wide_value;
-  if (!ConvertPrimitiveValue(NULL, false, field_type, Primitive::GetType(dst_descriptor),
-                             field_value, &wide_value)) {
+  if (!ConvertPrimitiveValue(nullptr, false, field_type, kPrimitiveType, field_value,
+                             &wide_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return JValue();
   }
@@ -177,36 +189,36 @@
 
 static jboolean Field_getBoolean(JNIEnv* env, jobject javaField, jobject javaObj,
                                  jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'Z', accessible).GetZ();
+  return GetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj, accessible).GetZ();
 }
 
 static jbyte Field_getByte(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'B', accessible).GetB();
+  return GetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj, accessible).GetB();
 }
 
 static jchar Field_getChar(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'C', accessible).GetC();
+  return GetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj, accessible).GetC();
 }
 
 static jdouble Field_getDouble(JNIEnv* env, jobject javaField, jobject javaObj,
                                jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'D', accessible).GetD();
+  return GetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj, accessible).GetD();
 }
 
 static jfloat Field_getFloat(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'F', accessible).GetF();
+  return GetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj, accessible).GetF();
 }
 
 static jint Field_getInt(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'I', accessible).GetI();
+  return GetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj, accessible).GetI();
 }
 
 static jlong Field_getLong(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'J', accessible).GetJ();
+  return GetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj, accessible).GetJ();
 }
 
 static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
-  return GetPrimitiveField(env, javaField, javaObj, 'S', accessible).GetS();
+  return GetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, accessible).GetS();
 }
 
 static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o,
@@ -289,14 +301,15 @@
     return;
   }
   // If field is not set to be accessible, verify it can be accessed by the caller.
-  if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, true)) {
+  if ((accessible == JNI_FALSE) && !VerifyFieldAccess<true>(soa.Self(), f, o)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
   SetFieldValue(soa, o, f, field_prim_type, true, unboxed_value);
 }
 
-static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
+template<Primitive::Type kPrimitiveType>
+static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
                               const JValue& new_value, jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
@@ -313,14 +326,13 @@
 
   // Widen the value if necessary (and possible).
   JValue wide_value;
-  if (!ConvertPrimitiveValue(nullptr, false, Primitive::GetType(src_descriptor),
-                             field_type, new_value, &wide_value)) {
+  if (!ConvertPrimitiveValue(nullptr, false, kPrimitiveType, field_type, new_value, &wide_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
 
   // If field is not set to be accessible, verify it can be accessed by the caller.
-  if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, true)) {
+  if ((accessible == JNI_FALSE) && !VerifyFieldAccess<true>(soa.Self(), f, o)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
@@ -333,56 +345,56 @@
                              jboolean accessible) {
   JValue value;
   value.SetZ(z);
-  SetPrimitiveField(env, javaField, javaObj, 'Z', value, accessible);
+  SetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj, value, accessible);
 }
 
 static void Field_setByte(JNIEnv* env, jobject javaField, jobject javaObj, jbyte b,
                           jboolean accessible) {
   JValue value;
   value.SetB(b);
-  SetPrimitiveField(env, javaField, javaObj, 'B', value, accessible);
+  SetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj, value, accessible);
 }
 
 static void Field_setChar(JNIEnv* env, jobject javaField, jobject javaObj, jchar c,
                           jboolean accessible) {
   JValue value;
   value.SetC(c);
-  SetPrimitiveField(env, javaField, javaObj, 'C', value, accessible);
+  SetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj, value, accessible);
 }
 
 static void Field_setDouble(JNIEnv* env, jobject javaField, jobject javaObj, jdouble d,
                             jboolean accessible) {
   JValue value;
   value.SetD(d);
-  SetPrimitiveField(env, javaField, javaObj, 'D', value, accessible);
+  SetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj, value, accessible);
 }
 
 static void Field_setFloat(JNIEnv* env, jobject javaField, jobject javaObj, jfloat f,
                            jboolean accessible) {
   JValue value;
   value.SetF(f);
-  SetPrimitiveField(env, javaField, javaObj, 'F', value, accessible);
+  SetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj, value, accessible);
 }
 
 static void Field_setInt(JNIEnv* env, jobject javaField, jobject javaObj, jint i,
                          jboolean accessible) {
   JValue value;
   value.SetI(i);
-  SetPrimitiveField(env, javaField, javaObj, 'I', value, accessible);
+  SetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj, value, accessible);
 }
 
 static void Field_setLong(JNIEnv* env, jobject javaField, jobject javaObj, jlong j,
                           jboolean accessible) {
   JValue value;
   value.SetJ(j);
-  SetPrimitiveField(env, javaField, javaObj, 'J', value, accessible);
+  SetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj, value, accessible);
 }
 
 static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jshort s,
                            jboolean accessible) {
   JValue value;
   value.SetS(s);
-  SetPrimitiveField(env, javaField, javaObj, 'S', value, accessible);
+  SetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, value, accessible);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index cc44615..809381b 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -71,7 +71,7 @@
       if (count < method_count) {
         methods[count].name = m->GetName();
         methods[count].signature = m->GetShorty();
-        methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
+        methods[count].fnPtr = m->GetEntryPointFromJni();
         count++;
       } else {
         LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m);
@@ -84,7 +84,7 @@
       if (count < method_count) {
         methods[count].name = m->GetName();
         methods[count].signature = m->GetShorty();
-        methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
+        methods[count].fnPtr = m->GetEntryPointFromJni();
         count++;
       } else {
         LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m);
@@ -107,10 +107,11 @@
   GetMethodShorty, GetNativeMethodCount, GetNativeMethods
 };
 
-void LoadNativeBridge(std::string& native_bridge_library_filename) {
-  android::LoadNativeBridge(native_bridge_library_filename.c_str(), &native_bridge_art_callbacks_);
+bool LoadNativeBridge(std::string& native_bridge_library_filename) {
   VLOG(startup) << "Runtime::Setup native bridge library: "
       << (native_bridge_library_filename.empty() ? "(empty)" : native_bridge_library_filename);
+  return android::LoadNativeBridge(native_bridge_library_filename.c_str(),
+                                   &native_bridge_art_callbacks_);
 }
 
 void PreInitializeNativeBridge(std::string dir) {
@@ -118,7 +119,6 @@
 #ifndef __APPLE__  // Mac OS does not support CLONE_NEWNS.
   if (unshare(CLONE_NEWNS) == -1) {
     LOG(WARNING) << "Could not create mount namespace.";
-    return;
   }
   android::PreInitializeNativeBridge(dir.c_str(), GetInstructionSetString(kRuntimeISA));
 #endif
diff --git a/runtime/native_bridge_art_interface.h b/runtime/native_bridge_art_interface.h
index 42f0ed2..c287f3b 100644
--- a/runtime/native_bridge_art_interface.h
+++ b/runtime/native_bridge_art_interface.h
@@ -26,7 +26,7 @@
 // Mirror libnativebridge interface. Done to have the ART callbacks out of line, and not require
 // the system/core header file in other files.
 
-void LoadNativeBridge(std::string& native_bridge_library_filename);
+bool LoadNativeBridge(std::string& native_bridge_library_filename);
 
 // This is mostly for testing purposes, as in a full system this is called by Zygote code.
 void PreInitializeNativeBridge(std::string dir);
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 9d11de8..34c1362 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -23,7 +23,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '9', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '4', '5', '\0' };
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -491,35 +491,19 @@
   key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_);
 }
 
-OatMethodOffsets::OatMethodOffsets()
-  : code_offset_(0),
-    gc_map_offset_(0)
-{}
-
-OatMethodOffsets::OatMethodOffsets(uint32_t code_offset,
-                                   uint32_t gc_map_offset
-                                   )
-  : code_offset_(code_offset),
-    gc_map_offset_(gc_map_offset)
-{}
+OatMethodOffsets::OatMethodOffsets(uint32_t code_offset) : code_offset_(code_offset) {
+}
 
 OatMethodOffsets::~OatMethodOffsets() {}
 
-OatQuickMethodHeader::OatQuickMethodHeader()
-  : mapping_table_offset_(0),
-    vmap_table_offset_(0),
-    frame_info_(0, 0, 0),
-    code_size_(0)
-{}
-
 OatQuickMethodHeader::OatQuickMethodHeader(
-    uint32_t mapping_table_offset, uint32_t vmap_table_offset, uint32_t frame_size_in_bytes,
-    uint32_t core_spill_mask, uint32_t fp_spill_mask, uint32_t code_size)
-  : mapping_table_offset_(mapping_table_offset),
-    vmap_table_offset_(vmap_table_offset),
-    frame_info_(frame_size_in_bytes, core_spill_mask, fp_spill_mask),
-    code_size_(code_size)
-{}
+    uint32_t mapping_table_offset, uint32_t vmap_table_offset, uint32_t gc_map_offset,
+    uint32_t frame_size_in_bytes, uint32_t core_spill_mask, uint32_t fp_spill_mask,
+    uint32_t code_size)
+    : mapping_table_offset_(mapping_table_offset), vmap_table_offset_(vmap_table_offset),
+      gc_map_offset_(gc_map_offset),
+      frame_info_(frame_size_in_bytes, core_spill_mask, fp_spill_mask), code_size_(code_size) {
+}
 
 OatQuickMethodHeader::~OatQuickMethodHeader() {}
 
diff --git a/runtime/oat.h b/runtime/oat.h
index a5cb4bc..0534b1d 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -162,25 +162,20 @@
 
 class PACKED(4) OatMethodOffsets {
  public:
-  OatMethodOffsets();
-
-  OatMethodOffsets(uint32_t code_offset,
-                   uint32_t gc_map_offset);
+  OatMethodOffsets(uint32_t code_offset = 0);
 
   ~OatMethodOffsets();
 
   uint32_t code_offset_;
-  uint32_t gc_map_offset_;
 };
 
 // OatQuickMethodHeader precedes the raw code chunk generated by the Quick compiler.
 class PACKED(4) OatQuickMethodHeader {
  public:
-  OatQuickMethodHeader();
-
-  explicit OatQuickMethodHeader(uint32_t mapping_table_offset, uint32_t vmap_table_offset,
-                                uint32_t frame_size_in_bytes, uint32_t core_spill_mask,
-                                uint32_t fp_spill_mask, uint32_t code_size);
+  OatQuickMethodHeader(uint32_t mapping_table_offset = 0U, uint32_t vmap_table_offset = 0U,
+                       uint32_t gc_map_offset = 0U, uint32_t frame_size_in_bytes = 0U,
+                       uint32_t core_spill_mask = 0U, uint32_t fp_spill_mask = 0U,
+                       uint32_t code_size = 0U);
 
   ~OatQuickMethodHeader();
 
@@ -188,6 +183,8 @@
   uint32_t mapping_table_offset_;
   // The offset in bytes from the start of the vmap table to the end of the header.
   uint32_t vmap_table_offset_;
+  // The offset in bytes from the start of the gc map to the end of the header.
+  uint32_t gc_map_offset_;
   // The stack frame information.
   QuickMethodFrameInfo frame_info_;
   // The code size in bytes.
diff --git a/runtime/oat_file-inl.h b/runtime/oat_file-inl.h
index 9570bb5..a0cec03 100644
--- a/runtime/oat_file-inl.h
+++ b/runtime/oat_file-inl.h
@@ -78,6 +78,31 @@
   return reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].frame_info_.FpSpillMask();
 }
 
+const uint8_t* OatFile::OatMethod::GetGcMap() const {
+  const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode());
+  if (code == nullptr) {
+    return nullptr;
+  }
+  uint32_t offset = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].gc_map_offset_;
+  if (UNLIKELY(offset == 0u)) {
+    return nullptr;
+  }
+  return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
+uint32_t OatFile::OatMethod::GetGcMapOffset() const {
+  const uint8_t* gc_map = GetGcMap();
+  return static_cast<uint32_t>(gc_map != nullptr ? gc_map - begin_ : 0u);
+}
+
+uint32_t OatFile::OatMethod::GetGcMapOffsetOffset() const {
+  const OatQuickMethodHeader* method_header = GetOatQuickMethodHeader();
+  if (method_header == nullptr) {
+    return 0u;
+  }
+  return reinterpret_cast<const byte*>(&method_header->gc_map_offset_) - begin_;
+}
+
 inline uint32_t OatFile::OatMethod::GetMappingTableOffset() const {
   const uint8_t* mapping_table = GetMappingTable();
   return static_cast<uint32_t>(mapping_table != nullptr ? mapping_table - begin_ : 0u);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 9ba860c..8bf7ad4 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -458,7 +458,7 @@
 
 const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
   return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
-                       dex_file_location_checksum_, error_msg);
+                       dex_file_location_checksum_, GetOatFile(), error_msg);
 }
 
 uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
@@ -572,27 +572,22 @@
 const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) const {
   const OatMethodOffsets* oat_method_offsets = GetOatMethodOffsets(method_index);
   if (oat_method_offsets == nullptr) {
-    return OatMethod(nullptr, 0, 0);
+    return OatMethod(nullptr, 0);
   }
   if (oat_file_->IsExecutable() ||
       Runtime::Current() == nullptr ||        // This case applies for oatdump.
       Runtime::Current()->IsCompiler()) {
-    return OatMethod(
-        oat_file_->Begin(),
-        oat_method_offsets->code_offset_,
-        oat_method_offsets->gc_map_offset_);
+    return OatMethod(oat_file_->Begin(), oat_method_offsets->code_offset_);
   } else {
     // We aren't allowed to use the compiled code. We just force it down the interpreted version.
-    return OatMethod(oat_file_->Begin(), 0, 0);
+    return OatMethod(oat_file_->Begin(), 0);
   }
 }
 
 OatFile::OatMethod::OatMethod(const byte* base,
-                              const uint32_t code_offset,
-                              const uint32_t gc_map_offset)
+                              const uint32_t code_offset)
   : begin_(base),
-    code_offset_(code_offset),
-    native_gc_map_offset_(gc_map_offset) {
+    code_offset_(code_offset) {
 }
 
 OatFile::OatMethod::~OatMethod() {}
@@ -603,7 +598,6 @@
   method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
 #endif
   method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
-  method->SetNativeGcMap(GetNativeGcMap());
 }
 
 bool OatFile::IsPic() const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 488988e..5d92a05 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -96,9 +96,6 @@
     uint32_t GetCodeOffset() const {
       return code_offset_;
     }
-    uint32_t GetNativeGcMapOffset() const {
-      return native_gc_map_offset_;
-    }
 
     const void* GetPortableCode() const {
       // TODO: encode whether code is portable/quick in flags within OatMethod.
@@ -134,10 +131,6 @@
     const OatQuickMethodHeader* GetOatQuickMethodHeader() const;
     uint32_t GetOatQuickMethodHeaderOffset() const;
 
-    const uint8_t* GetNativeGcMap() const {
-      return GetOatPointer<const uint8_t*>(native_gc_map_offset_);
-    }
-
     size_t GetFrameSizeInBytes() const;
     uint32_t GetCoreSpillMask() const;
     uint32_t GetFpSpillMask() const;
@@ -150,12 +143,14 @@
     uint32_t GetVmapTableOffset() const;
     uint32_t GetVmapTableOffsetOffset() const;
 
+    const uint8_t* GetGcMap() const;
+    uint32_t GetGcMapOffset() const;
+    uint32_t GetGcMapOffsetOffset() const;
+
     ~OatMethod();
 
     // Create an OatMethod with offsets relative to the given base address
-    OatMethod(const byte* base,
-              const uint32_t code_offset,
-              const uint32_t gc_map_offset);
+    OatMethod(const byte* base, const uint32_t code_offset);
 
     OatMethod() {}
 
@@ -171,7 +166,6 @@
     const byte* begin_;
 
     uint32_t code_offset_;
-    uint32_t native_gc_map_offset_;
 
     friend class OatClass;
   };
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 592deed..cf81cc5 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -35,34 +35,10 @@
 }  // namespace mirror
 class StackVisitor;
 
-enum RootType {
-  kRootUnknown = 0,
-  kRootJNIGlobal,
-  kRootJNILocal,
-  kRootJavaFrame,
-  kRootNativeStack,
-  kRootStickyClass,
-  kRootThreadBlock,
-  kRootMonitorUsed,
-  kRootThreadObject,
-  kRootInternedString,
-  kRootDebugger,
-  kRootVMInternal,
-  kRootJNIMonitor,
-};
-std::ostream& operator<<(std::ostream& os, const RootType& root_type);
-
-// Returns the new address of the object, returns root if it has not moved. tid and root_type are
-// only used by hprof.
-typedef void (RootCallback)(mirror::Object** root, void* arg, uint32_t thread_id,
-    RootType root_type);
 // A callback for visiting an object in the heap.
 typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
 // A callback used for marking an object, returns the new address of the object if the object moved.
 typedef mirror::Object* (MarkObjectCallback)(mirror::Object* obj, void* arg) WARN_UNUSED;
-// A callback for verifying roots.
-typedef void (VerifyRootCallback)(const mirror::Object* root, void* arg, size_t vreg,
-    const StackVisitor* visitor, RootType root_type);
 
 typedef void (MarkHeapReferenceCallback)(mirror::HeapReference<mirror::Object>* ref, void* arg);
 typedef void (DelayReferenceReferentCallback)(mirror::Class* klass, mirror::Reference* ref, void* arg);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index bb2ad44..6fd8814 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -200,9 +200,8 @@
 #else
 #error "ART default GC type must be set"
 #endif
-  // If we are using homogeneous space compaction then default background compaction to off since
-  // homogeneous space compactions when we transition to not jank perceptible.
-  use_homogeneous_space_compaction_for_oom_ = false;
+  // Enable hspace compaction on OOM by default.
+  use_homogeneous_space_compaction_for_oom_ = true;
   // If background_collector_type_ is kCollectorTypeNone, it defaults to the collector_type_ after
   // parsing options. If you set this to kCollectorTypeHSpaceCompact then we will do an hspace
   // compaction when we transition to background instead of a normal collector transition.
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 98eeda7..5b1bd78 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -203,17 +203,18 @@
     CHECK(code_item != nullptr);
     uint16_t num_regs = code_item->registers_size_;
     uint32_t dex_pc = GetDexPc();
-    const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
-    uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
-    ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
-    StackHandleScope<2> hs(self_);
+    StackHandleScope<3> hs(self_);  // Dex cache, class loader and method.
     mirror::Class* declaring_class = m->GetDeclaringClass();
     Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
     Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+    Handle<mirror::ArtMethod> h_method(hs.NewHandle(m));
     verifier::MethodVerifier verifier(h_dex_cache->GetDexFile(), &h_dex_cache, &h_class_loader,
                                       &m->GetClassDef(), code_item, m->GetDexMethodIndex(), m,
-                                      m->GetAccessFlags(), false, true, true);
-    verifier.Verify();
+                                      m->GetAccessFlags(), true, true, true);
+    bool verifier_success = verifier.Verify();
+    CHECK(verifier_success) << PrettyMethod(h_method.Get());
+    ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, h_method.Get(), dex_pc);
+    self_->SetShadowFrameUnderConstruction(new_frame);
     const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
     for (uint16_t reg = 0; reg < num_regs; ++reg) {
       VRegKind kind = GetVRegKind(reg, kinds);
@@ -226,40 +227,41 @@
           break;
         case kReferenceVReg:
           new_frame->SetVRegReference(reg,
-                                      reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
+                                      reinterpret_cast<mirror::Object*>(GetVReg(h_method.Get(),
+                                                                                reg, kind)));
           break;
         case kLongLoVReg:
-          if (GetVRegKind(reg + 1, kinds), kLongHiVReg) {
+          if (GetVRegKind(reg + 1, kinds) == kLongHiVReg) {
             // Treat it as a "long" register pair.
-            new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg));
+            new_frame->SetVRegLong(reg, GetVRegPair(h_method.Get(), reg, kLongLoVReg, kLongHiVReg));
           } else {
-            new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+            new_frame->SetVReg(reg, GetVReg(h_method.Get(), reg, kind));
           }
           break;
         case kLongHiVReg:
-          if (GetVRegKind(reg - 1, kinds), kLongLoVReg) {
+          if (GetVRegKind(reg - 1, kinds) == kLongLoVReg) {
             // Nothing to do: we treated it as a "long" register pair.
           } else {
-            new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+            new_frame->SetVReg(reg, GetVReg(h_method.Get(), reg, kind));
           }
           break;
         case kDoubleLoVReg:
-          if (GetVRegKind(reg + 1, kinds), kDoubleHiVReg) {
+          if (GetVRegKind(reg + 1, kinds) == kDoubleHiVReg) {
             // Treat it as a "double" register pair.
-            new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg));
+            new_frame->SetVRegLong(reg, GetVRegPair(h_method.Get(), reg, kDoubleLoVReg, kDoubleHiVReg));
           } else {
-            new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+            new_frame->SetVReg(reg, GetVReg(h_method.Get(), reg, kind));
           }
           break;
         case kDoubleHiVReg:
-          if (GetVRegKind(reg - 1, kinds), kDoubleLoVReg) {
+          if (GetVRegKind(reg - 1, kinds) == kDoubleLoVReg) {
             // Nothing to do: we treated it as a "double" register pair.
           } else {
-            new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+            new_frame->SetVReg(reg, GetVReg(h_method.Get(), reg, kind));
           }
           break;
         default:
-          new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+          new_frame->SetVReg(reg, GetVReg(h_method.Get(), reg, kind));
           break;
       }
     }
@@ -268,6 +270,7 @@
     } else {
       self_->SetDeoptimizationShadowFrame(new_frame);
     }
+    self_->ClearShadowFrameUnderConstruction();
     prev_shadow_frame_ = new_frame;
     return true;
   }
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 70aba9b..179110c 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -89,7 +89,7 @@
 
     // Sort by class...
     if (obj1->GetClass() != obj2->GetClass()) {
-      return obj1->GetClass()->IdentityHashCode() < obj2->IdentityHashCode();
+      return obj1->GetClass()->IdentityHashCode() < obj2->GetClass()->IdentityHashCode();
     } else {
       // ...then by size...
       size_t count1 = obj1->SizeOf();
@@ -242,10 +242,9 @@
                   identical, equiv);
 }
 
-void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, uint32_t tid,
-                                RootType root_type) {
+void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info) {
   for (GcRoot<mirror::Object>& root : entries_) {
-    root.VisitRoot(visitor, arg, tid, root_type);
+    root.VisitRoot(visitor, arg, root_info);
   }
 }
 
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 6cffa85..22cf1cd 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -49,7 +49,7 @@
 
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VisitRoots(RootCallback* visitor, void* arg, uint32_t tid, RootType root_type);
+  void VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info);
 
  private:
   typedef std::vector<GcRoot<mirror::Object>,
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
new file mode 100644
index 0000000..be4d560
--- /dev/null
+++ b/runtime/reflection-inl.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 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_REFLECTION_INL_H_
+#define ART_RUNTIME_REFLECTION_INL_H_
+
+#include "reflection.h"
+
+#include "base/stringprintf.h"
+#include "common_throws.h"
+#include "jvalue.h"
+#include "primitive.h"
+#include "utils.h"
+
+namespace art {
+
+inline bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+                                  Primitive::Type srcType, Primitive::Type dstType,
+                                  const JValue& src, JValue* dst) {
+  DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
+  if (LIKELY(srcType == dstType)) {
+    dst->SetJ(src.GetJ());
+    return true;
+  }
+  switch (dstType) {
+  case Primitive::kPrimBoolean:  // Fall-through.
+  case Primitive::kPrimChar:  // Fall-through.
+  case Primitive::kPrimByte:
+    // Only expect assignment with source and destination of identical type.
+    break;
+  case Primitive::kPrimShort:
+    if (srcType == Primitive::kPrimByte) {
+      dst->SetS(src.GetI());
+      return true;
+    }
+    break;
+  case Primitive::kPrimInt:
+    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+        srcType == Primitive::kPrimShort) {
+      dst->SetI(src.GetI());
+      return true;
+    }
+    break;
+  case Primitive::kPrimLong:
+    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+        srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
+      dst->SetJ(src.GetI());
+      return true;
+    }
+    break;
+  case Primitive::kPrimFloat:
+    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+        srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
+      dst->SetF(src.GetI());
+      return true;
+    } else if (srcType == Primitive::kPrimLong) {
+      dst->SetF(src.GetJ());
+      return true;
+    }
+    break;
+  case Primitive::kPrimDouble:
+    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+        srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
+      dst->SetD(src.GetI());
+      return true;
+    } else if (srcType == Primitive::kPrimLong) {
+      dst->SetD(src.GetJ());
+      return true;
+    } else if (srcType == Primitive::kPrimFloat) {
+      dst->SetD(src.GetF());
+      return true;
+    }
+    break;
+  default:
+    break;
+  }
+  if (!unbox_for_result) {
+    ThrowIllegalArgumentException(throw_location,
+                                  StringPrintf("Invalid primitive conversion from %s to %s",
+                                               PrettyDescriptor(srcType).c_str(),
+                                               PrettyDescriptor(dstType).c_str()).c_str());
+  } else {
+    ThrowClassCastException(throw_location,
+                            StringPrintf("Couldn't convert result of type %s to %s",
+                                         PrettyDescriptor(srcType).c_str(),
+                                         PrettyDescriptor(dstType).c_str()).c_str());
+  }
+  return false;
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_REFLECTION_INL_H_
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 4cd61a5..18fcee4 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "reflection.h"
+#include "reflection-inl.h"
 
 #include "class_linker.h"
 #include "common_throws.h"
@@ -565,7 +565,7 @@
   }
 
   // If method is not set to be accessible, verify it can be accessed by the caller.
-  if (!accessible && !VerifyAccess(receiver, declaring_class, m->GetAccessFlags())) {
+  if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags())) {
     ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access method: %s",
                                                       PrettyMethod(m).c_str()).c_str());
     return nullptr;
@@ -617,84 +617,6 @@
   return true;
 }
 
-static std::string PrettyDescriptor(Primitive::Type type) {
-  return PrettyDescriptor(Primitive::Descriptor(type));
-}
-
-bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
-                           Primitive::Type srcType, Primitive::Type dstType,
-                           const JValue& src, JValue* dst) {
-  DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
-  if (LIKELY(srcType == dstType)) {
-    dst->SetJ(src.GetJ());
-    return true;
-  }
-  switch (dstType) {
-  case Primitive::kPrimBoolean:  // Fall-through.
-  case Primitive::kPrimChar:  // Fall-through.
-  case Primitive::kPrimByte:
-    // Only expect assignment with source and destination of identical type.
-    break;
-  case Primitive::kPrimShort:
-    if (srcType == Primitive::kPrimByte) {
-      dst->SetS(src.GetI());
-      return true;
-    }
-    break;
-  case Primitive::kPrimInt:
-    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
-        srcType == Primitive::kPrimShort) {
-      dst->SetI(src.GetI());
-      return true;
-    }
-    break;
-  case Primitive::kPrimLong:
-    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
-        srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
-      dst->SetJ(src.GetI());
-      return true;
-    }
-    break;
-  case Primitive::kPrimFloat:
-    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
-        srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
-      dst->SetF(src.GetI());
-      return true;
-    } else if (srcType == Primitive::kPrimLong) {
-      dst->SetF(src.GetJ());
-      return true;
-    }
-    break;
-  case Primitive::kPrimDouble:
-    if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
-        srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
-      dst->SetD(src.GetI());
-      return true;
-    } else if (srcType == Primitive::kPrimLong) {
-      dst->SetD(src.GetJ());
-      return true;
-    } else if (srcType == Primitive::kPrimFloat) {
-      dst->SetD(src.GetF());
-      return true;
-    }
-    break;
-  default:
-    break;
-  }
-  if (!unbox_for_result) {
-    ThrowIllegalArgumentException(throw_location,
-                                  StringPrintf("Invalid primitive conversion from %s to %s",
-                                               PrettyDescriptor(srcType).c_str(),
-                                               PrettyDescriptor(dstType).c_str()).c_str());
-  } else {
-    ThrowClassCastException(throw_location,
-                            StringPrintf("Couldn't convert result of type %s to %s",
-                                         PrettyDescriptor(srcType).c_str(),
-                                         PrettyDescriptor(dstType).c_str()).c_str());
-  }
-  return false;
-}
-
 mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) {
   if (src_class == Primitive::kPrimNot) {
     return value.GetL();
@@ -866,16 +788,18 @@
   return UnboxPrimitive(&throw_location, o, dst_class, nullptr, unboxed_value);
 }
 
-bool VerifyAccess(mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags) {
-  NthCallerVisitor visitor(Thread::Current(), 2);
+bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags) {
+  if ((access_flags & kAccPublic) != 0) {
+    return true;
+  }
+  NthCallerVisitor visitor(self, 2);
   visitor.WalkStack();
   if (UNLIKELY(visitor.caller == nullptr)) {
     // The caller is an attached native thread.
-    return (access_flags & kAccPublic) != 0;
+    return false;
   }
   mirror::Class* caller_class = visitor.caller->GetDeclaringClass();
-
-  if (((access_flags & kAccPublic) != 0) || (caller_class == declaring_class)) {
+  if (caller_class == declaring_class) {
     return true;
   }
   if ((access_flags & kAccPrivate) != 0) {
@@ -889,10 +813,7 @@
       return true;
     }
   }
-  if (!declaring_class->IsInSamePackage(caller_class)) {
-    return false;
-  }
-  return true;
+  return declaring_class->IsInSamePackage(caller_class);
 }
 
 }  // namespace art
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 2c54c06..0f41aca 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -42,9 +42,9 @@
                              mirror::Class* dst_class, JValue* unboxed_value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
-                           Primitive::Type src_class, Primitive::Type dst_class,
-                           const JValue& src, JValue* dst)
+ALWAYS_INLINE bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+                                         Primitive::Type src_class, Primitive::Type dst_class,
+                                         const JValue& src, JValue* dst)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
@@ -74,7 +74,8 @@
 bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-bool VerifyAccess(mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags)
+bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
+                  uint32_t access_flags)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 }  // namespace art
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index ac9026b..001598f 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -49,6 +49,11 @@
   return imt_conflict_method_.Read();
 }
 
+inline mirror::ArtMethod* Runtime::GetImtUnimplementedMethod() {
+  CHECK(!imt_unimplemented_method_.IsNull());
+  return imt_unimplemented_method_.Read();
+}
+
 inline mirror::ObjectArray<mirror::ArtMethod>* Runtime::GetDefaultImt()
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   CHECK(HasDefaultImt());
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index bcb9e0c..346d0a1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -147,10 +147,14 @@
       target_sdk_version_(0),
       implicit_null_checks_(false),
       implicit_so_checks_(false),
-      implicit_suspend_checks_(false) {
+      implicit_suspend_checks_(false),
+      is_native_bridge_loaded_(false) {
 }
 
 Runtime::~Runtime() {
+  if (is_native_bridge_loaded_) {
+    UnloadNativeBridge();
+  }
   if (dump_gc_performance_on_shutdown_) {
     // This can't be called from the Heap destructor below because it
     // could call RosAlloc::InspectAll() which needs the thread_list
@@ -172,9 +176,6 @@
     BackgroundMethodSamplingProfiler::Shutdown();
   }
 
-  // Shutdown the fault manager if it was initialized.
-  fault_manager.Shutdown();
-
   Trace::Shutdown();
 
   // Make sure to let the GC complete if it is running.
@@ -187,6 +188,10 @@
 
   // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
   delete thread_list_;
+
+  // Shutdown the fault manager if it was initialized.
+  fault_manager.Shutdown();
+
   delete monitor_list_;
   delete monitor_pool_;
   delete class_linker_;
@@ -410,8 +415,17 @@
 
   started_ = true;
 
+  if (IsZygote()) {
+    ScopedObjectAccess soa(self);
+    gc::space::ImageSpace* image_space = heap_->GetImageSpace();
+    if (image_space != nullptr) {
+      Runtime::Current()->GetInternTable()->AddImageStringsToTable(image_space);
+      Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable();
+    }
+  }
+
   if (!IsImageDex2OatEnabled() || !Runtime::Current()->GetHeap()->HasImageSpace()) {
-    ScopedObjectAccess soa(Thread::Current());
+    ScopedObjectAccess soa(self);
     StackHandleScope<1> hs(soa.Self());
     auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
     class_linker_->EnsureInitialized(klass, true, true);
@@ -433,12 +447,11 @@
       return false;
     }
   } else {
-    bool have_native_bridge = !native_bridge_library_filename_.empty();
-    if (have_native_bridge) {
+    if (is_native_bridge_loaded_) {
       PreInitializeNativeBridge(".");
     }
-    DidForkFromZygote(self->GetJniEnv(), have_native_bridge ? NativeBridgeAction::kInitialize :
-        NativeBridgeAction::kUnload, GetInstructionSetString(kRuntimeISA));
+    DidForkFromZygote(self->GetJniEnv(), NativeBridgeAction::kInitialize,
+                      GetInstructionSetString(kRuntimeISA));
   }
 
   StartDaemonThreads();
@@ -517,14 +530,17 @@
 void Runtime::DidForkFromZygote(JNIEnv* env, NativeBridgeAction action, const char* isa) {
   is_zygote_ = false;
 
-  switch (action) {
-    case NativeBridgeAction::kUnload:
-      UnloadNativeBridge();
-      break;
+  if (is_native_bridge_loaded_) {
+    switch (action) {
+      case NativeBridgeAction::kUnload:
+        UnloadNativeBridge();
+        is_native_bridge_loaded_ = false;
+        break;
 
-    case NativeBridgeAction::kInitialize:
-      InitializeNativeBridge(env, isa);
-      break;
+      case NativeBridgeAction::kInitialize:
+        InitializeNativeBridge(env, isa);
+        break;
+    }
   }
 
   // Create the thread pool.
@@ -744,7 +760,7 @@
     case kArm64:
     case kX86_64:
       implicit_null_checks_ = true;
-      implicit_so_checks_ = true;
+      implicit_so_checks_ = (RUNNING_ON_VALGRIND == 0);
       break;
     default:
       // Keep the defaults.
@@ -881,8 +897,7 @@
   // Runtime::Start():
   //   DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
   //   No-op wrt native bridge.
-  native_bridge_library_filename_ = options->native_bridge_library_filename_;
-  LoadNativeBridge(native_bridge_library_filename_);
+  is_native_bridge_loaded_ = LoadNativeBridge(options->native_bridge_library_filename_);
 
   VLOG(startup) << "Runtime::Init exiting";
   return true;
@@ -1142,26 +1157,14 @@
 
 void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
   java_vm_->VisitRoots(callback, arg);
-  if (!pre_allocated_OutOfMemoryError_.IsNull()) {
-    pre_allocated_OutOfMemoryError_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!pre_allocated_OutOfMemoryError_.IsNull());
-  }
-  resolution_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  DCHECK(!resolution_method_.IsNull());
-  if (!pre_allocated_NoClassDefFoundError_.IsNull()) {
-    pre_allocated_NoClassDefFoundError_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!pre_allocated_NoClassDefFoundError_.IsNull());
-  }
-  if (HasImtConflictMethod()) {
-    imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
-  if (HasDefaultImt()) {
-    default_imt_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
+  pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  resolution_method_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
+  pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  imt_conflict_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  imt_unimplemented_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  default_imt_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
-    if (!callee_save_methods_[i].IsNull()) {
-      callee_save_methods_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
-    }
+    callee_save_methods_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   }
   verifier::MethodVerifier::VisitStaticRoots(callback, arg);
   {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8cfa8aa..6980e8d1 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -133,6 +133,10 @@
     return compiler_options_;
   }
 
+  void AddCompilerOption(std::string option) {
+    compiler_options_.push_back(option);
+  }
+
   const std::vector<std::string>& GetImageCompilerOptions() const {
     return image_compiler_options_;
   }
@@ -310,6 +314,7 @@
 
   // Returns a special method that calls into a trampoline for runtime imt conflicts.
   mirror::ArtMethod* GetImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::ArtMethod* GetImtUnimplementedMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool HasImtConflictMethod() const {
     return !imt_conflict_method_.IsNull();
@@ -318,6 +323,9 @@
   void SetImtConflictMethod(mirror::ArtMethod* method) {
     imt_conflict_method_ = GcRoot<mirror::ArtMethod>(method);
   }
+  void SetImtUnimplementedMethod(mirror::ArtMethod* method) {
+    imt_unimplemented_method_ = GcRoot<mirror::ArtMethod>(method);
+  }
 
   mirror::ArtMethod* CreateImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -515,6 +523,9 @@
   GcRoot<mirror::Throwable> pre_allocated_NoClassDefFoundError_;
   GcRoot<mirror::ArtMethod> resolution_method_;
   GcRoot<mirror::ArtMethod> imt_conflict_method_;
+  // Unresolved method has the same behavior as the conflict method, it is used by the class linker
+  // for differentiating between unfilled imt slots vs conflict slots in superclasses.
+  GcRoot<mirror::ArtMethod> imt_unimplemented_method_;
   GcRoot<mirror::ObjectArray<mirror::ArtMethod>> default_imt_;
 
   InstructionSet instruction_set_;
@@ -638,14 +649,16 @@
   bool implicit_so_checks_;         // StackOverflow checks are implicit.
   bool implicit_suspend_checks_;    // Thread suspension checks are implicit.
 
-  // The filename to the native bridge library. If this is not empty the native bridge will be
-  // initialized and loaded from the given file (initialized and available). An empty value means
-  // that there's no native bridge (initialized but not available).
+  // Whether or not a native bridge has been loaded.
   //
   // The native bridge allows running native code compiled for a foreign ISA. The way it works is,
   // if standard dlopen fails to load native library associated with native activity, it calls to
   // the native bridge to load it and then gets the trampoline for the entry to native activity.
-  std::string native_bridge_library_filename_;
+  //
+  // The option 'native_bridge_library_filename' specifies the name of the native bridge.
+  // When non-empty the native bridge will be loaded from the given file. An empty value means
+  // that there's no native bridge.
+  bool is_native_bridge_loaded_;
 
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 11e06fe..916e1e2 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -108,11 +108,17 @@
     PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'";
     return;
   }
-  std::unique_ptr<File> file(new File(fd, stack_trace_file_));
-  if (!file->WriteFully(s.data(), s.size())) {
-    PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'";
+  std::unique_ptr<File> file(new File(fd, stack_trace_file_, true));
+  bool success = file->WriteFully(s.data(), s.size());
+  if (success) {
+    success = file->FlushCloseOrErase() == 0;
   } else {
+    file->Erase();
+  }
+  if (success) {
     LOG(INFO) << "Wrote stack traces to '" << stack_trace_file_ << "'";
+  } else {
+    PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'";
   }
 }
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2d0060e..7dd4f08 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -112,6 +112,9 @@
   }
 }
 
+extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 mirror::Object* StackVisitor::GetThisObject() const {
   mirror::ArtMethod* m = GetMethod();
   if (m->IsStatic()) {
@@ -119,11 +122,17 @@
   } else if (m->IsNative()) {
     if (cur_quick_frame_ != NULL) {
       HandleScope* hs = reinterpret_cast<HandleScope*>(
-          reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffsetInBytes());
+          reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffsetInBytes().SizeValue());
       return hs->GetReference(0);
     } else {
       return cur_shadow_frame_->GetVRegReference(0);
     }
+  } else if (m->IsProxyMethod()) {
+    if (cur_quick_frame_ != nullptr) {
+      return artQuickGetProxyThisObject(cur_quick_frame_);
+    } else {
+      return cur_shadow_frame_->GetVRegReference(0);
+    }
   } else {
     const DexFile::CodeItem* code_item = m->GetCodeItem();
     if (code_item == NULL) {
@@ -147,9 +156,9 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably read registers without a context.
     DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer();
+    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
     DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
     QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
     uint32_t vmap_offset;
     // TODO: IsInContext stops before spotting floating point registers.
@@ -201,9 +210,9 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably read registers without a context.
     DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer();
+    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
     DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
     QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
     uint32_t vmap_offset_lo, vmap_offset_hi;
     // TODO: IsInContext stops before spotting floating point registers.
@@ -248,9 +257,9 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
     DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer();
+    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
     DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
     QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
     uint32_t vmap_offset;
     // TODO: IsInContext stops before spotting floating point registers.
@@ -312,9 +321,9 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
     DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer();
+    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
     DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
     QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
     uint32_t vmap_offset_lo, vmap_offset_hi;
     // TODO: IsInContext stops before spotting floating point registers.
@@ -599,4 +608,11 @@
   }
 }
 
+void JavaFrameRootInfo::Describe(std::ostream& os) const {
+  const StackVisitor* visitor = stack_visitor_;
+  CHECK(visitor != nullptr);
+  os << "Type=" << GetType() << " thread_id=" << GetThreadId() << " location=" <<
+      visitor->DescribeLocation() << " vreg=" << vreg_;
+}
+
 }  // namespace art
diff --git a/runtime/stack.h b/runtime/stack.h
index e58caee..7188d30 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -21,6 +21,7 @@
 #include <string>
 
 #include "dex_file.h"
+#include "gc_root.h"
 #include "instruction_set.h"
 #include "mirror/object_reference.h"
 #include "throw_location.h"
@@ -397,6 +398,19 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ShadowFrame);
 };
 
+class JavaFrameRootInfo : public RootInfo {
+ public:
+  JavaFrameRootInfo(uint32_t thread_id, const StackVisitor* stack_visitor, size_t vreg)
+     : RootInfo(kRootJavaFrame, thread_id), stack_visitor_(stack_visitor), vreg_(vreg) {
+  }
+  virtual void Describe(std::ostream& os) const OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+  const StackVisitor* const stack_visitor_;
+  const size_t vreg_;
+};
+
 // The managed stack is used to record fragments of managed code stacks. Managed code stacks
 // may either be shadow frames or lists of frames using fixed frame sizes. Transition records are
 // necessary for transitions between code using different frame layouts and transitions into native
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 6b65f12..2f474f7 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -168,6 +168,9 @@
     self->GetJniEnv()->DeleteGlobalRef(self->tlsPtr_.jpeer);
     self->tlsPtr_.jpeer = nullptr;
     self->SetThreadName(self->GetThreadName(soa)->ToModifiedUtf8().c_str());
+
+    mirror::ArtField* priorityField = soa.DecodeField(WellKnownClasses::java_lang_Thread_priority);
+    self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));
     Dbg::PostThreadStart(self);
 
     // Invoke the 'run' method of our java.lang.Thread.
@@ -409,6 +412,11 @@
     }
   }
 
+  {
+    ScopedObjectAccess soa(self);
+    Dbg::PostThreadStart(self);
+  }
+
   return self;
 }
 
@@ -421,6 +429,10 @@
     thread_group = runtime->GetMainThreadGroup();
   }
   ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+  if (name != nullptr && thread_name.get() == nullptr) {
+    CHECK(IsExceptionPending());
+    return;
+  }
   jint thread_priority = GetNativePriority();
   jboolean thread_is_daemon = as_daemon;
 
@@ -978,10 +990,36 @@
 }
 
 void Thread::DumpJavaStack(std::ostream& os) const {
+  // Dumping the Java stack involves the verifier for locks. The verifier operates under the
+  // assumption that there is no exception pending on entry. Thus, stash any pending exception.
+  // TODO: Find a way to avoid const_cast.
+  StackHandleScope<3> scope(const_cast<Thread*>(this));
+  Handle<mirror::Throwable> exc;
+  Handle<mirror::Object> throw_location_this_object;
+  Handle<mirror::ArtMethod> throw_location_method;
+  uint32_t throw_location_dex_pc;
+  bool have_exception = false;
+  if (IsExceptionPending()) {
+    ThrowLocation exc_location;
+    exc = scope.NewHandle(GetException(&exc_location));
+    throw_location_this_object = scope.NewHandle(exc_location.GetThis());
+    throw_location_method = scope.NewHandle(exc_location.GetMethod());
+    throw_location_dex_pc = exc_location.GetDexPc();
+    const_cast<Thread*>(this)->ClearException();
+    have_exception = true;
+  }
+
   std::unique_ptr<Context> context(Context::Create());
   StackDumpVisitor dumper(os, const_cast<Thread*>(this), context.get(),
                           !tls32_.throwing_OutOfMemoryError);
   dumper.WalkStack();
+
+  if (have_exception) {
+    ThrowLocation exc_location(throw_location_this_object.Get(),
+                               throw_location_method.Get(),
+                               throw_location_dex_pc);
+    const_cast<Thread*>(this)->SetException(exc_location, exc.Get());
+  }
 }
 
 void Thread::DumpStack(std::ostream& os) const {
@@ -1115,8 +1153,7 @@
   }
 }
 
-static void MonitorExitVisitor(mirror::Object** object, void* arg, uint32_t /*thread_id*/,
-                               RootType /*root_type*/)
+static void MonitorExitVisitor(mirror::Object** object, void* arg, const RootInfo& /*root_info*/)
     NO_THREAD_SAFETY_ANALYSIS {
   Thread* self = reinterpret_cast<Thread*>(arg);
   mirror::Object* entered_monitor = *object;
@@ -1164,7 +1201,7 @@
 
   // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
   if (tlsPtr_.jni_env != nullptr) {
-    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
+    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, RootInfo(kRootVMInternal));
   }
 }
 
@@ -1285,7 +1322,7 @@
       mirror::Object* object = cur->GetReference(j);
       if (object != nullptr) {
         mirror::Object* old_obj = object;
-        visitor(&object, arg, thread_id, kRootNativeStack);
+        visitor(&object, arg, RootInfo(kRootNativeStack, thread_id));
         if (old_obj != object) {
           cur->SetReference(j, object);
         }
@@ -2036,7 +2073,7 @@
     } else {
       // Java method.
       // Portable path use DexGcMap and store in Method.native_gc_map_.
-      const uint8_t* gc_map = m->GetNativeGcMap();
+      const uint8_t* gc_map = m->GetNativeGcMap(sizeof(void*));
       CHECK(gc_map != nullptr) << PrettyMethod(m);
       verifier::DexPcToReferenceMap dex_gc_map(gc_map);
       uint32_t dex_pc = shadow_frame->GetDexPC();
@@ -2070,7 +2107,7 @@
 
     // Process register map (which native and runtime methods don't have)
     if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
-      const uint8_t* native_gc_map = m->GetNativeGcMap();
+      const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*));
       CHECK(native_gc_map != nullptr) << PrettyMethod(m);
       const DexFile::CodeItem* code_item = m->GetCodeItem();
       DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be nullptr or how would we compile its instructions?
@@ -2079,12 +2116,12 @@
                                  static_cast<size_t>(code_item->registers_size_));
       if (num_regs > 0) {
         Runtime* runtime = Runtime::Current();
-        const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
+        const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*));
         uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
         const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
         DCHECK(reg_bitmap != nullptr);
         const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
-        const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+        const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
         QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
         // For all dex registers in the bitmap
         StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
@@ -2134,8 +2171,8 @@
   RootCallbackVisitor(RootCallback* callback, void* arg, uint32_t tid)
      : callback_(callback), arg_(arg), tid_(tid) {}
 
-  void operator()(mirror::Object** obj, size_t, const StackVisitor*) const {
-    callback_(obj, arg_, tid_, kRootJavaFrame);
+  void operator()(mirror::Object** obj, size_t vreg, const StackVisitor* stack_visitor) const {
+    callback_(obj, arg_, JavaFrameRootInfo(tid_, stack_visitor, vreg));
   }
 
  private:
@@ -2152,27 +2189,28 @@
 void Thread::VisitRoots(RootCallback* visitor, void* arg) {
   uint32_t thread_id = GetThreadId();
   if (tlsPtr_.opeer != nullptr) {
-    visitor(&tlsPtr_.opeer, arg, thread_id, kRootThreadObject);
+    visitor(&tlsPtr_.opeer, arg, RootInfo(kRootThreadObject, thread_id));
   }
   if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) {
-    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
+    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg,
+            RootInfo(kRootNativeStack, thread_id));
   }
   tlsPtr_.throw_location.VisitRoots(visitor, arg);
   if (tlsPtr_.class_loader_override != nullptr) {
-    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.class_loader_override), arg, thread_id,
-            kRootNativeStack);
+    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.class_loader_override), arg,
+            RootInfo(kRootNativeStack, thread_id));
   }
   if (tlsPtr_.monitor_enter_object != nullptr) {
-    visitor(&tlsPtr_.monitor_enter_object, arg, thread_id, kRootNativeStack);
+    visitor(&tlsPtr_.monitor_enter_object, arg, RootInfo(kRootNativeStack, thread_id));
   }
-  tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, thread_id, kRootJNILocal);
-  tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, thread_id, kRootJNIMonitor);
+  tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, RootInfo(kRootJNILocal, thread_id));
+  tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, RootInfo(kRootJNIMonitor, thread_id));
   HandleScopeVisitRoots(visitor, arg, thread_id);
   if (tlsPtr_.debug_invoke_req != nullptr) {
-    tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+    tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
   }
   if (tlsPtr_.single_step_control != nullptr) {
-    tlsPtr_.single_step_control->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+    tlsPtr_.single_step_control->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
   }
   if (tlsPtr_.deoptimization_shadow_frame != nullptr) {
     RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
@@ -2183,8 +2221,8 @@
     }
   }
   if (tlsPtr_.shadow_frame_under_construction != nullptr) {
-    RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
-    ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitorToCallback);
+    RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+    ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
     for (ShadowFrame* shadow_frame = tlsPtr_.shadow_frame_under_construction;
         shadow_frame != nullptr;
         shadow_frame = shadow_frame->GetLink()) {
@@ -2193,21 +2231,22 @@
   }
   // Visit roots on this thread's stack
   Context* context = GetLongJumpContext();
-  RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
-  ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitorToCallback);
+  RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+  ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitor_to_callback);
   mapper.WalkStack();
   ReleaseLongJumpContext(context);
   for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
     if (frame.this_object_ != nullptr) {
-      visitor(&frame.this_object_, arg, thread_id, kRootJavaFrame);
+      visitor(&frame.this_object_, arg, RootInfo(kRootVMInternal, thread_id));
     }
     DCHECK(frame.method_ != nullptr);
-    visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg, thread_id, kRootJavaFrame);
+    visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg,
+            RootInfo(kRootVMInternal, thread_id));
   }
 }
 
-static void VerifyRoot(mirror::Object** root, void* /*arg*/, uint32_t /*thread_id*/,
-                       RootType /*root_type*/) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static void VerifyRoot(mirror::Object** root, void* /*arg*/, const RootInfo& /*root_info*/)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   VerifyObject(*root);
 }
 
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index 73a9e54..d5db983 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -55,6 +55,13 @@
   int newNice = kNiceValues[newPriority-1];
   pid_t tid = GetTid();
 
+  // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a
+  // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background
+  // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will
+  // have to call setPriority twice to do what they want :
+  //
+  //     Thread.setPriority(Thread.MIN_PRIORITY);  // no-op wrt to cgroups
+  //     Thread.setPriority(Thread.MAX_PRIORITY);  // will actually change cgroups.
   if (newNice >= ANDROID_PRIORITY_BACKGROUND) {
     set_sched_policy(tid, SP_BACKGROUND);
   } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index b48fcd4..5460249 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -34,6 +34,7 @@
 #include "monitor.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
+#include "trace.h"
 #include "utils.h"
 #include "well_known_classes.h"
 
@@ -166,6 +167,8 @@
   Runtime* runtime = Runtime::Current();
   std::ostringstream ss;
   ss << "Thread suspend timeout\n";
+  Locks::mutator_lock_->Dump(ss);
+  ss << "\n";
   runtime->GetThreadList()->DumpLocked(ss);
   LOG(FATAL) << ss.str();
   exit(0);
@@ -622,6 +625,7 @@
     {
       MutexLock mu(self, *Locks::thread_suspend_count_lock_);
       // Update global suspend all state for attaching threads.
+      DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
       ++suspend_all_count_;
       ++debug_suspend_all_count_;
       // Increment everybody's suspend count (except our own).
@@ -713,6 +717,56 @@
   VLOG(threads) << *self << " self-reviving (debugger)";
 }
 
+void ThreadList::ResumeAllForDebugger() {
+  Thread* self = Thread::Current();
+  Thread* debug_thread = Dbg::GetDebugThread();
+
+  VLOG(threads) << *self << " ResumeAllForDebugger starting...";
+
+  // Threads can't resume if we exclusively hold the mutator lock.
+  Locks::mutator_lock_->AssertNotExclusiveHeld(self);
+
+  {
+    MutexLock mu(self, *Locks::thread_list_lock_);
+    {
+      MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+      // Update global suspend all state for attaching threads.
+      DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
+      if (debug_suspend_all_count_ > 0) {
+        --suspend_all_count_;
+        --debug_suspend_all_count_;
+      } else {
+        // We've been asked to resume all threads without being asked to
+        // suspend them all before. That may happen if a debugger tries
+        // to resume some suspended threads (with suspend count == 1)
+        // at once with a VirtualMachine.Resume command. Let's print a
+        // warning.
+        LOG(WARNING) << "Debugger attempted to resume all threads without "
+                     << "having suspended them all before.";
+      }
+      // Decrement everybody's suspend count (except our own).
+      for (const auto& thread : list_) {
+        if (thread == self || thread == debug_thread) {
+          continue;
+        }
+        if (thread->GetDebugSuspendCount() == 0) {
+          // This thread may have been individually resumed with ThreadReference.Resume.
+          continue;
+        }
+        VLOG(threads) << "requesting thread resume: " << *thread;
+        thread->ModifySuspendCount(self, -1, true);
+      }
+    }
+  }
+
+  {
+    MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+    Thread::resume_cond_->Broadcast(self);
+  }
+
+  VLOG(threads) << *self << " ResumeAllForDebugger complete";
+}
+
 void ThreadList::UndoDebuggerSuspensions() {
   Thread* self = Thread::Current();
 
@@ -836,6 +890,9 @@
   // suspend and so on, must happen at this point, and not in ~Thread.
   self->Destroy();
 
+  // If tracing, remember thread id and name before thread exits.
+  Trace::StoreExitingThreadInfo(self);
+
   uint32_t thin_lock_id = self->GetThreadId();
   while (self != nullptr) {
     // Remove and delete the Thread* while holding the thread_list_lock_ and
@@ -885,28 +942,6 @@
   }
 }
 
-class VerifyRootWrapperArg {
- public:
-  VerifyRootWrapperArg(VerifyRootCallback* callback, void* arg) : callback_(callback), arg_(arg) {
-  }
-  VerifyRootCallback* const callback_;
-  void* const arg_;
-};
-
-static void VerifyRootWrapperCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                      RootType root_type) {
-  VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
-  wrapperArg->callback_(*root, wrapperArg->arg_, 0, NULL, root_type);
-}
-
-void ThreadList::VerifyRoots(VerifyRootCallback* callback, void* arg) const {
-  VerifyRootWrapperArg wrapper(callback, arg);
-  MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
-  for (const auto& thread : list_) {
-    thread->VisitRoots(VerifyRootWrapperCallback, &wrapper);
-  }
-}
-
 uint32_t ThreadList::AllocThreadId(Thread* self) {
   MutexLock mu(self, *Locks::allocated_thread_ids_lock_);
   for (size_t i = 0; i < allocated_ids_.size(); ++i) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index bb4f775..3c72190 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_THREAD_LIST_H_
 
 #include "base/mutex.h"
+#include "gc_root.h"
 #include "jni.h"
 #include "object_callbacks.h"
 
@@ -104,6 +105,11 @@
   void SuspendSelfForDebugger()
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_);
 
+  // Resume all threads
+  void ResumeAllForDebugger()
+      LOCKS_EXCLUDED(Locks::thread_list_lock_,
+                     Locks::thread_suspend_count_lock_);
+
   void UndoDebuggerSuspensions()
       LOCKS_EXCLUDED(Locks::thread_list_lock_,
                      Locks::thread_suspend_count_lock_);
@@ -121,9 +127,6 @@
   void VisitRoots(RootCallback* callback, void* arg) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VerifyRoots(VerifyRootCallback* callback, void* arg) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // Return a copy of the thread list.
   std::list<Thread*> GetList() EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_) {
     return list_;
diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc
index 04abe64..4d2aec0 100644
--- a/runtime/throw_location.cc
+++ b/runtime/throw_location.cc
@@ -34,11 +34,11 @@
 
 void ThrowLocation::VisitRoots(RootCallback* visitor, void* arg) {
   if (this_object_ != nullptr) {
-    visitor(&this_object_, arg, 0, kRootVMInternal);
+    visitor(&this_object_, arg, RootInfo(kRootVMInternal));
     DCHECK(this_object_ != nullptr);
   }
   if (method_ != nullptr) {
-    visitor(reinterpret_cast<mirror::Object**>(&method_), arg, 0, kRootVMInternal);
+    visitor(reinterpret_cast<mirror::Object**>(&method_), arg, RootInfo(kRootVMInternal));
     DCHECK(method_ != nullptr);
   }
 }
diff --git a/runtime/throw_location.h b/runtime/throw_location.h
index b36eb67..bec0da4 100644
--- a/runtime/throw_location.h
+++ b/runtime/throw_location.h
@@ -20,6 +20,7 @@
 #include "object_callbacks.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "gc_root.h"
 
 #include <stdint.h>
 #include <string>
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4bb388f..17ede83 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -427,6 +427,15 @@
                                                     instrumentation::Instrumentation::kMethodExited |
                                                     instrumentation::Instrumentation::kMethodUnwind);
     }
+    if (the_trace->trace_file_.get() != nullptr) {
+      // Do not try to erase, so flush and close explicitly.
+      if (the_trace->trace_file_->Flush() != 0) {
+        PLOG(ERROR) << "Could not flush trace file.";
+      }
+      if (the_trace->trace_file_->Close() != 0) {
+        PLOG(ERROR) << "Could not close trace file.";
+      }
+    }
     delete the_trace;
   }
   runtime->GetThreadList()->ResumeAll();
@@ -706,9 +715,21 @@
 
 void Trace::DumpThreadList(std::ostream& os) {
   Thread* self = Thread::Current();
+  for (auto it : exited_threads_) {
+    os << it.first << "\t" << it.second << "\n";
+  }
   Locks::thread_list_lock_->AssertNotHeld(self);
   MutexLock mu(self, *Locks::thread_list_lock_);
   Runtime::Current()->GetThreadList()->ForEach(DumpThread, &os);
 }
 
+void Trace::StoreExitingThreadInfo(Thread* thread) {
+  MutexLock mu(thread, *Locks::trace_lock_);
+  if (the_trace_ != nullptr) {
+    std::string name;
+    thread->GetThreadName(name);
+    the_trace_->exited_threads_.Put(thread->GetTid(), name);
+  }
+}
+
 }  // namespace art
diff --git a/runtime/trace.h b/runtime/trace.h
index 45a02da..ead1c29 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -104,6 +104,8 @@
   static std::vector<mirror::ArtMethod*>* AllocStackTrace();
   // Clear and store an old stack trace for later use.
   static void FreeStackTrace(std::vector<mirror::ArtMethod*>* stack_trace);
+  // Save id and name of a thread before it exits.
+  static void StoreExitingThreadInfo(Thread* thread);
 
  private:
   explicit Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled);
@@ -166,6 +168,9 @@
   // Did we overflow the buffer recording traces?
   bool overflow_;
 
+  // Map of thread ids and names that have already exited.
+  SafeMap<pid_t, std::string> exited_threads_;
+
   DISALLOW_COPY_AND_ASSIGN(Trace);
 };
 
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 50f1eca..23b9656 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -172,7 +172,7 @@
     it.second.VisitRoots(callback, arg);
     mirror::Object* old_root = it.first;
     mirror::Object* new_root = old_root;
-    callback(&new_root, arg, 0, kRootUnknown);
+    callback(&new_root, arg, RootInfo(kRootUnknown));
     if (new_root != old_root) {
       moving_roots.push_back(std::make_pair(old_root, new_root));
     }
@@ -199,7 +199,7 @@
     mirror::Array* old_root = it.first;
     CHECK(!old_root->IsObjectArray());
     mirror::Array* new_root = old_root;
-    callback(reinterpret_cast<mirror::Object**>(&new_root), arg, 0, kRootUnknown);
+    callback(reinterpret_cast<mirror::Object**>(&new_root), arg, RootInfo(kRootUnknown));
     if (new_root != old_root) {
       moving_roots.push_back(std::make_pair(old_root, new_root));
     }
@@ -319,7 +319,7 @@
       mirror::Object* obj =
           reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
       if (obj != nullptr) {
-        callback(&obj, arg, 0, kRootUnknown);
+        callback(&obj, arg, RootInfo(kRootUnknown));
         field_value.value = reinterpret_cast<uintptr_t>(obj);
       }
     }
@@ -364,7 +364,7 @@
 }
 
 void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
-  callback(reinterpret_cast<mirror::Object**>(&str_), arg, 0, kRootInternedString);
+  callback(reinterpret_cast<mirror::Object**>(&str_), arg, RootInfo(kRootInternedString));
 }
 
 void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 6625390..83206b2 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -19,6 +19,7 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "gc_root.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "primitive.h"
diff --git a/runtime/utf.cc b/runtime/utf.cc
index 02cbe3b..797c717 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -85,10 +85,10 @@
   return hash;
 }
 
-int32_t ComputeUtf8Hash(const char* chars) {
-  int32_t hash = 0;
+size_t ComputeModifiedUtf8Hash(const char* chars) {
+  size_t hash = 0;
   while (*chars != '\0') {
-    hash = hash * 31 + GetUtf16FromUtf8(&chars);
+    hash = hash * 31 + *chars++;
   }
   return hash;
 }
diff --git a/runtime/utf.h b/runtime/utf.h
index a09db9d..b55227b 100644
--- a/runtime/utf.h
+++ b/runtime/utf.h
@@ -79,8 +79,9 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count);
 
-// Compute a hash code of a UTF-8 string.
-int32_t ComputeUtf8Hash(const char* chars);
+// Compute a hash code of a modified UTF-8 string. Not the standard java hash since it returns a
+// size_t and hashes individual chars instead of codepoint words.
+size_t ComputeModifiedUtf8Hash(const char* chars);
 
 /*
  * Retrieve the next UTF-16 character from a UTF-8 string.
diff --git a/runtime/utils.cc b/runtime/utils.cc
index d01c2cb..3765759 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1376,4 +1376,8 @@
   return true;
 }
 
+std::string PrettyDescriptor(Primitive::Type type) {
+  return PrettyDescriptor(Primitive::Descriptor(type));
+}
+
 }  // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index 1dfa02a..e6235ad 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -25,9 +25,10 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/mutex.h"
 #include "globals.h"
 #include "instruction_set.h"
-#include "base/mutex.h"
+#include "primitive.h"
 
 #ifdef HAVE_ANDROID_OS
 #include "cutils/properties.h"
@@ -166,6 +167,18 @@
   typedef T type;
 };
 
+// Like sizeof, but count how many bits a type takes. Pass type explicitly.
+template <typename T>
+static constexpr size_t BitSizeOf() {
+  return sizeof(T) * CHAR_BIT;
+}
+
+// Like sizeof, but count how many bits a type takes. Infers type from parameter.
+template <typename T>
+static constexpr size_t BitSizeOf(T x) {
+  return sizeof(T) * CHAR_BIT;
+}
+
 // For rounding integers.
 template<typename T>
 static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n) WARN_UNUSED;
@@ -202,6 +215,22 @@
   return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uintptr_t>(x), n));
 }
 
+namespace utils {
+namespace detail {  // Private, implementation-specific namespace. Do not poke outside of this file.
+template <typename T>
+static constexpr inline T RoundUpToPowerOfTwoRecursive(T x, size_t bit) {
+  return bit == (BitSizeOf<T>()) ? x: RoundUpToPowerOfTwoRecursive(x | x >> bit, bit << 1);
+}
+}  // namespace detail
+}  // namespace utils
+
+// Recursive implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
+// figure 3-3, page 48, where the function is called clp2.
+template <typename T>
+static constexpr inline T RoundUpToPowerOfTwo(T x) {
+  return art::utils::detail::RoundUpToPowerOfTwoRecursive(x - 1, 1) + 1;
+}
+
 // Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
 // figure 3-3, page 48, where the function is called clp2.
 static inline uint32_t RoundUpToPowerOfTwo(uint32_t x) {
@@ -214,10 +243,23 @@
   return x + 1;
 }
 
+// Find the bit position of the most significant bit (0-based), or -1 if there were no bits set.
+template <typename T>
+static constexpr ssize_t MostSignificantBit(T value) {
+  return (value == 0) ? -1 : (MostSignificantBit(value >> 1) + 1);
+}
+
+// How many bits (minimally) does it take to store the constant 'value'? i.e. 1 for 1, 3 for 5, etc.
+template <typename T>
+static constexpr size_t MinimumBitsToStore(T value) {
+  return static_cast<size_t>(MostSignificantBit(value) + 1);
+}
+
 template<typename T>
 static constexpr int CLZ(T x) {
+  static_assert(sizeof(T) <= sizeof(long long), "T too large, must be smaller than long long");  // NOLINT [runtime/int] [4]
   return (sizeof(T) == sizeof(uint32_t))
-      ? __builtin_clz(x)
+      ? __builtin_clz(x)  // TODO: __builtin_clz[ll] has undefined behavior for x=0
       : __builtin_clzll(x);
 }
 
@@ -280,6 +322,7 @@
 std::string PrettyDescriptor(const char* descriptor);
 std::string PrettyDescriptor(mirror::Class* klass)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+std::string PrettyDescriptor(Primitive::Type type);
 
 // Returns a human-readable signature for 'f'. Something like "a.b.C.f" or
 // "int a.b.C.f" (depending on the value of 'with_type').
@@ -397,7 +440,8 @@
 // Sleep for the given number of nanoseconds, a bad way to handle contention.
 void NanoSleep(uint64_t ns);
 
-// Initialize a timespec to either an absolute or relative time.
+// Initialize a timespec to either a relative time (ms,ns), or to the absolute
+// time corresponding to the indicated clock value plus the supplied offset.
 void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts);
 
 // Splits a string using the given separator character into a vector of
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index d6c90e1..bc2a004 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -44,6 +44,18 @@
   EXPECT_EQ("java.lang.String", PrettyDescriptor("Ljava/lang/String;"));
 }
 
+TEST_F(UtilsTest, PrettyDescriptor_Primitive) {
+  EXPECT_EQ("boolean", PrettyDescriptor(Primitive::kPrimBoolean));
+  EXPECT_EQ("byte", PrettyDescriptor(Primitive::kPrimByte));
+  EXPECT_EQ("char", PrettyDescriptor(Primitive::kPrimChar));
+  EXPECT_EQ("short", PrettyDescriptor(Primitive::kPrimShort));
+  EXPECT_EQ("int", PrettyDescriptor(Primitive::kPrimInt));
+  EXPECT_EQ("float", PrettyDescriptor(Primitive::kPrimFloat));
+  EXPECT_EQ("long", PrettyDescriptor(Primitive::kPrimLong));
+  EXPECT_EQ("double", PrettyDescriptor(Primitive::kPrimDouble));
+  EXPECT_EQ("void", PrettyDescriptor(Primitive::kPrimVoid));
+}
+
 TEST_F(UtilsTest, PrettyDescriptor_PrimitiveArrays) {
   EXPECT_EQ("boolean[]", PrettyDescriptor("[Z"));
   EXPECT_EQ("boolean[][]", PrettyDescriptor("[[Z"));
@@ -390,4 +402,36 @@
   }
 }
 
+TEST_F(UtilsTest, RoundUpToPowerOfTwo) {
+  // Tests the constexpr variant since all the parameters are constexpr
+  EXPECT_EQ(0, RoundUpToPowerOfTwo(0));
+  EXPECT_EQ(1, RoundUpToPowerOfTwo(1));
+  EXPECT_EQ(2, RoundUpToPowerOfTwo(2));
+  EXPECT_EQ(4, RoundUpToPowerOfTwo(3));
+  EXPECT_EQ(8, RoundUpToPowerOfTwo(7));
+
+  EXPECT_EQ(0b10000L, RoundUpToPowerOfTwo(0b01101L));
+  EXPECT_EQ(1ULL << 63, RoundUpToPowerOfTwo(1ULL << 62 | 1ULL));
+}
+
+TEST_F(UtilsTest, MostSignificantBit) {
+  EXPECT_EQ(-1, MostSignificantBit(0));
+  EXPECT_EQ(0, MostSignificantBit(1));
+  EXPECT_EQ(31, MostSignificantBit(~static_cast<uint32_t>(0)));
+  EXPECT_EQ(2, MostSignificantBit(0b110));
+  EXPECT_EQ(2, MostSignificantBit(0b100));
+}
+
+TEST_F(UtilsTest, MinimumBitsToStore) {
+  EXPECT_EQ(0u, MinimumBitsToStore(0));
+  EXPECT_EQ(1u, MinimumBitsToStore(1));
+  EXPECT_EQ(2u, MinimumBitsToStore(0b10));
+  EXPECT_EQ(2u, MinimumBitsToStore(0b11));
+  EXPECT_EQ(3u, MinimumBitsToStore(0b100));
+  EXPECT_EQ(3u, MinimumBitsToStore(0b110));
+  EXPECT_EQ(3u, MinimumBitsToStore(0b101));
+  EXPECT_EQ(8u, MinimumBitsToStore(0xFF));
+  EXPECT_EQ(32u, MinimumBitsToStore(~static_cast<uint32_t>(0)));
+}
+
 }  // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1720e18..ee7bd14 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -87,6 +87,23 @@
   }
 }
 
+// Note: returns true on failure.
+ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool condition,
+                                             const char* error_msg, uint32_t work_insn_idx) {
+  if (kIsDebugBuild) {
+    // In a debug build, abort if the error condition is wrong.
+    DCHECK(condition) << error_msg << work_insn_idx;
+  } else {
+    // In a non-debug build, just fail the class.
+    if (!condition) {
+      verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx;
+      return true;
+    }
+  }
+
+  return false;
+}
+
 MethodVerifier::FailureKind MethodVerifier::VerifyClass(mirror::Class* klass,
                                                         bool allow_soft_failures,
                                                         std::string* error) {
@@ -506,9 +523,9 @@
     }
   }
   failures_.push_back(error);
-  std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
+  std::string location(StringPrintf("%s: [0x%X] ", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
                                     work_insn_idx_));
-  std::ostringstream* failure_message = new std::ostringstream(location);
+  std::ostringstream* failure_message = new std::ostringstream(location, std::ostringstream::ate);
   failure_messages_.push_back(failure_message);
   return *failure_message;
 }
@@ -523,7 +540,7 @@
   DCHECK_NE(failure_num, 0U);
   std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
   prepend += last_fail_message->str();
-  failure_messages_[failure_num - 1] = new std::ostringstream(prepend);
+  failure_messages_[failure_num - 1] = new std::ostringstream(prepend, std::ostringstream::ate);
   delete last_fail_message;
 }
 
@@ -1538,6 +1555,12 @@
       break;
 
     case Instruction::MOVE_EXCEPTION: {
+      // We do not allow MOVE_EXCEPTION as the first instruction in a method. This is a simple case
+      // where one entrypoint to the catch block is not actually an exception path.
+      if (work_insn_idx_ == 0) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "move-exception at pc 0x0";
+        break;
+      }
       /*
        * This statement can only appear as the first instruction in an exception handler. We verify
        * that as part of extracting the exception type from the catch block list.
@@ -1951,7 +1974,11 @@
         while (0 != instance_of_idx && !insn_flags_[instance_of_idx].IsOpcode()) {
           instance_of_idx--;
         }
-        CHECK(insn_flags_[instance_of_idx].IsOpcode());
+        if (FailOrAbort(this, insn_flags_[instance_of_idx].IsOpcode(),
+                        "Unable to get previous instruction of if-eqz/if-nez for work index ",
+                        work_insn_idx_)) {
+          break;
+        }
       } else {
         break;
       }
@@ -2009,7 +2036,11 @@
             while (0 != move_idx && !insn_flags_[move_idx].IsOpcode()) {
               move_idx--;
             }
-            CHECK(insn_flags_[move_idx].IsOpcode());
+            if (FailOrAbort(this, insn_flags_[move_idx].IsOpcode(),
+                            "Unable to get previous instruction of if-eqz/if-nez for work index ",
+                            work_insn_idx_)) {
+              break;
+            }
             const Instruction* move_inst = Instruction::At(code_item_->insns_ + move_idx);
             switch (move_inst->Opcode()) {
               case Instruction::MOVE_OBJECT:
@@ -2092,91 +2123,95 @@
       break;
 
     case Instruction::IGET_BOOLEAN:
-      VerifyISGet(inst, reg_types_.Boolean(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Boolean(), true, false);
       break;
     case Instruction::IGET_BYTE:
-      VerifyISGet(inst, reg_types_.Byte(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Byte(), true, false);
       break;
     case Instruction::IGET_CHAR:
-      VerifyISGet(inst, reg_types_.Char(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Char(), true, false);
       break;
     case Instruction::IGET_SHORT:
-      VerifyISGet(inst, reg_types_.Short(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Short(), true, false);
       break;
     case Instruction::IGET:
-      VerifyISGet(inst, reg_types_.Integer(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Integer(), true, false);
       break;
     case Instruction::IGET_WIDE:
-      VerifyISGet(inst, reg_types_.LongLo(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.LongLo(), true, false);
       break;
     case Instruction::IGET_OBJECT:
-      VerifyISGet(inst, reg_types_.JavaLangObject(false), false, false);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.JavaLangObject(false), false,
+                                                    false);
       break;
 
     case Instruction::IPUT_BOOLEAN:
-      VerifyISPut(inst, reg_types_.Boolean(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Boolean(), true, false);
       break;
     case Instruction::IPUT_BYTE:
-      VerifyISPut(inst, reg_types_.Byte(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Byte(), true, false);
       break;
     case Instruction::IPUT_CHAR:
-      VerifyISPut(inst, reg_types_.Char(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Char(), true, false);
       break;
     case Instruction::IPUT_SHORT:
-      VerifyISPut(inst, reg_types_.Short(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Short(), true, false);
       break;
     case Instruction::IPUT:
-      VerifyISPut(inst, reg_types_.Integer(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Integer(), true, false);
       break;
     case Instruction::IPUT_WIDE:
-      VerifyISPut(inst, reg_types_.LongLo(), true, false);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.LongLo(), true, false);
       break;
     case Instruction::IPUT_OBJECT:
-      VerifyISPut(inst, reg_types_.JavaLangObject(false), false, false);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.JavaLangObject(false), false,
+                                                    false);
       break;
 
     case Instruction::SGET_BOOLEAN:
-      VerifyISGet(inst, reg_types_.Boolean(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Boolean(), true, true);
       break;
     case Instruction::SGET_BYTE:
-      VerifyISGet(inst, reg_types_.Byte(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Byte(), true, true);
       break;
     case Instruction::SGET_CHAR:
-      VerifyISGet(inst, reg_types_.Char(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Char(), true, true);
       break;
     case Instruction::SGET_SHORT:
-      VerifyISGet(inst, reg_types_.Short(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Short(), true, true);
       break;
     case Instruction::SGET:
-      VerifyISGet(inst, reg_types_.Integer(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Integer(), true, true);
       break;
     case Instruction::SGET_WIDE:
-      VerifyISGet(inst, reg_types_.LongLo(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.LongLo(), true, true);
       break;
     case Instruction::SGET_OBJECT:
-      VerifyISGet(inst, reg_types_.JavaLangObject(false), false, true);
+      VerifyISFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.JavaLangObject(false), false,
+                                                    true);
       break;
 
     case Instruction::SPUT_BOOLEAN:
-      VerifyISPut(inst, reg_types_.Boolean(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Boolean(), true, true);
       break;
     case Instruction::SPUT_BYTE:
-      VerifyISPut(inst, reg_types_.Byte(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Byte(), true, true);
       break;
     case Instruction::SPUT_CHAR:
-      VerifyISPut(inst, reg_types_.Char(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Char(), true, true);
       break;
     case Instruction::SPUT_SHORT:
-      VerifyISPut(inst, reg_types_.Short(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Short(), true, true);
       break;
     case Instruction::SPUT:
-      VerifyISPut(inst, reg_types_.Integer(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Integer(), true, true);
       break;
     case Instruction::SPUT_WIDE:
-      VerifyISPut(inst, reg_types_.LongLo(), true, true);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.LongLo(), true, true);
       break;
     case Instruction::SPUT_OBJECT:
-      VerifyISPut(inst, reg_types_.JavaLangObject(false), false, true);
+      VerifyISFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.JavaLangObject(false), false,
+                                                    true);
       break;
 
     case Instruction::INVOKE_VIRTUAL:
@@ -2620,22 +2655,22 @@
     // As such they use Class*/Field*/AbstractMethod* as these offsets only have
     // meaning if the class linking and resolution were successful.
     case Instruction::IGET_QUICK:
-      VerifyIGetQuick(inst, reg_types_.Integer(), true);
+      VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Integer(), true);
       break;
     case Instruction::IGET_WIDE_QUICK:
-      VerifyIGetQuick(inst, reg_types_.LongLo(), true);
+      VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.LongLo(), true);
       break;
     case Instruction::IGET_OBJECT_QUICK:
-      VerifyIGetQuick(inst, reg_types_.JavaLangObject(false), false);
+      VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.JavaLangObject(false), false);
       break;
     case Instruction::IPUT_QUICK:
-      VerifyIPutQuick(inst, reg_types_.Integer(), true);
+      VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Integer(), true);
       break;
     case Instruction::IPUT_WIDE_QUICK:
-      VerifyIPutQuick(inst, reg_types_.LongLo(), true);
+      VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.LongLo(), true);
       break;
     case Instruction::IPUT_OBJECT_QUICK:
-      VerifyIPutQuick(inst, reg_types_.JavaLangObject(false), false);
+      VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.JavaLangObject(false), false);
       break;
     case Instruction::INVOKE_VIRTUAL_QUICK:
     case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
@@ -2982,7 +3017,12 @@
               // odd case, but nothing to do
             } else {
               common_super = &common_super->Merge(exception, &reg_types_);
-              CHECK(reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super));
+              if (FailOrAbort(this,
+                              reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super),
+                              "java.lang.Throwable is not assignable-from common_super at ",
+                              work_insn_idx_)) {
+                break;
+              }
             }
           }
         }
@@ -3079,7 +3119,7 @@
   }
   // See if the method type implied by the invoke instruction matches the access flags for the
   // target method.
-  if ((method_type == METHOD_DIRECT && !res_method->IsDirect()) ||
+  if ((method_type == METHOD_DIRECT && (!res_method->IsDirect() || res_method->IsStatic())) ||
       (method_type == METHOD_STATIC && !res_method->IsStatic()) ||
       ((method_type == METHOD_VIRTUAL || method_type == METHOD_INTERFACE) && res_method->IsDirect())
       ) {
@@ -3308,17 +3348,32 @@
   if (klass->IsInterface()) {
     // Derive Object.class from Class.class.getSuperclass().
     mirror::Class* object_klass = klass->GetClass()->GetSuperClass();
-    CHECK(object_klass->IsObjectClass());
+    if (FailOrAbort(this, object_klass->IsObjectClass(),
+                    "Failed to find Object class in quickened invoke receiver",
+                    work_insn_idx_)) {
+      return nullptr;
+    }
     dispatch_class = object_klass;
   } else {
     dispatch_class = klass;
   }
-  CHECK(dispatch_class->HasVTable()) << PrettyDescriptor(dispatch_class);
+  if (FailOrAbort(this, dispatch_class->HasVTable(),
+                  "Receiver class has no vtable for quickened invoke at ",
+                  work_insn_idx_)) {
+    return nullptr;
+  }
   uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
-  CHECK_LT(static_cast<int32_t>(vtable_index), dispatch_class->GetVTableLength())
-      << PrettyDescriptor(klass);
+  if (FailOrAbort(this, static_cast<int32_t>(vtable_index) < dispatch_class->GetVTableLength(),
+                  "Receiver class has not enough vtable slots for quickened invoke at ",
+                  work_insn_idx_)) {
+    return nullptr;
+  }
   mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index);
-  CHECK(!Thread::Current()->IsExceptionPending());
+  if (FailOrAbort(this, !Thread::Current()->IsExceptionPending(),
+                  "Unexpected exception pending for quickened invoke at ",
+                  work_insn_idx_)) {
+    return nullptr;
+  }
   return res_method;
 }
 
@@ -3331,7 +3386,14 @@
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name();
     return nullptr;
   }
-  CHECK(!res_method->IsDirect() && !res_method->IsStatic());
+  if (FailOrAbort(this, !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 ",
+                  work_insn_idx_)) {
+    return nullptr;
+  }
 
   // We use vAA as our expected arg count, rather than res_method->insSize, because we need to
   // match the call to the signature. Also, we might be calling through an abstract method
@@ -3695,8 +3757,9 @@
   }
 }
 
-void MethodVerifier::VerifyISGet(const Instruction* inst, RegType& insn_type,
-                                 bool is_primitive, bool is_static) {
+template <MethodVerifier::FieldAccessType kAccType>
+void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, RegType& insn_type,
+                                         bool is_primitive, bool is_static) {
   uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
   mirror::ArtField* field;
   if (is_static) {
@@ -3704,9 +3767,20 @@
   } else {
     RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
     field = GetInstanceField(object_type, field_idx);
+    if (UNLIKELY(have_pending_hard_failure_)) {
+      return;
+    }
   }
   RegType* field_type = nullptr;
   if (field != nullptr) {
+    if (kAccType == FieldAccessType::kAccPut) {
+      if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
+        Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
+                                        << " from other class " << GetDeclaringClass();
+        return;
+      }
+    }
+
     Thread* self = Thread::Current();
     mirror::Class* field_type_class;
     {
@@ -3729,89 +3803,56 @@
   }
   DCHECK(field_type != nullptr);
   const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
-  if (is_primitive) {
-    if (field_type->Equals(insn_type) ||
-        (field_type->IsFloat() && insn_type.IsInteger()) ||
-        (field_type->IsDouble() && insn_type.IsLong())) {
-      // expected that read is of the correct primitive type or that int reads are reading
-      // floats or long reads are reading doubles
+  static_assert(kAccType == FieldAccessType::kAccPut || kAccType == FieldAccessType::kAccGet,
+                "Unexpected third access type");
+  if (kAccType == FieldAccessType::kAccPut) {
+    // sput or iput.
+    if (is_primitive) {
+      VerifyPrimitivePut(*field_type, insn_type, vregA);
     } else {
-      // This is a global failure rather than a class change failure as the instructions and
-      // the descriptors for the type should have been consistent within the same file at
-      // compile time
-      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
-                                        << " to be of type '" << insn_type
-                                        << "' but found type '" << *field_type << "' in get";
-      return;
+      if (!insn_type.IsAssignableFrom(*field_type)) {
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                                << " to be compatible with type '" << insn_type
+                                                << "' but found type '" << *field_type
+                                                << "' in put-object";
+        return;
+      }
+      work_line_->VerifyRegisterType(vregA, *field_type);
     }
-  } else {
-    if (!insn_type.IsAssignableFrom(*field_type)) {
-      Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                        << " to be compatible with type '" << insn_type
-                                        << "' but found type '" << *field_type
-                                        << "' in Get-object";
-      work_line_->SetRegisterType(vregA, reg_types_.Conflict());
-      return;
-    }
-  }
-  if (!field_type->IsLowHalf()) {
-    work_line_->SetRegisterType(vregA, *field_type);
-  } else {
-    work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
-  }
-}
-
-void MethodVerifier::VerifyISPut(const Instruction* inst, RegType& insn_type,
-                                 bool is_primitive, bool is_static) {
-  uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
-  mirror::ArtField* field;
-  if (is_static) {
-    field = GetStaticField(field_idx);
-  } else {
-    RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
-    field = GetInstanceField(object_type, field_idx);
-  }
-  RegType* field_type = nullptr;
-  if (field != nullptr) {
-    if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
-      Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
-                                      << " from other class " << GetDeclaringClass();
-      return;
-    }
-    mirror::Class* field_type_class;
-    {
-      StackHandleScope<1> hs(Thread::Current());
-      HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
-      FieldHelper fh(h_field);
-      field_type_class = fh.GetType(can_load_classes_);
-    }
-    if (field_type_class != nullptr) {
-      field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
-                                         field_type_class->CannotBeAssignedFromOtherTypes());
+  } else if (kAccType == FieldAccessType::kAccGet) {
+    // sget or iget.
+    if (is_primitive) {
+      if (field_type->Equals(insn_type) ||
+          (field_type->IsFloat() && insn_type.IsInteger()) ||
+          (field_type->IsDouble() && insn_type.IsLong())) {
+        // expected that read is of the correct primitive type or that int reads are reading
+        // floats or long reads are reading doubles
+      } else {
+        // This is a global failure rather than a class change failure as the instructions and
+        // the descriptors for the type should have been consistent within the same file at
+        // compile time
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                          << " to be of type '" << insn_type
+                                          << "' but found type '" << *field_type << "' in get";
+        return;
+      }
     } else {
-      Thread* self = Thread::Current();
-      DCHECK(!can_load_classes_ || self->IsExceptionPending());
-      self->ClearException();
+      if (!insn_type.IsAssignableFrom(*field_type)) {
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                          << " to be compatible with type '" << insn_type
+                                          << "' but found type '" << *field_type
+                                          << "' in get-object";
+        work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+        return;
+      }
     }
-  }
-  if (field_type == nullptr) {
-    const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
-    const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
-    field_type = &reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
-  }
-  DCHECK(field_type != nullptr);
-  const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
-  if (is_primitive) {
-    VerifyPrimitivePut(*field_type, insn_type, vregA);
+    if (!field_type->IsLowHalf()) {
+      work_line_->SetRegisterType(vregA, *field_type);
+    } else {
+      work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
+    }
   } else {
-    if (!insn_type.IsAssignableFrom(*field_type)) {
-      Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                        << " to be compatible with type '" << insn_type
-                                        << "' but found type '" << *field_type
-                                        << "' in put-object";
-      return;
-    }
-    work_line_->VerifyRegisterType(vregA, *field_type);
+    LOG(FATAL) << "Unexpected case.";
   }
 }
 
@@ -3838,132 +3879,137 @@
   return f;
 }
 
-void MethodVerifier::VerifyIGetQuick(const Instruction* inst, RegType& insn_type,
-                                     bool is_primitive) {
+template <MethodVerifier::FieldAccessType kAccType>
+void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, RegType& insn_type,
+                                            bool is_primitive) {
   DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
-  mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get());
-  if (field == nullptr) {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name();
-    return;
-  }
-  mirror::Class* field_type_class;
-  {
-    StackHandleScope<1> hs(Thread::Current());
-    HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
-    FieldHelper fh(h_field);
-    field_type_class = fh.GetType(can_load_classes_);
-  }
-  RegType* field_type;
-  if (field_type_class != nullptr) {
-    field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
-                                       field_type_class->CannotBeAssignedFromOtherTypes());
-  } else {
-    Thread* self = Thread::Current();
-    DCHECK(!can_load_classes_ || self->IsExceptionPending());
-    self->ClearException();
-    field_type = &reg_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(),
-                                            field->GetTypeDescriptor(), false);
-  }
-  DCHECK(field_type != nullptr);
-  const uint32_t vregA = inst->VRegA_22c();
-  if (is_primitive) {
-    if (field_type->Equals(insn_type) ||
-        (field_type->IsFloat() && insn_type.IsIntegralTypes()) ||
-        (field_type->IsDouble() && insn_type.IsLongTypes())) {
-      // expected that read is of the correct primitive type or that int reads are reading
-      // floats or long reads are reading doubles
-    } else {
-      // This is a global failure rather than a class change failure as the instructions and
-      // the descriptors for the type should have been consistent within the same file at
-      // compile time
-      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
-                                        << " to be of type '" << insn_type
-                                        << "' but found type '" << *field_type << "' in Get";
-      return;
-    }
-  } else {
-    if (!insn_type.IsAssignableFrom(*field_type)) {
-      Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                        << " to be compatible with type '" << insn_type
-                                        << "' but found type '" << *field_type
-                                        << "' in get-object";
-      work_line_->SetRegisterType(vregA, reg_types_.Conflict());
-      return;
-    }
-  }
-  if (!field_type->IsLowHalf()) {
-    work_line_->SetRegisterType(vregA, *field_type);
-  } else {
-    work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
-  }
-}
 
-void MethodVerifier::VerifyIPutQuick(const Instruction* inst, RegType& insn_type,
-                                     bool is_primitive) {
-  DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
   mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get());
   if (field == nullptr) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name();
     return;
   }
-  const char* descriptor = field->GetTypeDescriptor();
-  mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
-  RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
-  if (field != nullptr) {
+
+  // For an IPUT_QUICK, we now test for final flag of the field.
+  if (kAccType == FieldAccessType::kAccPut) {
     if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
       Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
                                       << " from other class " << GetDeclaringClass();
       return;
     }
   }
-  const uint32_t vregA = inst->VRegA_22c();
-  if (is_primitive) {
-    // Primitive field assignability rules are weaker than regular assignability rules
-    bool instruction_compatible;
-    bool value_compatible;
-    RegType& value_type = work_line_->GetRegisterType(vregA);
-    if (field_type.IsIntegralTypes()) {
-      instruction_compatible = insn_type.IsIntegralTypes();
-      value_compatible = value_type.IsIntegralTypes();
-    } else if (field_type.IsFloat()) {
-      instruction_compatible = insn_type.IsInteger();  // no [is]put-float, so expect [is]put-int
-      value_compatible = value_type.IsFloatTypes();
-    } else if (field_type.IsLong()) {
-      instruction_compatible = insn_type.IsLong();
-      value_compatible = value_type.IsLongTypes();
-    } else if (field_type.IsDouble()) {
-      instruction_compatible = insn_type.IsLong();  // no [is]put-double, so expect [is]put-long
-      value_compatible = value_type.IsDoubleTypes();
+
+  // Get the field type.
+  RegType* field_type;
+  {
+    mirror::Class* field_type_class;
+    {
+      StackHandleScope<1> hs(Thread::Current());
+      HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
+      field_type_class = FieldHelper(h_field).GetType(can_load_classes_);
+    }
+
+    if (field_type_class != nullptr) {
+      field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
+                                         field_type_class->CannotBeAssignedFromOtherTypes());
     } else {
-      instruction_compatible = false;  // reference field with primitive store
-      value_compatible = false;  // unused
+      Thread* self = Thread::Current();
+      DCHECK(!can_load_classes_ || self->IsExceptionPending());
+      self->ClearException();
+      field_type = &reg_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(),
+                                              field->GetTypeDescriptor(), false);
     }
-    if (!instruction_compatible) {
-      // This is a global failure rather than a class change failure as the instructions and
-      // the descriptors for the type should have been consistent within the same file at
-      // compile time
-      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
-                                        << " to be of type '" << insn_type
-                                        << "' but found type '" << field_type
-                                        << "' in put";
+    if (field_type == nullptr) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field type from " << inst->Name();
       return;
     }
-    if (!value_compatible) {
-      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA
-          << " of type " << value_type
-          << " but expected " << field_type
-          << " for store to " << PrettyField(field) << " in put";
-      return;
+  }
+
+  const uint32_t vregA = inst->VRegA_22c();
+  static_assert(kAccType == FieldAccessType::kAccPut || kAccType == FieldAccessType::kAccGet,
+                "Unexpected third access type");
+  if (kAccType == FieldAccessType::kAccPut) {
+    if (is_primitive) {
+      // Primitive field assignability rules are weaker than regular assignability rules
+      bool instruction_compatible;
+      bool value_compatible;
+      RegType& value_type = work_line_->GetRegisterType(vregA);
+      if (field_type->IsIntegralTypes()) {
+        instruction_compatible = insn_type.IsIntegralTypes();
+        value_compatible = value_type.IsIntegralTypes();
+      } else if (field_type->IsFloat()) {
+        instruction_compatible = insn_type.IsInteger();  // no [is]put-float, so expect [is]put-int
+        value_compatible = value_type.IsFloatTypes();
+      } else if (field_type->IsLong()) {
+        instruction_compatible = insn_type.IsLong();
+        value_compatible = value_type.IsLongTypes();
+      } else if (field_type->IsDouble()) {
+        instruction_compatible = insn_type.IsLong();  // no [is]put-double, so expect [is]put-long
+        value_compatible = value_type.IsDoubleTypes();
+      } else {
+        instruction_compatible = false;  // reference field with primitive store
+        value_compatible = false;  // unused
+      }
+      if (!instruction_compatible) {
+        // This is a global failure rather than a class change failure as the instructions and
+        // the descriptors for the type should have been consistent within the same file at
+        // compile time
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                          << " to be of type '" << insn_type
+                                          << "' but found type '" << *field_type
+                                          << "' in put";
+        return;
+      }
+      if (!value_compatible) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA
+            << " of type " << value_type
+            << " but expected " << *field_type
+            << " for store to " << PrettyField(field) << " in put";
+        return;
+      }
+    } else {
+      if (!insn_type.IsAssignableFrom(*field_type)) {
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                          << " to be compatible with type '" << insn_type
+                                          << "' but found type '" << *field_type
+                                          << "' in put-object";
+        return;
+      }
+      work_line_->VerifyRegisterType(vregA, *field_type);
+    }
+  } else if (kAccType == FieldAccessType::kAccGet) {
+    if (is_primitive) {
+      if (field_type->Equals(insn_type) ||
+          (field_type->IsFloat() && insn_type.IsIntegralTypes()) ||
+          (field_type->IsDouble() && insn_type.IsLongTypes())) {
+        // expected that read is of the correct primitive type or that int reads are reading
+        // floats or long reads are reading doubles
+      } else {
+        // This is a global failure rather than a class change failure as the instructions and
+        // the descriptors for the type should have been consistent within the same file at
+        // compile time
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                          << " to be of type '" << insn_type
+                                          << "' but found type '" << *field_type << "' in Get";
+        return;
+      }
+    } else {
+      if (!insn_type.IsAssignableFrom(*field_type)) {
+        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                          << " to be compatible with type '" << insn_type
+                                          << "' but found type '" << *field_type
+                                          << "' in get-object";
+        work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+        return;
+      }
+    }
+    if (!field_type->IsLowHalf()) {
+      work_line_->SetRegisterType(vregA, *field_type);
+    } else {
+      work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
     }
   } else {
-    if (!insn_type.IsAssignableFrom(field_type)) {
-      Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                        << " to be compatible with type '" << insn_type
-                                        << "' but found type '" << field_type
-                                        << "' in put-object";
-      return;
-    }
-    work_line_->VerifyRegisterType(vregA, field_type);
+    LOG(FATAL) << "Unexpected case.";
   }
 }
 
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 17713d2..d7dd4b8 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -506,14 +506,14 @@
   // Lookup static field and fail for resolution violations
   mirror::ArtField* GetStaticField(int field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Perform verification of an iget or sget instruction.
-  void VerifyISGet(const Instruction* inst, RegType& insn_type,
-                   bool is_primitive, bool is_static)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  // Perform verification of an iput or sput instruction.
-  void VerifyISPut(const Instruction* inst, RegType& insn_type,
-                   bool is_primitive, bool is_static)
+  // Perform verification of an iget/sget/iput/sput instruction.
+  enum class FieldAccessType {  // private
+    kAccGet,
+    kAccPut
+  };
+  template <FieldAccessType kAccType>
+  void VerifyISFieldAccess(const Instruction* inst, RegType& insn_type,
+                           bool is_primitive, bool is_static)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns the access field of a quick field access (iget/iput-quick) or nullptr
@@ -521,14 +521,8 @@
   mirror::ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Perform verification of an iget-quick instruction.
-  void VerifyIGetQuick(const Instruction* inst, RegType& insn_type,
-                       bool is_primitive)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  // Perform verification of an iput-quick instruction.
-  void VerifyIPutQuick(const Instruction* inst, RegType& insn_type,
-                       bool is_primitive)
+  template <FieldAccessType kAccType>
+  void VerifyQuickFieldAccess(const Instruction* inst, RegType& insn_type, bool is_primitive)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Resolves a class based on an index and performs access checks to ensure the referrer can
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 30be82f..ede9866 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -968,9 +968,7 @@
 }
 
 void RegType::VisitRoots(RootCallback* callback, void* arg) {
-  if (!klass_.IsNull()) {
-    klass_.VisitRoot(callback, arg, 0, kRootUnknown);
-  }
+  klass_.VisitRootIfNonNull(callback, arg, RootInfo(kRootUnknown));
 }
 
 void UninitializedThisReferenceType::CheckInvariants() const {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 63fb2d9..4bb99c1 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -147,7 +147,7 @@
   if (can_load_classes_) {
     klass = class_linker->FindClass(self, descriptor, class_loader);
   } else {
-    klass = class_linker->LookupClass(descriptor, loader);
+    klass = class_linker->LookupClass(descriptor, ComputeModifiedUtf8Hash(descriptor), loader);
     if (klass != nullptr && !klass->IsLoaded()) {
       // We found the class but without it being loaded its not safe for use.
       klass = nullptr;
diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc
index 96abee2..70a4dda 100644
--- a/runtime/zip_archive_test.cc
+++ b/runtime/zip_archive_test.cc
@@ -41,7 +41,7 @@
 
   ScratchFile tmp;
   ASSERT_NE(-1, tmp.GetFd());
-  std::unique_ptr<File> file(new File(tmp.GetFd(), tmp.GetFilename()));
+  std::unique_ptr<File> file(new File(tmp.GetFd(), tmp.GetFilename(), false));
   ASSERT_TRUE(file.get() != NULL);
   bool success = zip_entry->ExtractToFile(*file, &error_msg);
   ASSERT_TRUE(success) << error_msg;
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
index d86735d..b7ff360 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -22,14 +22,26 @@
 LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
 LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
-LOCAL_SRC_FILES := sigchain.cc
+LOCAL_SRC_FILES := sigchain_dummy.cc
 LOCAL_CLANG = $(ART_TARGET_CLANG)
 LOCAL_MODULE:= libsigchain
-LOCAL_SHARED_LIBRARIES := liblog libdl
+LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
 include $(BUILD_SHARED_LIBRARY)
 
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+LOCAL_SRC_FILES := sigchain.cc
+LOCAL_CLANG = $(ART_TARGET_CLANG)
+LOCAL_MODULE:= libsigchain
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+include $(BUILD_STATIC_LIBRARY)
+
 # Build host library.
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
@@ -37,9 +49,23 @@
 LOCAL_IS_HOST_MODULE := true
 LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
 LOCAL_CLANG = $(ART_HOST_CLANG)
+LOCAL_SRC_FILES := sigchain_dummy.cc
+LOCAL_MODULE:= libsigchain
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_LDLIBS = -ldl
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+LOCAL_MODULE_TAGS := optional
+LOCAL_IS_HOST_MODULE := true
+LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+LOCAL_CLANG = $(ART_HOST_CLANG)
 LOCAL_SRC_FILES := sigchain.cc
 LOCAL_MODULE:= libsigchain
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_LDLIBS = -ldl
 LOCAL_MULTILIB := both
-include $(BUILD_HOST_SHARED_LIBRARY)
+include external/libcxx/libcxx.mk
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 4991891..938faf1 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -99,21 +99,20 @@
   }
 }
 
-
 // Claim a signal chain for a particular signal.
-void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
   CheckSignalValid(signal);
   user_sigactions[signal].Claim(*oldaction);
 }
 
-void UnclaimSignalChain(int signal) {
+extern "C" void UnclaimSignalChain(int signal) {
   CheckSignalValid(signal);
 
   user_sigactions[signal].Unclaim(signal);
 }
 
 // Invoke the user's signal handler.
-void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
   // Check the arguments.
   CheckSignalValid(sig);
 
@@ -140,7 +139,7 @@
   }
 }
 
-void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
   CheckSignalValid(signal);
   // Read the current action without looking at the chain, it should be the expected action.
   SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
@@ -155,21 +154,21 @@
   }
 }
 
-extern "C" {
 // These functions are C linkage since they replace the functions in libc.
 
-int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
+extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
   // If this signal has been claimed as a signal chain, record the user's
   // action but don't pass it on to the kernel.
   // Note that we check that the signal number is in range here.  An out of range signal
   // number should behave exactly as the libc sigaction.
   if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
-    if (old_action != NULL) {
-      *old_action = user_sigactions[signal].GetAction();
-    }
+    struct sigaction saved_action = user_sigactions[signal].GetAction();
     if (new_action != NULL) {
       user_sigactions[signal].SetAction(*new_action);
     }
+    if (old_action != NULL) {
+      *old_action = saved_action;
+    }
     return 0;
   }
 
@@ -192,7 +191,7 @@
   return linked_sigaction(signal, new_action, old_action);
 }
 
-int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
+extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
   const sigset_t* new_set_ptr = bionic_new_set;
   sigset_t tmpset;
   if (bionic_new_set != NULL) {
@@ -224,9 +223,8 @@
   SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
   return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
 }
-}   // extern "C"
 
-void InitializeSignalChain() {
+extern "C" void InitializeSignalChain() {
   // Warning.
   // Don't call this from within a signal context as it makes calls to
   // dlsym.  Calling into the dynamic linker will result in locks being
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
index be69ca4..59a1f1e 100644
--- a/sigchainlib/sigchain.h
+++ b/sigchainlib/sigchain.h
@@ -19,18 +19,14 @@
 
 #include <signal.h>
 
-namespace art {
+extern "C" void InitializeSignalChain();
 
-void InitializeSignalChain();
+extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction);
 
-void ClaimSignalChain(int signal, struct sigaction* oldaction);
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
 
-void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
+extern "C" void UnclaimSignalChain(int signal);
 
-void UnclaimSignalChain(int signal);
-
-void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
-
-}   // namespace art
+extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
 
 #endif  // ART_SIGCHAINLIB_SIGCHAIN_H_
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
new file mode 100644
index 0000000..b0a6ebb
--- /dev/null
+++ b/sigchainlib/sigchain_dummy.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifdef HAVE_ANDROID_OS
+#include <android/log.h>
+#else
+#include <stdarg.h>
+#include <iostream>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "sigchain.h"
+
+static void log(const char* format, ...) {
+  char buf[256];
+  va_list ap;
+  va_start(ap, format);
+  vsnprintf(buf, sizeof(buf), format, ap);
+#ifdef HAVE_ANDROID_OS
+  __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
+#else
+  std::cout << buf << "\n";
+#endif
+  va_end(ap);
+}
+
+extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+  log("ClaimSignalChain is not exported by the main executable.");
+  abort();
+}
+
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
+  log("EnsureFrontOfChain is not exported by the main executable.");
+  abort();
+}
+
+extern "C" void UnclaimSignalChain(int signal) {
+  log("UnclaimSignalChain is not exported by the main executable.");
+  abort();
+}
+
+extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+  log("InvokeUserSignalHandler is not exported by the main executable.");
+  abort();
+}
+
+extern "C" void InitializeSignalChain() {
+  log("InitializeSignalChain is not exported by the main executable.");
+  abort();
+}
diff --git a/sigchainlib/version-script.txt b/sigchainlib/version-script.txt
new file mode 100644
index 0000000..5c72a3e
--- /dev/null
+++ b/sigchainlib/version-script.txt
@@ -0,0 +1,13 @@
+{
+global:
+  ClaimSignalChain;
+  EnsureFrontOfChain;
+  UnclaimSignalChain;
+  InvokeUserSignalHandler;
+  InitializeSignalChain;
+  sigaction;
+  signal;
+  sigprocmask;
+local:
+  *;
+};
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 7929554..1bd3843 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -57,7 +57,7 @@
     }
     LOG(INFO) << "At " << PrettyMethod(m, false);
 
-    NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
+    NativePcOffsetToReferenceMap map(m->GetNativeGcMap(sizeof(void*)));
 
     if (m->IsCalleeSaveMethod()) {
       LOG(WARNING) << "no PC for " << PrettyMethod(m);
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index 30a0d59..2d55cc8 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -59,7 +59,7 @@
     }
     const uint8_t* reg_bitmap = NULL;
     if (!IsShadowFrame()) {
-      NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
+      NativePcOffsetToReferenceMap map(m->GetNativeGcMap(sizeof(void*)));
       reg_bitmap = map.FindBitMap(GetNativePcOffset());
     }
     StringPiece m_name(m->GetName());
diff --git a/test/040-miranda/src/Main.java b/test/040-miranda/src/Main.java
index ff5eba0..65f4fb4 100644
--- a/test/040-miranda/src/Main.java
+++ b/test/040-miranda/src/Main.java
@@ -42,8 +42,8 @@
 
         System.out.println("Test getting miranda method via reflection:");
         try {
-          Class mirandaClass = Class.forName("MirandaAbstract");
-          Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface", (Class[]) null);
+          Class<?> mirandaClass = Class.forName("MirandaAbstract");
+          Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface");
           System.out.println("  did not expect to find miranda method");
         } catch (NoSuchMethodException nsme) {
           System.out.println("  caught expected NoSuchMethodException");
diff --git a/test/040-miranda/src/MirandaAbstract.java b/test/040-miranda/src/MirandaAbstract.java
index 309ecca..c8cfa34 100644
--- a/test/040-miranda/src/MirandaAbstract.java
+++ b/test/040-miranda/src/MirandaAbstract.java
@@ -21,6 +21,8 @@
 {
     protected MirandaAbstract() { }
 
+    // These will be miranda methods, as the interfaces define them, but they are not
+    // implemented in this abstract class:
     //public abstract boolean inInterface();
     //public abstract int inInterface2();
 
diff --git a/test/040-miranda/src/MirandaClass.java b/test/040-miranda/src/MirandaClass.java
index 0d942f0..4160992 100644
--- a/test/040-miranda/src/MirandaClass.java
+++ b/test/040-miranda/src/MirandaClass.java
@@ -22,17 +22,14 @@
     public MirandaClass() {}
 
     public boolean inInterface() {
-        //System.out.println("    MirandaClass inInterface");
         return true;
     }
 
     public int inInterface2() {
-        //System.out.println("    MirandaClass inInterface2");
         return 27;
     }
 
     public boolean inAbstract() {
-        //System.out.println("    MirandaClass inAbstract");
         return false;
     }
 }
diff --git a/test/040-miranda/src/MirandaClass2.java b/test/040-miranda/src/MirandaClass2.java
index e9bdf2b..143eb37 100644
--- a/test/040-miranda/src/MirandaClass2.java
+++ b/test/040-miranda/src/MirandaClass2.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 class MirandaClass2 extends MirandaAbstract {
     public boolean inInterface() {
         return true;
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index ecb3599..fa053fb 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -123,3 +123,17 @@
 fields are .equals
 methods are unique
 methods are .equals
+type1 is a ParameterizedType
+type2 is a ParameterizedType
+type3 is a ParameterizedType
+type1(java.util.Set<java.lang.String>) equals type2(java.util.Set<java.lang.String>)
+type1(java.util.Set<java.lang.String>) equals type3(java.util.Set<java.lang.String>)
+type1(java.util.Set<java.lang.String>) hashCode equals type2(java.util.Set<java.lang.String>) hashCode
+type1(java.util.Set<java.lang.String>) hashCode equals type3(java.util.Set<java.lang.String>) hashCode
+type1 is a GenericArrayType
+type2 is a GenericArrayType
+type3 is a GenericArrayType
+type1(T[]) equals type2(T[])
+type1(T[]) equals type3(T[])
+type1(T[]) hashCode equals type2(T[]) hashCode
+type1(T[]) hashCode equals type3(T[]) hashCode
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index 3e6d700..11eb773 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -18,8 +18,10 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Reflection test.
@@ -579,6 +581,118 @@
         }
     }
 
+    public static void checkParametrizedTypeEqualsAndHashCode() {
+        Method method1;
+        Method method2;
+        Method method3;
+        try {
+            method1 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
+            method2 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
+            method3 = ParametrizedTypeTest.class.getDeclaredMethod("aMethodIdentical", Set.class);
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+
+        List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
+        List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
+        List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
+
+        Type type1 = types1.get(0);
+        Type type2 = types2.get(0);
+        Type type3 = types3.get(0);
+
+        if (type1 instanceof ParameterizedType) {
+            System.out.println("type1 is a ParameterizedType");
+        }
+        if (type2 instanceof ParameterizedType) {
+            System.out.println("type2 is a ParameterizedType");
+        }
+        if (type3 instanceof ParameterizedType) {
+            System.out.println("type3 is a ParameterizedType");
+        }
+
+        if (type1.equals(type2)) {
+            System.out.println("type1("+type1+") equals type2("+type2+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type2("+type2+")");
+        }
+
+        if (type1.equals(type3)) {
+            System.out.println("type1("+type1+") equals type3("+type3+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type3("+type3+")");
+        }
+        if (type1.hashCode() == type2.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
+        } else {
+            System.out.println(
+                   "type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
+        }
+
+        if (type1.hashCode() == type3.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
+        } else {
+            System.out.println(
+                    "type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
+        }
+    }
+
+    public static void checkGenericArrayTypeEqualsAndHashCode() {
+        Method method1;
+        Method method2;
+        Method method3;
+        try {
+            method1 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
+            method2 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
+            method3 = GenericArrayTypeTest.class.getDeclaredMethod("aMethodIdentical", Object[].class);
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+
+        List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
+        List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
+        List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
+
+        Type type1 = types1.get(0);
+        Type type2 = types2.get(0);
+        Type type3 = types3.get(0);
+
+        if (type1 instanceof GenericArrayType) {
+            System.out.println("type1 is a GenericArrayType");
+        }
+        if (type2 instanceof GenericArrayType) {
+            System.out.println("type2 is a GenericArrayType");
+        }
+        if (type3 instanceof GenericArrayType) {
+            System.out.println("type3 is a GenericArrayType");
+        }
+
+        if (type1.equals(type2)) {
+            System.out.println("type1("+type1+") equals type2("+type2+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type2("+type2+")");
+        }
+
+        if (type1.equals(type3)) {
+            System.out.println("type1("+type1+") equals type3("+type3+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type3("+type3+")");
+        }
+        if (type1.hashCode() == type2.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
+        } else {
+            System.out.println(
+                   "type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
+        }
+
+        if (type1.hashCode() == type3.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
+        } else {
+            System.out.println(
+                    "type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
+        }
+    }
+
     public static void main(String[] args) throws Exception {
         Main test = new Main();
         test.run();
@@ -589,6 +703,8 @@
         checkClinitForMethods();
         checkGeneric();
         checkUnique();
+        checkParametrizedTypeEqualsAndHashCode();
+        checkGenericArrayTypeEqualsAndHashCode();
     }
 }
 
@@ -696,3 +812,13 @@
     throw new UnsupportedOperationException();
   }
 }
+
+class ParametrizedTypeTest {
+    public void aMethod(Set<String> names) {}
+    public void aMethodIdentical(Set<String> names) {}
+}
+
+class GenericArrayTypeTest<T> {
+    public void aMethod(T[] names) {}
+    public void aMethodIdentical(T[] names) {}
+}
diff --git a/test/051-thread/expected.txt b/test/051-thread/expected.txt
index 943d1df..54e34af 100644
--- a/test/051-thread/expected.txt
+++ b/test/051-thread/expected.txt
@@ -9,4 +9,6 @@
 testSetName starting
 testSetName running
 testSetName finished
+testThreadPriorities starting
+testThreadPriorities finished
 thread test done
diff --git a/test/051-thread/src/Main.java b/test/051-thread/src/Main.java
index 390685d..b81273e 100644
--- a/test/051-thread/src/Main.java
+++ b/test/051-thread/src/Main.java
@@ -20,12 +20,17 @@
  * Test some basic thread stuff.
  */
 public class Main {
+    static {
+        System.loadLibrary("arttest");
+    }
+
     public static void main(String[] args) throws Exception {
         System.out.println("thread test starting");
         testThreadCapacity();
         testThreadDaemons();
         testSleepZero();
         testSetName();
+        testThreadPriorities();
         System.out.println("thread test done");
     }
 
@@ -133,4 +138,53 @@
         }
         System.out.print("testSetName finished\n");
     }
+
+    private static void testThreadPriorities() throws Exception {
+        System.out.print("testThreadPriorities starting\n");
+
+        PriorityStoringThread t1 = new PriorityStoringThread(false);
+        t1.setPriority(Thread.MAX_PRIORITY);
+        t1.start();
+        t1.join();
+        if (supportsThreadPriorities() && (t1.getNativePriority() != Thread.MAX_PRIORITY)) {
+            System.out.print("thread priority for t1 was " + t1.getNativePriority() +
+                " [expected Thread.MAX_PRIORITY]\n");
+        }
+
+        PriorityStoringThread t2 = new PriorityStoringThread(true);
+        t2.start();
+        t2.join();
+        if (supportsThreadPriorities() && (t2.getNativePriority() != Thread.MAX_PRIORITY)) {
+            System.out.print("thread priority for t2 was " + t2.getNativePriority() +
+                " [expected Thread.MAX_PRIORITY]\n");
+        }
+
+        System.out.print("testThreadPriorities finished\n");
+    }
+
+    private static native int getNativePriority();
+    private static native boolean supportsThreadPriorities();
+
+    static class PriorityStoringThread extends Thread {
+        private final boolean setPriority;
+        private volatile int nativePriority;
+
+        public PriorityStoringThread(boolean setPriority) {
+            this.setPriority = setPriority;
+            this.nativePriority = -1;
+        }
+
+        @Override
+        public void run() {
+            if (setPriority) {
+                setPriority(Thread.MAX_PRIORITY);
+            }
+
+            nativePriority = Main.getNativePriority();
+        }
+
+        public int getNativePriority() {
+            return nativePriority;
+        }
+    }
 }
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
new file mode 100644
index 0000000..2f5fffc
--- /dev/null
+++ b/test/051-thread/thread_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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 "jni.h"
+#include "thread-inl.h"
+
+namespace art {
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env, jclass) {
+  return ThreadForEnv(env)->GetNativePriority();
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(JNIEnv* env, jclass) {
+#if defined(HAVE_ANDROID_OS)
+  return JNI_TRUE;
+#else
+  return JNI_FALSE;
+#endif
+}
+
+}  // namespace art
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 56972ff..84405cc 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -125,7 +125,11 @@
     Assert.assertEquals('N', testStr.charAt(0));
     Assert.assertEquals('o', testStr.charAt(1));
     Assert.assertEquals(' ', testStr.charAt(10));
-    Assert.assertEquals('e', testStr.charAt(testStr.length()-1));
+    Assert.assertEquals('e', testStr.charAt(14));  // 14 = testStr.length()-1 as a constant.
+    Assert.assertEquals('N', test_String_charAt_inner(testStr, 0));
+    Assert.assertEquals('o', test_String_charAt_inner(testStr, 1));
+    Assert.assertEquals(' ', test_String_charAt_inner(testStr, 10));
+    Assert.assertEquals('e', test_String_charAt_inner(testStr, testStr.length()-1));
 
     try {
       testStr.charAt(-1);
@@ -137,6 +141,33 @@
       Assert.fail();
     } catch (StringIndexOutOfBoundsException expected) {
     }
+    try {
+      testStr.charAt(15);  // 15 = "Now is the time".length()
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+    try {
+      test_String_charAt_inner(testStr, -1);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+    try {
+      test_String_charAt_inner(testStr, 80);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+    try {
+      test_String_charAt_inner(testStr, 15);  // 15 = "Now is the time".length()
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+
+    String strEmpty = "";
+    try {
+      strEmpty.charAt(0);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
 
     String strNull = null;
     try {
@@ -146,6 +177,11 @@
     }
   }
 
+  private static char test_String_charAt_inner(String s, int index) {
+    // Using non-constant index here (assuming that this method wasn't inlined).
+    return s.charAt(index);
+  }
+
   static int start;
   private static int[] negIndex = { -100000 };
   public static void test_String_indexOf() {
diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index 5251c17..82d36e3 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -1,3 +1,4 @@
+b17325447 passes
 b17630605 passes
 b17411468 passes
 b2296099 passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 8010711..ab0febf 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -30,6 +30,7 @@
     }
 
     public static void main(String args[]) throws Exception {
+        b17325447();
         b17630605();
         b17411468();
         b2296099Test();
@@ -63,6 +64,31 @@
         minDoubleWith3ConstsTest();
     }
 
+    public static double b17325447_i1(int i1, double f) {
+      return f;
+    }
+
+    public static double b17325447_i2(int i1, int i2, double f) {
+      return f;
+    }
+
+    public static double b17325447_i3(int i1, int i2, int i3, double f) {
+      return f;
+    }
+
+    public static void b17325447() {
+      // b/17325447 - x86 handling of special identity method w/ double spanning reg/mem.
+      double d = 0.0;
+      d += b17325447_i1(123, 1.0);
+      d += b17325447_i2(123, 456, 2.0);
+      d += b17325447_i3(123, 456, 789, 3.0);
+      if (d == 6.0) {
+        System.out.println("b17325447 passes");
+      } else {
+        System.out.println("b17325447 fails: " + d);
+      }
+    }
+
     public static void b17630605() {
       // b/17630605 - failure to properly handle min long immediates.
       long a1 = 40455547223404749L;
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 1af4121..8fdeccc 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -32,7 +32,7 @@
 62 (class java.lang.Long)
 14 (class java.lang.Short)
 [public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)]
-[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final long java.lang.String.serialVersionUID, private static final char java.lang.String.REPLACEMENT_CHAR]
+[private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private final char[] java.lang.String.value, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID]
 [void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)]
 []
 [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index a5eedc6..16a71e4 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -1,3 +1,4 @@
+Code cache exists: './code_cache'.
 Native bridge initialized.
 Checking for getEnvValues.
 Ready for native bridge tests.
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 442f99c..23145e3 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -23,6 +23,7 @@
 #include "jni.h"
 #include "stdio.h"
 #include "unistd.h"
+#include "sys/stat.h"
 
 #include "nativebridge/native_bridge.h"
 
@@ -208,7 +209,13 @@
 
 // NativeBridgeCallbacks implementations
 extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* art_cbs,
-                                         const char* private_dir, const char* isa) {
+                                         const char* app_code_cache_dir, const char* isa) {
+  struct stat st;
+  if ((app_code_cache_dir != nullptr)
+      && (stat(app_code_cache_dir, &st) == 0)
+      && S_ISDIR(st.st_mode)) {
+    printf("Code cache exists: '%s'.\n", app_code_cache_dir);
+  }
   if (art_cbs != nullptr) {
     gNativeBridgeArtCallbacks = art_cbs;
     printf("Native bridge initialized.\n");
diff --git a/test/122-npe/expected.txt b/test/122-npe/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/122-npe/expected.txt
diff --git a/test/122-npe/info.txt b/test/122-npe/info.txt
new file mode 100644
index 0000000..eef46d8
--- /dev/null
+++ b/test/122-npe/info.txt
@@ -0,0 +1 @@
+Test that our NPE checks and stack traces work.
diff --git a/test/122-npe/src/Main.java b/test/122-npe/src/Main.java
new file mode 100644
index 0000000..8f68205
--- /dev/null
+++ b/test/122-npe/src/Main.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+/**
+ * Test that null pointer exceptions are thrown by the VM.
+ */
+public class Main {
+  private int f;
+  public static void main(String[] args) {
+    methodOne();
+  }
+
+  static void methodOne() {
+    methodTwo();
+  }
+
+  private int callSpecial() {
+    return f;
+  }
+
+  final int callFinal() {
+    return f;
+  }
+
+  static void methodTwo() {
+    NullPointerException npe = null;
+
+    int thisLine = 41;
+
+    new Object().getClass(); // Ensure compiled.
+    try {
+      ((Object) null).getClass();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 4);
+
+    new Main().callSpecial();  // Ensure compiled.
+    try {
+      ((Main) null).callSpecial();  // Test invokespecial.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    new Main().callFinal();  // Ensure compiled.
+    try {
+      ((Main) null).callFinal();  // Test invokevirtual on final.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    try {
+      ((Value) null).objectField.toString();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).intField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useFloat(((Value) null).floatField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useLong(((Value) null).longField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useDouble(((Value) null).doubleField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).objectField = "Fisk";
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).intField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).floatField = 42.0F;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).longField = 42L;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).doubleField = 42.0d;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).byteField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      if (((Value) null).booleanField) { }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).charField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).shortField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).byteField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).booleanField = true;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).charField = '\u0042';
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).shortField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileObjectField.toString();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileObjectField = "Fisk";
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileIntField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileIntField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useFloat(((Value) null).volatileFloatField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileFloatField = 42.0F;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useLong(((Value) null).volatileLongField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileLongField = 42L;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useDouble(((Value) null).volatileDoubleField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileDoubleField = 42.0d;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileByteField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileByteField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      if (((Value) null).volatileBooleanField) { }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileBooleanField = true;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileCharField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileCharField = '\u0042';
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileShortField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileShortField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Object[]) null)[0].toString();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((int[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useFloat(((float[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useLong(((long[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useDouble(((double[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Object[]) null)[0] = "Fisk";
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((int[]) null)[0] = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((float[]) null)[0] = 42.0F;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((long[]) null)[0] = 42L;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((double[]) null)[0] = 42.0d;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((byte[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      if (((boolean[]) null)[0]) { }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((char[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((short[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((byte[]) null)[0] = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((boolean[]) null)[0] = true;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((char[]) null)[0] = '\u0042';
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((short[]) null)[0] = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Object[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((int[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((float[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((long[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((double[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((byte[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((boolean[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((char[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((short[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      Interface i = null;
+      i.methodInterface();  // Test null on invokeinterface.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    try {
+      Object o = null;
+      o.toString();  // Test null on invokevirtual.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    npe = null;
+    try {
+      String s = null;
+      try {
+        throw new AssertionError();
+      } finally {
+        // Cause an implicit NPE.
+        s.getClass();
+      }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 13);
+
+    npe = null;
+    try {
+      String s = null;
+      try {
+        throw new AssertionError();
+      } catch (AssertionError ex) {
+      }
+      s.getClass();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 14);
+  }
+
+  static void check(NullPointerException npe, int firstLine) {
+    final boolean debug = false;
+    if (debug) {
+      System.out.print("Got to line ");
+      System.out.print(firstLine);
+      System.out.println();
+    }
+    StackTraceElement[] trace = npe.getStackTrace();
+    checkElement(trace[0], "Main", "methodTwo", "Main.java", firstLine);
+    checkElement(trace[1], "Main", "methodOne", "Main.java", 27);
+    checkElement(trace[2], "Main", "main", "Main.java", 23);
+  }
+
+  static void checkElement(StackTraceElement element,
+                                  String declaringClass, String methodName,
+                                  String fileName, int lineNumber) {
+    assertEquals(declaringClass, element.getClassName());
+    assertEquals(methodName, element.getMethodName());
+    assertEquals(fileName, element.getFileName());
+    assertEquals(lineNumber, element.getLineNumber());
+  }
+
+  static void assertEquals(Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+      throw new AssertionError(msg);
+    }
+  }
+
+  static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  interface Interface {
+    void methodInterface();
+  }
+
+  static void useInt(int i) {
+  }
+
+  static void useFloat(float f) {
+  }
+
+  static void useDouble(double d) {
+  }
+
+  static void useLong(long l) {
+  }
+
+  static class Value {
+    Object objectField;
+    int intField;
+    float floatField;
+    long longField;
+    double doubleField;
+    byte byteField;
+    boolean booleanField;
+    char charField;
+    short shortField;
+
+    volatile Object volatileObjectField;
+    volatile int volatileIntField;
+    volatile float volatileFloatField;
+    volatile long volatileLongField;
+    volatile double volatileDoubleField;
+    volatile byte volatileByteField;
+    volatile boolean volatileBooleanField;
+    volatile char volatileCharField;
+    volatile short volatileShortField;
+  }
+}
diff --git a/test/122-secondarydex/build b/test/122-secondarydex/build
new file mode 100755
index 0000000..712774f
--- /dev/null
+++ b/test/122-secondarydex/build
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+mv classes/Super.class classes-ex
+
+if [ ${NEED_DEX} = "true" ]; then
+  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+  zip $TEST_NAME.jar classes.dex
+  ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+  zip ${TEST_NAME}-ex.jar classes.dex
+fi
diff --git a/test/122-secondarydex/expected.txt b/test/122-secondarydex/expected.txt
new file mode 100644
index 0000000..29a1411
--- /dev/null
+++ b/test/122-secondarydex/expected.txt
@@ -0,0 +1,3 @@
+testSlowPathDirectInvoke
+Test
+Got null pointer exception
diff --git a/test/122-secondarydex/info.txt b/test/122-secondarydex/info.txt
new file mode 100644
index 0000000..0479d1a
--- /dev/null
+++ b/test/122-secondarydex/info.txt
@@ -0,0 +1,3 @@
+Test features with a secondary dex file.
+
+- Regression test to ensure slow path of direct invoke does null check.
diff --git a/test/122-secondarydex/run b/test/122-secondarydex/run
new file mode 100755
index 0000000..d8c3c79
--- /dev/null
+++ b/test/122-secondarydex/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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 secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/test/122-secondarydex/src/Main.java b/test/122-secondarydex/src/Main.java
new file mode 100644
index 0000000..c921c5b
--- /dev/null
+++ b/test/122-secondarydex/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Secondary dex file test.
+ */
+public class Main {
+    public static void main(String[] args) {
+        testSlowPathDirectInvoke();
+    }
+
+    public static void testSlowPathDirectInvoke() {
+        System.out.println("testSlowPathDirectInvoke");
+        try {
+            Test t1 = new Test();
+            Test t2 = new Test();
+            Test t3 = null;
+            t1.test(t2);
+            t1.test(t3);
+        } catch (NullPointerException npe) {
+            System.out.println("Got null pointer exception");
+        } catch (Exception e) {
+            System.out.println("Got unexpected exception " + e);
+        }
+    }
+}
diff --git a/test/122-secondarydex/src/Super.java b/test/122-secondarydex/src/Super.java
new file mode 100644
index 0000000..7608d4a
--- /dev/null
+++ b/test/122-secondarydex/src/Super.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Super {
+    private void print() {
+        System.out.println("Super");
+    }
+}
diff --git a/test/122-secondarydex/src/Test.java b/test/122-secondarydex/src/Test.java
new file mode 100644
index 0000000..82cb901
--- /dev/null
+++ b/test/122-secondarydex/src/Test.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Test extends Super {
+    public void test(Test t) {
+        t.print();
+    }
+
+    private void print() {
+        System.out.println("Test");
+    }
+}
diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build
new file mode 100644
index 0000000..4c30f3f
--- /dev/null
+++ b/test/126-miranda-multidex/build
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+
+# All except Main
+${JAVAC} -d classes `find src -name '*.java'`
+rm classes/MirandaInterface.class
+${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+
+# Only Main
+${JAVAC} -d classes `find src -name '*.java'`
+rm classes/Main.class classes/MirandaAbstract.class classes/MirandaClass*.class classes/MirandaInterface2*.class
+${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes
+
+zip $TEST_NAME.jar classes.dex classes2.dex
diff --git a/test/126-miranda-multidex/expected.txt b/test/126-miranda-multidex/expected.txt
new file mode 100644
index 0000000..dbe3717
--- /dev/null
+++ b/test/126-miranda-multidex/expected.txt
@@ -0,0 +1,32 @@
+MirandaClass:
+  inInterface:  true
+  inInterface2: 27
+  inAbstract:   false
+MirandaAbstract / MirandaClass:
+  inInterface:  true
+  inInterface2: 27
+  inAbstract:   false
+true 27
+MirandaAbstract / MirandaClass2:
+  inInterface:  true
+  inInterface2: 28
+  inAbstract:   true
+true 28
+Test getting miranda method via reflection:
+  caught expected NoSuchMethodException
+MirandaClass:
+  inInterface:  true
+  inInterface2: 27
+  inAbstract:   false
+MirandaAbstract / MirandaClass:
+  inInterface:  true
+  inInterface2: 27
+  inAbstract:   false
+true 27
+MirandaAbstract / MirandaClass2:
+  inInterface:  true
+  inInterface2: 28
+  inAbstract:   true
+true 28
+Test getting miranda method via reflection:
+  caught expected NoSuchMethodException
diff --git a/test/126-miranda-multidex/info.txt b/test/126-miranda-multidex/info.txt
new file mode 100644
index 0000000..ac50e2e
--- /dev/null
+++ b/test/126-miranda-multidex/info.txt
@@ -0,0 +1,2 @@
+This test ensures that cross-dex-file Miranda methods are correctly resolved.
+See b/18193682 for details.
diff --git a/test/126-miranda-multidex/run b/test/126-miranda-multidex/run
new file mode 100755
index 0000000..23c9935
--- /dev/null
+++ b/test/126-miranda-multidex/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+${RUN} $@
+
+# The problem was first exposed in a no-verify setting, as that changes the resolution path
+# taken. Make sure we also test in that environment.
+${RUN} --no-verify ${@}
diff --git a/test/126-miranda-multidex/src/Main.java b/test/126-miranda-multidex/src/Main.java
new file mode 100644
index 0000000..8624378
--- /dev/null
+++ b/test/126-miranda-multidex/src/Main.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Miranda testing.
+ */
+public class Main {
+    public static void main(String[] args) {
+        MirandaClass mir = new MirandaClass();
+        System.out.println("MirandaClass:");
+        System.out.println("  inInterface:  " + mir.inInterface());
+        System.out.println("  inInterface2: " + mir.inInterface2());
+        System.out.println("  inAbstract:   " + mir.inAbstract());
+
+        /* try again through abstract class; results should be identical */
+        MirandaAbstract mira = mir;
+        System.out.println("MirandaAbstract / MirandaClass:");
+        System.out.println("  inInterface:  " + mira.inInterface());
+        System.out.println("  inInterface2: " + mira.inInterface2());
+        System.out.println("  inAbstract:   " + mira.inAbstract());
+        mira.callMiranda();
+
+        MirandaAbstract mira2 = new MirandaClass2();
+        System.out.println("MirandaAbstract / MirandaClass2:");
+        System.out.println("  inInterface:  " + mira2.inInterface());
+        System.out.println("  inInterface2: " + mira2.inInterface2());
+        System.out.println("  inAbstract:   " + mira2.inAbstract());
+        mira2.callMiranda();
+
+        System.out.println("Test getting miranda method via reflection:");
+        try {
+          Class<?> mirandaClass = Class.forName("MirandaAbstract");
+          Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface");
+          System.out.println("  did not expect to find miranda method");
+        } catch (NoSuchMethodException nsme) {
+          System.out.println("  caught expected NoSuchMethodException");
+        } catch (Exception e) {
+          System.out.println("  caught unexpected exception " + e);
+        }
+    }
+}
diff --git a/test/126-miranda-multidex/src/MirandaAbstract.java b/test/126-miranda-multidex/src/MirandaAbstract.java
new file mode 100644
index 0000000..c09a61f
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaAbstract.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public abstract class MirandaAbstract implements MirandaInterface, MirandaInterface2
+{
+    protected MirandaAbstract() { }
+
+    // These will be miranda methods, as the interfaces define them, but they are not
+    // implemented in this abstract class:
+    //public abstract boolean inInterface();
+    //public abstract int inInterface2();
+
+    public boolean inAbstract() {
+        return true;
+    }
+
+    public void callMiranda() {
+        System.out.println(inInterface() + " " + inInterface2());
+    }
+}
diff --git a/test/126-miranda-multidex/src/MirandaClass.java b/test/126-miranda-multidex/src/MirandaClass.java
new file mode 100644
index 0000000..7bb37e7
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaClass.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public class MirandaClass extends MirandaAbstract {
+
+    public MirandaClass() {}
+
+    public boolean inInterface() {
+        return true;
+    }
+
+    public int inInterface2() {
+        return 27;
+    }
+
+    public boolean inAbstract() {
+        return false;
+    }
+
+    // Better not hit any of these...
+    public void inInterfaceDummy1() {
+        System.out.println("inInterfaceDummy1");
+    }
+    public void inInterfaceDummy2() {
+        System.out.println("inInterfaceDummy2");
+    }
+    public void inInterfaceDummy3() {
+        System.out.println("inInterfaceDummy3");
+    }
+    public void inInterfaceDummy4() {
+        System.out.println("inInterfaceDummy4");
+    }
+    public void inInterfaceDummy5() {
+        System.out.println("inInterfaceDummy5");
+    }
+}
diff --git a/test/126-miranda-multidex/src/MirandaClass2.java b/test/126-miranda-multidex/src/MirandaClass2.java
new file mode 100644
index 0000000..797ead2
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaClass2.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class MirandaClass2 extends MirandaAbstract {
+    public boolean inInterface() {
+        return true;
+    }
+
+    public int inInterface2() {
+        return 28;
+    }
+
+    // Better not hit any of these...
+    public void inInterfaceDummy1() {
+        System.out.println("inInterfaceDummy1");
+    }
+    public void inInterfaceDummy2() {
+        System.out.println("inInterfaceDummy2");
+    }
+    public void inInterfaceDummy3() {
+        System.out.println("inInterfaceDummy3");
+    }
+    public void inInterfaceDummy4() {
+        System.out.println("inInterfaceDummy4");
+    }
+    public void inInterfaceDummy5() {
+        System.out.println("inInterfaceDummy5");
+    }
+}
diff --git a/test/126-miranda-multidex/src/MirandaInterface.java b/test/126-miranda-multidex/src/MirandaInterface.java
new file mode 100644
index 0000000..df12fcc
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaInterface.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface {
+
+    public boolean inInterface();
+
+    // A couple of dummy methods to fill the method table.
+    public void inInterfaceDummy1();
+    public void inInterfaceDummy2();
+    public void inInterfaceDummy3();
+    public void inInterfaceDummy4();
+    public void inInterfaceDummy5();
+
+}
diff --git a/test/126-miranda-multidex/src/MirandaInterface2.java b/test/126-miranda-multidex/src/MirandaInterface2.java
new file mode 100644
index 0000000..7c93fd0
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaInterface2.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface2 {
+
+    public boolean inInterface();
+
+    public int inInterface2();
+
+}
diff --git a/test/128-reg-spilling-on-implicit-nullcheck/expected.txt b/test/128-reg-spilling-on-implicit-nullcheck/expected.txt
new file mode 100644
index 0000000..9bdf6588
--- /dev/null
+++ b/test/128-reg-spilling-on-implicit-nullcheck/expected.txt
@@ -0,0 +1 @@
+t7q = 2
diff --git a/test/128-reg-spilling-on-implicit-nullcheck/info.txt b/test/128-reg-spilling-on-implicit-nullcheck/info.txt
new file mode 100644
index 0000000..18b2112
--- /dev/null
+++ b/test/128-reg-spilling-on-implicit-nullcheck/info.txt
@@ -0,0 +1 @@
+This is a compiler reggression test for missing reg spilling on implicit nullcheck.
diff --git a/test/128-reg-spilling-on-implicit-nullcheck/src/Main.java b/test/128-reg-spilling-on-implicit-nullcheck/src/Main.java
new file mode 100644
index 0000000..48276bf
--- /dev/null
+++ b/test/128-reg-spilling-on-implicit-nullcheck/src/Main.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+    public static void main(String[] args) {
+        int t7q = 0;
+        long q = 1L;
+
+        try {
+            for (int i = 1; i < 8; i++) {
+                t7q = (--t7q);
+                TestClass f = null;
+                t7q = f.field;
+            }
+        }
+        catch (NullPointerException wpw) {
+            q++;
+        }
+        finally {
+            t7q += (int)(1 - ((q - q) - 2));
+        }
+
+        System.out.println("t7q = " + t7q);
+    }
+}
+
+class TestClass {
+    public int field;
+    public void meth() {field = 1;}
+}
diff --git a/test/131-structural-change/build b/test/131-structural-change/build
new file mode 100755
index 0000000..7ddc81d
--- /dev/null
+++ b/test/131-structural-change/build
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+${JAVAC} -d classes-ex `find src-ex -name '*.java'`
+
+if [ ${NEED_DEX} = "true" ]; then
+  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+  zip $TEST_NAME.jar classes.dex
+  ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+  zip ${TEST_NAME}-ex.jar classes.dex
+fi
diff --git a/test/131-structural-change/expected.txt b/test/131-structural-change/expected.txt
new file mode 100644
index 0000000..79facd5
--- /dev/null
+++ b/test/131-structural-change/expected.txt
@@ -0,0 +1,2 @@
+Should really reach here.
+Got expected error.
diff --git a/test/131-structural-change/info.txt b/test/131-structural-change/info.txt
new file mode 100644
index 0000000..6d5817b
--- /dev/null
+++ b/test/131-structural-change/info.txt
@@ -0,0 +1 @@
+Check whether a structural change in a (non-native) multi-dex scenario is detected.
diff --git a/test/131-structural-change/run b/test/131-structural-change/run
new file mode 100755
index 0000000..63fdb8c
--- /dev/null
+++ b/test/131-structural-change/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/test/131-structural-change/src-ex/A.java b/test/131-structural-change/src-ex/A.java
new file mode 100644
index 0000000..800347b
--- /dev/null
+++ b/test/131-structural-change/src-ex/A.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class A {
+    public void bar() {
+    }
+}
diff --git a/test/131-structural-change/src-ex/B.java b/test/131-structural-change/src-ex/B.java
new file mode 100644
index 0000000..61369db
--- /dev/null
+++ b/test/131-structural-change/src-ex/B.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class B extends A {
+    public void test() {
+        System.out.println("Should not reach this!");
+    }
+}
diff --git a/test/131-structural-change/src/A.java b/test/131-structural-change/src/A.java
new file mode 100644
index 0000000..b07de58
--- /dev/null
+++ b/test/131-structural-change/src/A.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class A {
+    public void foo() {
+    }
+
+    public void bar() {
+    }
+
+    public void baz() {
+    }
+}
diff --git a/test/131-structural-change/src/Main.java b/test/131-structural-change/src/Main.java
new file mode 100644
index 0000000..c94843e
--- /dev/null
+++ b/test/131-structural-change/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Structural hazard test.
+ */
+public class Main {
+    public static void main(String[] args) {
+        new Main().run();
+    }
+
+    private void run() {
+        try {
+            Class<?> bClass = getClass().getClassLoader().loadClass("A");
+            System.out.println("Should really reach here.");
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+
+        try {
+            Class<?> bClass = getClass().getClassLoader().loadClass("B");
+            System.out.println("Should not reach here.");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected error.");
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+
+    }
+
+}
diff --git a/test/132-daemon-locks-shutdown/expected.txt b/test/132-daemon-locks-shutdown/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/expected.txt
diff --git a/test/132-daemon-locks-shutdown/info.txt b/test/132-daemon-locks-shutdown/info.txt
new file mode 100644
index 0000000..f804064
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/info.txt
@@ -0,0 +1 @@
+Tests that we can shut down the runtime with daemons still looping over locks.
diff --git a/test/132-daemon-locks-shutdown/src/Main.java b/test/132-daemon-locks-shutdown/src/Main.java
new file mode 100644
index 0000000..b5bbc8c
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test that daemon threads still contending for a lock don't make the runtime abort on shutdown.
+ */
+public class Main {
+
+    public final static int THREAD_COUNT = 32;
+
+    public static void main(String[] args) throws Exception {
+        Object sync = new Object();
+
+        for (int i = 0; i < THREAD_COUNT; i++) {
+            Thread t = new Thread(new Wait(sync));
+            t.setDaemon(true);
+            t.start();
+        }
+    }
+
+    private static class Wait implements Runnable {
+        private Object obj;
+
+        public Wait(Object obj) {
+            this.obj = obj;
+        }
+
+        public void run() {
+            for (;;) {
+                synchronized(obj) {
+                    try {
+                        obj.wait(1);
+                    } catch (Exception exc) {
+                        exc.printStackTrace(System.out);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/test/133-static-invoke-super/expected.txt b/test/133-static-invoke-super/expected.txt
new file mode 100644
index 0000000..089d8e8
--- /dev/null
+++ b/test/133-static-invoke-super/expected.txt
@@ -0,0 +1,3 @@
+basis: performed 50000000 iterations
+test1: performed 50000000 iterations
+Timing is acceptable.
diff --git a/test/133-static-invoke-super/info.txt b/test/133-static-invoke-super/info.txt
new file mode 100644
index 0000000..606331b
--- /dev/null
+++ b/test/133-static-invoke-super/info.txt
@@ -0,0 +1,2 @@
+This is a performance test of invoking static methods in super class. To see the numbers, invoke
+this test with the "--timing" option.
diff --git a/test/133-static-invoke-super/run b/test/133-static-invoke-super/run
new file mode 100755
index 0000000..e27a622
--- /dev/null
+++ b/test/133-static-invoke-super/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# As this is a performance test we always use the non-debug build.
+exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/133-static-invoke-super/src/Main.java b/test/133-static-invoke-super/src/Main.java
new file mode 100644
index 0000000..7cfd099
--- /dev/null
+++ b/test/133-static-invoke-super/src/Main.java
@@ -0,0 +1,63 @@
+
+public class Main {
+    static class SuperClass {
+      protected static int getVar(int w) {
+          return w & 0xF;
+      }
+    }
+    static class SubClass extends SuperClass {
+      final int getVarDirect(int w) {
+        return w & 0xF;
+      }
+      public void testDirect(int max) {
+        for (int i = 0; i < max; ++i) {
+          getVarDirect(max);
+        }
+      }
+      public void testStatic(int max) {
+        for (int i = 0; i < max; ++i) {
+          getVar(max);
+        }
+      }
+    }
+
+    static public void main(String[] args) throws Exception {
+        boolean timing = (args.length >= 1) && args[0].equals("--timing");
+        run(timing);
+    }
+
+    static int testBasis(int interations) {
+      (new SubClass()).testDirect(interations);
+      return interations;
+    }
+
+    static int testStatic(int interations) {
+      (new SubClass()).testStatic(interations);
+      return interations;
+    }
+
+    static public void run(boolean timing) {
+        long time0 = System.nanoTime();
+        int count1 = testBasis(50000000);
+        long time1 = System.nanoTime();
+        int count2 = testStatic(50000000);
+        long time2 = System.nanoTime();
+
+        System.out.println("basis: performed " + count1 + " iterations");
+        System.out.println("test1: performed " + count2 + " iterations");
+
+        double basisMsec = (time1 - time0) / (double) count1 / 1000000;
+        double msec1 = (time2 - time1) / (double) count2 / 1000000;
+
+        if (msec1 < basisMsec * 5) {
+            System.out.println("Timing is acceptable.");
+        } else {
+            System.out.println("Iterations are taking too long!");
+            timing = true;
+        }
+        if (timing) {
+            System.out.printf("basis time: %.3g msec\n", basisMsec);
+            System.out.printf("test1: %.3g msec per iteration\n", msec1);
+        }
+    }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 2d139a6..2e12335 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -24,6 +24,7 @@
   004-ReferenceMap/stack_walk_refmap_jni.cc \
   004-StackWalk/stack_walk_jni.cc \
   004-UnsafeTest/unsafe_test.cc \
+  051-thread/thread_test.cc \
   116-nodex2oat/nodex2oat.cc \
   117-nopatchoat/nopatchoat.cc \
   118-noimage-dex2oat/noimage-dex2oat.cc
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 4ff6c65..50b8236 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -53,7 +53,8 @@
 # Tests that are timing sensitive and flaky on heavily loaded systems.
 TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
   053-wait-some \
-  055-enum-performance
+  055-enum-performance \
+  133-static-invoke-super
 
  # disable timing sensitive tests on "dist" builds.
 ifdef dist_goal
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 7253a2b..efc2001 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -23,8 +23,11 @@
 FLAGS=""
 COMPILER_FLAGS=""
 BUILD_BOOT_OPT=""
+SECONDARY_DEX=""
 exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
 main="Main"
+DEX_VERIFY=""
+DEX2OAT_SWAP="n"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -91,6 +94,9 @@
     elif [ "x$1" = "x--relocate" ]; then
         RELOCATE="y"
         shift
+    elif [ "x$1" = "x--secondary" ]; then
+        SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+        shift
     elif [ "x$1" = "x-Xcompiler-option" ]; then
         shift
         option="$1"
@@ -102,6 +108,9 @@
         option="$1"
         FLAGS="${FLAGS} $option"
         shift
+    elif [ "x$1" = "x--dex2oat-swap" ]; then
+        DEX2OAT_SWAP="y"
+        shift
     elif [ "x$1" = "x--" ]; then
         shift
         break
@@ -132,6 +141,10 @@
 export LD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
 export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
 
+if [ "$DEX2OAT_SWAP" = "y" ]; then
+  COMPILER_FLAGS="${COMPILER_FLAGS} --swap-file=$ANDROID_DATA/dex2oat.swap"
+fi
+
 if [ "$DEBUGGER" = "y" ]; then
     PORT=8000
     msg "Waiting for jdb to connect:"
@@ -154,7 +167,12 @@
 
 if [ "$INTERPRETER" = "y" ]; then
     INT_OPTS="-Xint"
-    COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
+    if [ "$VERIFY" = "y" ] ; then
+      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
+    else
+      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=verify-none"
+      DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
+    fi
 fi
 
 if [ "$RELOCATE" = "y" ]; then
@@ -180,7 +198,7 @@
 fi
 
 JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar $main"
+cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $DEX_VERIFY $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $main"
 if [ "$DEV_MODE" = "y" ]; then
   if [ "$PREBUILD" = "y" ]; then
     echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
@@ -192,4 +210,4 @@
 fi
 
 cd $ANDROID_BUILD_TOP
-$mkdir_cmd && $prebuild_cmd && LD_PRELOAD=libsigchain.so $cmdline "$@"
+$mkdir_cmd && $prebuild_cmd && $cmdline "$@"
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
index ad23edf..7353544 100755
--- a/test/etc/push-and-run-prebuilt-test-jar
+++ b/test/etc/push-and-run-prebuilt-test-jar
@@ -26,6 +26,8 @@
 TARGET_SUFFIX="32"
 GDB_TARGET_SUFFIX=""
 COMPILE_FLAGS=""
+SECONDARY_DEX=""
+DEX_VERIFY=""
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -104,6 +106,9 @@
         GDB_TARGET_SUFFIX="64"
         ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
         shift
+    elif [ "x$1" = "x--secondary" ]; then
+        SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -126,7 +131,6 @@
     fi
 
     if [ "$VERIFY" = "y" ]; then
-        DEX_VERIFY=""
         msg "Performing verification"
     else
         DEX_VERIFY="-Xverify:none"
@@ -173,7 +177,12 @@
 
 if [ "$INTERPRETER" = "y" ]; then
     INT_OPTS="-Xint"
-    COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+    if [ "$VERIFY" = "y" ] ; then
+      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+    else
+      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
+      DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
+    fi
 fi
 
 JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
@@ -194,7 +203,7 @@
 cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
     mkdir -p $DEX_LOCATION/dalvik-cache/$ARCH/ && \
     $INVOKE_WITH /system/bin/dex2oatd $COMPILE_FLAGS $BUILD_BOOT_OPT $BUILD_RELOCATE_OPT  --runtime-arg -classpath --runtime-arg $DEX_LOCATION/$TEST_NAME.jar --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ARCH/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") --instruction-set=$ARCH && \
-    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
+    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX Main $@"
 cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
 echo "$cmdline" > $cmdfile
 
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index 06075c2..a6a4e78 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -22,6 +22,8 @@
 FLAGS=""
 TARGET_SUFFIX="32"
 GDB_TARGET_SUFFIX=""
+SECONDARY_DEX=""
+DEX_VERIFY=""
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -97,6 +99,9 @@
         TARGET_SUFFIX="64"
         GDB_TARGET_SUFFIX="64"
         shift
+    elif [ "x$1" = "x--secondary" ]; then
+        SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -119,7 +124,6 @@
     fi
 
     if [ "$VERIFY" = "y" ]; then
-        DEX_VERIFY=""
         msg "Performing verification"
     else
         DEX_VERIFY="-Xverify:none"
@@ -160,6 +164,12 @@
 
 if [ "$INTERPRETER" = "y" ]; then
     INT_OPTS="-Xint"
+    if [ "$VERIFY" = "y" ] ; then
+      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
+    else
+      COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=verify-none"
+      DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
+    fi
 fi
 
 JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
@@ -172,7 +182,7 @@
 fi
 
 cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
-    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $DEX_VERIFY $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX Main"
 if [ "$DEV_MODE" = "y" ]; then
   echo $cmdline "$@"
 fi
diff --git a/test/run-test b/test/run-test
index 5d3cbac..3582628 100755
--- a/test/run-test
+++ b/test/run-test
@@ -187,6 +187,9 @@
     elif [ "x$1" = "x--always-clean" ]; then
         always_clean="yes"
         shift
+    elif [ "x$1" = "x--dex2oat-swap" ]; then
+        run_args="${run_args} --dex2oat-swap"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"
@@ -342,6 +345,7 @@
         echo "    --64                 Run the test in 64-bit mode"
         echo "    --trace              Run with method tracing"
         echo "    --always-clean       Delete the test files even if the test fails."
+        echo "    --dex2oat-swap       Use a dex2oat swap file."
     ) 1>&2
     exit 1
 fi
@@ -416,6 +420,7 @@
             good="yes"
         fi
     fi
+    exit
 elif [ "$update_mode" = "yes" ]; then
     "./${build}" >"$build_output" 2>&1
     build_exit="$?"