Merge "ART: Fix possible soft+hard failure in verifier" into lmp-mr1-dev
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index d887acd..02252ab 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -54,7 +54,7 @@
   include $(CLEAR_VARS)
   LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
   LOCAL_MODULE_TAGS := optional
-  LOCAL_SRC_FILES := $$(art_source)
+  LOCAL_SRC_FILES := $$(art_source) ../sigchainlib/sigchain.cc
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes)
   LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries)
 
@@ -65,9 +65,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 +82,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..807c5cc 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 \
@@ -278,7 +279,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)
@@ -326,7 +327,7 @@
     LOCAL_MODULE_TAGS := tests
   endif
   LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
-  LOCAL_SRC_FILES := $$(art_gtest_filename)
+  LOCAL_SRC_FILES := $$(art_gtest_filename) sigchainlib/sigchain.cc
   LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes)
   LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest
 
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 022ec6b..d278884 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -242,8 +242,14 @@
     CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) ==
         resolved_method) << PrettyMethod(resolved_method);
     int stats_flags = kFlagMethodResolved;
-    GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method,
-                                  &stats_flags, target_method, direct_code, direct_method);
+    GetCodeAndMethodForDirectCall(/*out*/invoke_type,
+                                  kDirect,  // Sharp type
+                                  false,    // The dex cache is guaranteed to be available
+                                  referrer_class, resolved_method,
+                                  /*out*/&stats_flags,
+                                  target_method,
+                                  /*out*/direct_code,
+                                  /*out*/direct_method);
     DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method);
     if (*invoke_type == kDirect) {
       stats_flags |= kFlagsMethodResolvedVirtualMadeDirect;
@@ -272,8 +278,14 @@
     CHECK(called_method != NULL);
     CHECK(!called_method->IsAbstract());
     int stats_flags = kFlagMethodResolved;
-    GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method,
-                                  &stats_flags, target_method, direct_code, direct_method);
+    GetCodeAndMethodForDirectCall(/*out*/invoke_type,
+                                  kDirect,  // Sharp type
+                                  true,     // The dex cache may not be available
+                                  referrer_class, called_method,
+                                  /*out*/&stats_flags,
+                                  target_method,
+                                  /*out*/direct_code,
+                                  /*out*/direct_method);
     DCHECK_NE(*invoke_type, kSuper);
     if (*invoke_type == kDirect) {
       stats_flags |= kFlagsMethodResolvedPreciseTypeDevirtualization;
@@ -288,8 +300,14 @@
 
   // Sharpening failed so generate a regular resolved method dispatch.
   int stats_flags = kFlagMethodResolved;
-  GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method,
-                                &stats_flags, target_method, direct_code, direct_method);
+  GetCodeAndMethodForDirectCall(/*out*/invoke_type,
+                                *invoke_type,  // Sharp type
+                                false,         // The dex cache is guaranteed to be available
+                                referrer_class, resolved_method,
+                                /*out*/&stats_flags,
+                                target_method,
+                                /*out*/direct_code,
+                                /*out*/direct_method);
   return stats_flags;
 }
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index fa2a560..9a44ade 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -934,6 +934,10 @@
   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();
@@ -1089,7 +1093,7 @@
 
 void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
                                                    bool no_guarantee_of_dex_cache_entry,
-                                                   mirror::Class* referrer_class,
+                                                   const mirror::Class* referrer_class,
                                                    mirror::ArtMethod* method,
                                                    int* stats_flags,
                                                    MethodReference* target_method,
@@ -1101,7 +1105,7 @@
   // invoked, so this can be passed to the out-of-line runtime support code.
   *direct_code = 0;
   *direct_method = 0;
-  bool use_dex_cache = false;
+  bool use_dex_cache = GetCompilerOptions().GetCompilePic();  // Off by default
   const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
   // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
   const bool force_relocations = (compiling_boot ||
@@ -1116,7 +1120,7 @@
       return;
     }
     // TODO: support patching on all architectures.
-    use_dex_cache = force_relocations && !support_boot_image_fixup_;
+    use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
   }
   bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
   if (!use_dex_cache) {
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index a165901..598e196 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -633,11 +633,12 @@
 
  public:  // TODO make private or eliminate.
   // Compute constant code and method pointers when possible.
-  void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
+  void GetCodeAndMethodForDirectCall(/*out*/InvokeType* type,
+                                     InvokeType sharp_type,
                                      bool no_guarantee_of_dex_cache_entry,
-                                     mirror::Class* referrer_class,
+                                     const mirror::Class* referrer_class,
                                      mirror::ArtMethod* method,
-                                     int* stats_flags,
+                                     /*out*/int* stats_flags,
                                      MethodReference* target_method,
                                      uintptr_t* direct_code, uintptr_t* direct_method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index c0f91d16..03b9371 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -27,7 +27,7 @@
     kSpace,               // Maximize space savings.
     kBalanced,            // Try to get the best performance return on compilation investment.
     kSpeed,               // Maximize runtime performance.
-    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
+    kEverything,          // Force compilation (Note: excludes compilaton of class initializers).
   };
 
   // Guide heuristics to determine whether to compile method if profile data not available.
@@ -58,7 +58,8 @@
     include_debug_symbols_(kDefaultIncludeDebugSymbols),
     implicit_null_checks_(false),
     implicit_so_checks_(false),
-    implicit_suspend_checks_(false)
+    implicit_suspend_checks_(false),
+    compile_pic_(false)
 #ifdef ART_SEA_IR_MODE
     , sea_ir_mode_(false)
 #endif
@@ -76,7 +77,8 @@
                   bool include_debug_symbols,
                   bool implicit_null_checks,
                   bool implicit_so_checks,
-                  bool implicit_suspend_checks
+                  bool implicit_suspend_checks,
+                  bool compile_pic
 #ifdef ART_SEA_IR_MODE
                   , bool sea_ir_mode
 #endif
@@ -93,7 +95,8 @@
     include_debug_symbols_(include_debug_symbols),
     implicit_null_checks_(implicit_null_checks),
     implicit_so_checks_(implicit_so_checks),
-    implicit_suspend_checks_(implicit_suspend_checks)
+    implicit_suspend_checks_(implicit_suspend_checks),
+    compile_pic_(compile_pic)
 #ifdef ART_SEA_IR_MODE
     , sea_ir_mode_(sea_ir_mode)
 #endif
@@ -196,6 +199,11 @@
     return include_patch_information_;
   }
 
+  // Should the code be compiled as position independent?
+  bool GetCompilePic() const {
+    return compile_pic_;
+  }
+
  private:
   CompilerFilter compiler_filter_;
   size_t huge_method_threshold_;
@@ -211,6 +219,7 @@
   bool implicit_null_checks_;
   bool implicit_so_checks_;
   bool implicit_suspend_checks_;
+  bool compile_pic_;
 #ifdef ART_SEA_IR_MODE
   bool sea_ir_mode_;
 #endif
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
index e4c957a..e8ccd67 100644
--- a/compiler/elf_patcher.cc
+++ b/compiler/elf_patcher.cc
@@ -44,7 +44,7 @@
   const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
   if (oat_file == nullptr) {
     CHECK(Runtime::Current()->IsCompiler());
-    oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
+    oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, error_msg);
     if (oat_file == nullptr) {
       *error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
                                 error_msg->c_str());
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 355036b..0aef512 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -98,7 +98,7 @@
   {
     ImageWriter writer(*compiler_driver_.get());
     bool success_image = writer.Write(image_file.GetFilename(), requested_image_base,
-                                      dup_oat->GetPath(), dup_oat->GetPath());
+                                      dup_oat->GetPath(), dup_oat->GetPath(), /*compile_pic*/false);
     ASSERT_TRUE(success_image);
     bool success_fixup = ElfFixup::Fixup(dup_oat.get(), writer.GetOatDataBegin());
     ASSERT_TRUE(success_fixup);
@@ -210,7 +210,8 @@
                              oat_file_begin,
                              oat_data_begin,
                              oat_data_end,
-                             oat_file_end);
+                             oat_file_end,
+                             /*compile_pic*/false);
     ASSERT_TRUE(image_header.IsValid());
 
     char* magic = const_cast<char*>(image_header.GetMagic());
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c6fc115..0b304eb 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -71,11 +71,13 @@
 bool ImageWriter::Write(const std::string& image_filename,
                         uintptr_t image_begin,
                         const std::string& oat_filename,
-                        const std::string& oat_location) {
+                        const std::string& oat_location,
+                        bool compile_pic) {
   CHECK(!image_filename.empty());
 
   CHECK_NE(image_begin, 0U);
   image_begin_ = reinterpret_cast<byte*>(image_begin);
+  compile_pic_ = compile_pic;
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
@@ -319,7 +321,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.
@@ -449,6 +452,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));
@@ -569,7 +574,8 @@
                            PointerToLowMemUInt32(oat_file_begin),
                            PointerToLowMemUInt32(oat_data_begin_),
                            PointerToLowMemUInt32(oat_data_end),
-                           PointerToLowMemUInt32(oat_file_end));
+                           PointerToLowMemUInt32(oat_file_end),
+                           compile_pic_);
   memcpy(image_->Begin(), &image_header, sizeof(image_header));
 
   // Note that image_end_ is left at end of used space
@@ -685,7 +691,7 @@
 
 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.
@@ -716,9 +722,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
@@ -738,12 +746,14 @@
   // oat_begin_
 
   // 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_));
 #endif
     copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
-  } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
+  } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
+                      orig == runtime->GetImtUnimplementedMethod())) {
 #if defined(ART_USE_PORTABLE_COMPILER)
     copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
 #endif
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index e8bcf7f..61365fe 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -24,6 +24,7 @@
 #include <set>
 #include <string>
 
+#include "base/macros.h"
 #include "driver/compiler_driver.h"
 #include "mem_map.h"
 #include "oat_file.h"
@@ -35,21 +36,23 @@
 namespace art {
 
 // Write a Space built during compilation for use during execution.
-class ImageWriter {
+class ImageWriter FINAL {
  public:
   explicit ImageWriter(const CompilerDriver& compiler_driver)
       : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(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) {}
+        quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
+        compile_pic_(false) {}
 
   ~ImageWriter() {}
 
   bool Write(const std::string& image_filename,
              uintptr_t image_begin,
              const std::string& oat_filename,
-             const std::string& oat_location)
+             const std::string& oat_location,
+             bool compile_pic)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
   uintptr_t GetOatDataBegin() {
@@ -97,8 +100,8 @@
     // different .o ELF objects.
     DCHECK_LT(offset, oat_file_->Size());
 #endif
-    if (offset == 0) {
-      return NULL;
+    if (offset == 0u) {
+      return nullptr;
     }
     return oat_data_begin_ + offset;
   }
@@ -199,6 +202,7 @@
   uint32_t quick_imt_conflict_trampoline_offset_;
   uint32_t quick_resolution_trampoline_offset_;
   uint32_t quick_to_interpreter_bridge_offset_;
+  bool compile_pic_;
 
   friend class FixupVisitor;
   friend class FixupClassVisitor;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 11d1728..7c1f6c5 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -135,8 +135,8 @@
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
   std::string error_msg;
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false,
-                                            &error_msg));
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
+                                                  nullptr, false, &error_msg));
   ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
   const OatHeader& oat_header = oat_file->GetOatHeader();
   ASSERT_TRUE(oat_header.IsValid());
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index a06b5c5..0bab429 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -24,10 +24,11 @@
 LOCAL_MODULE := dalvikvm
 LOCAL_MODULE_TAGS := optional
 LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc
+LOCAL_SRC_FILES := dalvikvm.cc ../sigchainlib/sigchain.cc
 LOCAL_CFLAGS := $(dalvikvm_cflags)
 LOCAL_C_INCLUDES := art/runtime
-LOCAL_SHARED_LIBRARIES := libdl libnativehelper
+LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper
+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
@@ -50,11 +51,15 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_CLANG := true
 LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc
+LOCAL_SRC_FILES := dalvikvm.cc ../sigchainlib/sigchain.cc
 LOCAL_CFLAGS := $(dalvikvm_cflags)
 LOCAL_C_INCLUDES := art/runtime
 LOCAL_SHARED_LIBRARIES := libnativehelper
 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 6d7f115..833a678 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -155,6 +155,9 @@
   UsageError("      Example: --instruction-set-features=div");
   UsageError("      Default: default");
   UsageError("");
+  UsageError("  --compile-pic: Force indirect use of code, methods, and classes");
+  UsageError("      Default: disabled");
+  UsageError("");
   UsageError("  --compiler-backend=(Quick|Optimizing|Portable): select compiler backend");
   UsageError("      set.");
   UsageError("      Example: --compiler-backend=Portable");
@@ -453,7 +456,8 @@
     {
       // ImageWriter is scoped so it can free memory before doing FixupElf
       ImageWriter image_writer(compiler);
-      if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
+      if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location,
+                              compiler_options_->GetCompilePic())) {
         LOG(ERROR) << "Failed to create image file " << image_filename;
         return false;
       }
@@ -465,10 +469,14 @@
       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();
-      return false;
+    // Do not fix up the ELF file if we are --compile-pic
+    if (!compiler_options_->GetCompilePic()) {
+      if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
+        LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+        return false;
+      }
     }
+
     return true;
   }
 
@@ -782,7 +790,7 @@
   *parsed_value = value;
 }
 
-static int dex2oat(int argc, char** argv) {
+static void b13564922() {
 #if defined(__linux__) && defined(__arm__)
   int major, minor;
   struct utsname uts;
@@ -800,6 +808,10 @@
     }
   }
 #endif
+}
+
+static int dex2oat(int argc, char** argv) {
+  b13564922();
 
   original_argc = argc;
   original_argv = argv;
@@ -838,6 +850,7 @@
       ? Compiler::kPortable
       : Compiler::kQuick;
   const char* compiler_filter_string = nullptr;
+  bool compile_pic = false;
   int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
   int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
   int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
@@ -964,6 +977,8 @@
       }
     } else if (option.starts_with("--compiler-filter=")) {
       compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+    } else if (option == "--compile-pic") {
+      compile_pic = true;
     } else if (option.starts_with("--huge-method-max=")) {
       const char* threshold = option.substr(strlen("--huge-method-max=")).data();
       if (!ParseInt(threshold, &huge_method_threshold)) {
@@ -1203,7 +1218,8 @@
                                                                         include_debug_symbols,
                                                                         implicit_null_checks,
                                                                         implicit_so_checks,
-                                                                        implicit_suspend_checks
+                                                                        implicit_suspend_checks,
+                                                                        compile_pic
 #ifdef ART_SEA_IR_MODE
                                                                         , compiler_options.sea_ir_ =
                                                                               true;
@@ -1275,6 +1291,7 @@
                        &method_inliner_map,
                        thread_count)) {
     LOG(ERROR) << "Failed to create dex2oat";
+    timings.EndTiming();
     return EXIT_FAILURE;
   }
   std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat);
@@ -1303,6 +1320,7 @@
     if (image_classes.get() == nullptr) {
       LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
           "': " << error_msg;
+      timings.EndTiming();
       return EXIT_FAILURE;
     }
   } else if (image) {
@@ -1321,11 +1339,13 @@
       if (zip_archive.get() == nullptr) {
         LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
             << error_msg;
+        timings.EndTiming();
         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();
         return EXIT_FAILURE;
       }
       ATRACE_END();
@@ -1333,6 +1353,7 @@
       size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files);
       if (failure_count > 0) {
         LOG(ERROR) << "Failed to open some dex files: " << failure_count;
+        timings.EndTiming();
         return EXIT_FAILURE;
       }
     }
@@ -1382,17 +1403,20 @@
       new SafeMap<std::string, std::string>());
 
   // Insert some compiler things.
-  std::ostringstream oss;
-  for (int i = 0; i < argc; ++i) {
-    if (i > 0) {
-      oss << ' ';
+  {
+    std::ostringstream oss;
+    for (int i = 0; i < argc; ++i) {
+      if (i > 0) {
+        oss << ' ';
+      }
+      oss << argv[i];
     }
-    oss << argv[i];
+    key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+    oss.str("");  // Reset.
+    oss << kRuntimeISA;
+    key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
+    key_value_store->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
   }
-  key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
-  oss.str("");  // Reset.
-  oss << kRuntimeISA;
-  key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
 
   std::unique_ptr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
                                                                         android_root,
@@ -1411,6 +1435,7 @@
                                                                         key_value_store.get()));
   if (compiler.get() == nullptr) {
     LOG(ERROR) << "Failed to create oat file: " << oat_location;
+    timings.EndTiming();
     return EXIT_FAILURE;
   }
 
@@ -1474,6 +1499,7 @@
                                                            oat_location,
                                                            *compiler.get());
     if (!image_creation_success) {
+      timings.EndTiming();
       return EXIT_FAILURE;
     }
     VLOG(compiler) << "Image written successfully: " << image_filename;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 83c7871..8f93a66 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -108,6 +108,7 @@
 const char* image_roots_descriptions_[] = {
   "kResolutionMethod",
   "kImtConflictMethod",
+  "kImtUnimplementedMethod",
   "kDefaultImt",
   "kCalleeSaveMethod",
   "kRefsOnlySaveMethod",
@@ -278,8 +279,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);
@@ -983,6 +985,8 @@
 
     os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
 
+    os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n";
+
     {
       os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
       Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
@@ -1033,7 +1037,7 @@
     std::string error_msg;
     const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
     if (oat_file == nullptr) {
-      oat_file = OatFile::Open(oat_location, oat_location, nullptr, false, &error_msg);
+      oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
       if (oat_file == nullptr) {
         os << "NOT FOUND: " << error_msg << "\n";
         return false;
@@ -1315,7 +1319,7 @@
         }
       } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
           method->IsResolutionMethod() || method->IsImtConflictMethod() ||
-          method->IsClassInitializer()) {
+          method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
         DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method);
         DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method);
       } else {
@@ -1761,7 +1765,7 @@
   if (oat_filename != nullptr) {
     std::string error_msg;
     OatFile* oat_file =
-        OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+        OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
     if (oat_file == nullptr) {
       fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
       return EXIT_FAILURE;
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 74f6779..b9637d0 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -71,7 +71,7 @@
   bool has_system = false;
   bool has_cache = false;
   // image_location = /system/framework/boot.art
-  // system_image_location = /system/framework/<image_isa>/boot.art
+  // system_image_filename = /system/framework/<image_isa>/boot.art
   std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
   if (OS::FileExists(system_filename.c_str())) {
     has_system = true;
@@ -130,6 +130,7 @@
                << " for location " << image_location;
     return false;
   }
+
   int64_t image_len = input_image->GetLength();
   if (image_len < 0) {
     LOG(ERROR) << "Error while getting image length";
@@ -142,6 +143,10 @@
     return false;
   }
 
+  /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+  // Nothing special to do right now since the image always needs to get patched.
+  // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
   // Set up the runtime
   RuntimeOptions options;
   NoopCompilerCallbacks callbacks;
@@ -186,9 +191,11 @@
   return true;
 }
 
-bool PatchOat::Patch(const File* input_oat, const std::string& image_location, off_t delta,
+bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta,
                      File* output_oat, File* output_image, InstructionSet isa,
-                     TimingLogger* timings) {
+                     TimingLogger* timings,
+                     bool output_oat_opened_from_fd,
+                     bool new_oat_out) {
   CHECK(Runtime::Current() == nullptr);
   CHECK(output_image != nullptr);
   CHECK_GE(output_image->Fd(), 0);
@@ -231,6 +238,10 @@
     LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath();
   }
 
+  /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+  // Nothing special to do right now since the image always needs to get patched.
+  // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
   // Set up the runtime
   RuntimeOptions options;
   NoopCompilerCallbacks callbacks;
@@ -260,17 +271,37 @@
   }
   gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace();
 
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
                                              PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
   if (elf.get() == nullptr) {
     LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
     return false;
   }
 
+  bool skip_patching_oat = false;
+  MaybePic is_oat_pic = IsOatPic(elf.get());
+  if (is_oat_pic >= ERROR_FIRST) {
+    // Error logged by IsOatPic
+    return false;
+  } else if (is_oat_pic == PIC) {
+    // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
+    if (!ReplaceOatFileWithSymlink(input_oat->GetPath(),
+                                   output_oat->GetPath(),
+                                   output_oat_opened_from_fd,
+                                   new_oat_out)) {
+      // Errors already logged by above call.
+      return false;
+    }
+    // Don't patch the OAT, since we just symlinked it. Image still needs patching.
+    skip_patching_oat = true;
+  } else {
+    CHECK(is_oat_pic == NOT_PIC);
+  }
+
   PatchOat p(elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
              delta, timings);
   t.NewTiming("Patching files");
-  if (!p.PatchElf()) {
+  if (!skip_patching_oat && !p.PatchElf()) {
     LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
     return false;
   }
@@ -280,10 +311,12 @@
   }
 
   t.NewTiming("Writing files");
-  if (!p.WriteElf(output_oat)) {
+  if (!skip_patching_oat && !p.WriteElf(output_oat)) {
+    LOG(ERROR) << "Failed to write oat file " << input_oat->GetPath();
     return false;
   }
   if (!p.WriteImage(output_image)) {
+    LOG(ERROR) << "Failed to write image file " << input_image->GetPath();
     return false;
   }
   return true;
@@ -323,6 +356,83 @@
   }
 }
 
+bool PatchOat::IsImagePic(const ImageHeader& image_header, const std::string& image_path) {
+  if (!image_header.CompilePic()) {
+    if (kIsDebugBuild) {
+      LOG(INFO) << "image at location " << image_path << " was *not* compiled pic";
+    }
+    return false;
+  }
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "image at location " << image_path << " was compiled PIC";
+  }
+
+  return true;
+}
+
+PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) {
+  if (oat_in == nullptr) {
+    LOG(ERROR) << "No ELF input oat fie available";
+    return ERROR_OAT_FILE;
+  }
+
+  const std::string& file_path = oat_in->GetFile().GetPath();
+
+  const OatHeader* oat_header = GetOatHeader(oat_in);
+  if (oat_header == nullptr) {
+    LOG(ERROR) << "Failed to find oat header in oat file " << file_path;
+    return ERROR_OAT_FILE;
+  }
+
+  if (!oat_header->IsValid()) {
+    LOG(ERROR) << "Elf file " << file_path << " has an invalid oat header";
+    return ERROR_OAT_FILE;
+  }
+
+  bool is_pic = oat_header->IsPic();
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Oat file at " << file_path << " is " << (is_pic ? "PIC" : "not pic");
+  }
+
+  return is_pic ? PIC : NOT_PIC;
+}
+
+bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+                                         const std::string& output_oat_filename,
+                                         bool output_oat_opened_from_fd,
+                                         bool new_oat_out) {
+  // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD.
+  if (output_oat_opened_from_fd) {
+    // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC?
+    LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC";
+    return false;
+  }
+
+  // Image was PIC. Create symlink where the oat is supposed to go.
+  if (!new_oat_out) {
+    LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite";
+    return false;
+  }
+
+  // Delete the original file, since we won't need it.
+  TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str()));
+
+  // Create a symlink from the old oat to the new oat
+  if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) < 0) {
+    int err = errno;
+    LOG(ERROR) << "Failed to create symlink at " << output_oat_filename
+               << " error(" << err << "): " << strerror(err);
+    return false;
+  }
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename;
+  }
+
+  return true;
+}
+
 bool PatchOat::PatchImage() {
   ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
   CHECK_GT(image_->Size(), sizeof(ImageHeader));
@@ -388,6 +498,16 @@
   }
 }
 
+const OatHeader* PatchOat::GetOatHeader(const ElfFile* elf_file) {
+  auto rodata_sec = elf_file->FindSectionByName(".rodata");
+  if (rodata_sec == nullptr) {
+    return nullptr;
+  }
+
+  OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf_file->Begin() + rodata_sec->sh_offset);
+  return oat_header;
+}
+
 // Called by BitmapCallback
 void PatchOat::VisitObject(mirror::Object* object) {
   mirror::Object* copy = RelocatedCopyOf(object);
@@ -441,7 +561,8 @@
   }
 }
 
-bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings) {
+bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings,
+                     bool output_oat_opened_from_fd, bool new_oat_out) {
   CHECK(input_oat != nullptr);
   CHECK(output_oat != nullptr);
   CHECK_GE(input_oat->Fd(), 0);
@@ -449,13 +570,28 @@
   TimingLogger::ScopedTiming t("Setup Oat File Patching", timings);
 
   std::string error_msg;
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
                                              PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
   if (elf.get() == nullptr) {
     LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
     return false;
   }
 
+  MaybePic is_oat_pic = IsOatPic(elf.get());
+  if (is_oat_pic >= ERROR_FIRST) {
+    // Error logged by IsOatPic
+    return false;
+  } else if (is_oat_pic == PIC) {
+    // Do not need to do ELF-file patching. Create a symlink and skip the rest.
+    // Any errors will be logged by the function call.
+    return ReplaceOatFileWithSymlink(input_oat->GetPath(),
+                                     output_oat->GetPath(),
+                                     output_oat_opened_from_fd,
+                                     new_oat_out);
+  } else {
+    CHECK(is_oat_pic == NOT_PIC);
+  }
+
   PatchOat p(elf.release(), delta, timings);
   t.NewTiming("Patch Oat file");
   if (!p.PatchElf()) {
@@ -1045,11 +1181,17 @@
         input_oat_filename = "input-oat-file";
       }
       input_oat.reset(new File(input_oat_fd, input_oat_filename));
+      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;
+      }
     } else {
       CHECK(!input_oat_filename.empty());
       input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
-      if (input_oat.get() == nullptr) {
-        LOG(ERROR) << "Could not open input oat file: " << strerror(errno);
+      if (input_oat == nullptr) {
+        int err = errno;
+        LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
+                   << ": " << strerror(err) << "(" << err << ")";
       }
     }
 
@@ -1058,12 +1200,22 @@
         output_oat_filename = "output-oat-file";
       }
       output_oat.reset(new File(output_oat_fd, output_oat_filename));
+      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;
+      }
     } else {
       CHECK(!output_oat_filename.empty());
       output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
+      if (output_oat == nullptr) {
+        int err = errno;
+        LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
+                   << ": " << strerror(err) << "(" << err << ")";
+      }
     }
   }
 
+  // TODO: get rid of this.
   auto cleanup = [&output_image_filename, &output_oat_filename,
                   &new_oat_out, &new_image_out, &timings, &dump_timings](bool success) {
     timings.EndTiming();
@@ -1080,14 +1232,29 @@
     if (dump_timings) {
       LOG(INFO) << Dumpable<TimingLogger>(timings);
     }
+
+    if (kIsDebugBuild) {
+      LOG(INFO) << "Cleaning up.. success? " << success;
+    }
   };
 
-  if ((have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) ||
-      (have_image_files && output_image.get() == nullptr)) {
+  if (have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) {
+    LOG(ERROR) << "Failed to open input/output oat files";
+    cleanup(false);
+    return EXIT_FAILURE;
+  } else if (have_image_files && output_image.get() == nullptr) {
+    LOG(ERROR) << "Failed to open output image file";
     cleanup(false);
     return EXIT_FAILURE;
   }
 
+  if (debug) {
+    LOG(INFO) << "moving offset by " << base_delta
+              << " (0x" << std::hex << base_delta << ") bytes or "
+              << std::dec << (base_delta/kPageSize) << " pages.";
+  }
+
+  // TODO: is it going to be promatic to unlink a file that was flock-ed?
   ScopedFlock output_oat_lock;
   if (lock_output) {
     std::string error_msg;
@@ -1098,24 +1265,28 @@
     }
   }
 
-  if (debug) {
-    LOG(INFO) << "moving offset by " << base_delta
-              << " (0x" << std::hex << base_delta << ") bytes or "
-              << std::dec << (base_delta/kPageSize) << " pages.";
-  }
-
   bool ret;
   if (have_image_files && have_oat_files) {
     TimingLogger::ScopedTiming pt("patch image and oat", &timings);
     ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta,
-                          output_oat.get(), output_image.get(), isa, &timings);
+                          output_oat.get(), output_image.get(), isa, &timings,
+                          output_oat_fd >= 0,  // was it opened from FD?
+                          new_oat_out);
   } else if (have_oat_files) {
     TimingLogger::ScopedTiming pt("patch oat", &timings);
-    ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings);
-  } else {
+    ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
+                          output_oat_fd >= 0,  // was it opened from FD?
+                          new_oat_out);
+  } else if (have_image_files) {
     TimingLogger::ScopedTiming pt("patch image", &timings);
-    CHECK(have_image_files);
     ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
+  } else {
+    CHECK(false);
+    ret = true;
+  }
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Exiting with return ... " << ret;
   }
   cleanup(ret);
   return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 6960d3b..21041fb 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -30,6 +30,7 @@
 namespace art {
 
 class ImageHeader;
+class OatHeader;
 
 namespace mirror {
 class Object;
@@ -40,14 +41,21 @@
 
 class PatchOat {
  public:
-  static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings);
+  // Patch only the oat file
+  static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings,
+                    bool output_oat_opened_from_fd,  // Was this using --oatput-oat-fd ?
+                    bool new_oat_out);               // Output oat was a new file created by us?
 
+  // Patch only the image (art file)
   static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa,
                     TimingLogger* timings);
 
-  static bool Patch(const File* oat_in, const std::string& art_location,
+  // Patch both the image and the oat file
+  static bool Patch(File* oat_in, const std::string& art_location,
                     off_t delta, File* oat_out, File* art_out, InstructionSet isa,
-                    TimingLogger* timings);
+                    TimingLogger* timings,
+                    bool output_oat_opened_from_fd,  // Was this using --oatput-oat-fd ?
+                    bool new_oat_out);               // Output oat was a new file created by us?
 
  private:
   // Takes ownership only of the ElfFile. All other pointers are only borrowed.
@@ -63,6 +71,26 @@
         delta_(delta), timings_(timings) {}
   ~PatchOat() {}
 
+  // Was the .art image at image_path made with --compile-pic ?
+  static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path);
+
+  enum MaybePic {
+      NOT_PIC,            // Code not pic. Patch as usual.
+      PIC,                // Code was pic. Create symlink; skip OAT patching.
+      ERROR_OAT_FILE,     // Failed to symlink oat file
+      ERROR_FIRST = ERROR_OAT_FILE,
+  };
+
+  // Was the .oat image at oat_in made with --compile-pic ?
+  static MaybePic IsOatPic(const ElfFile* oat_in);
+
+  // Attempt to replace the file with a symlink
+  // Returns false if it fails
+  static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+                                        const std::string& output_oat_filename,
+                                        bool output_oat_opened_from_fd,
+                                        bool new_oat_out);  // Output oat was newly created?
+
   static void BitmapCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     reinterpret_cast<PatchOat*>(arg)->VisitObject(obj);
@@ -90,6 +118,9 @@
   mirror::Object* RelocatedCopyOf(mirror::Object*);
   mirror::Object* RelocatedAddressOf(mirror::Object* obj);
 
+  // Look up the oat header from any elf file.
+  static const OatHeader* GetOatHeader(const ElfFile* elf_file);
+
   // Walks through the old image and patches the mmap'd copy of it to the new offset. It does not
   // change the heap.
   class PatchVisitor {
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/class_linker.cc b/runtime/class_linker.cc
index d718367..900bc9d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -128,15 +128,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;",
@@ -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
@@ -964,7 +956,7 @@
                                                                uint32_t dex_location_checksum,
                                                                const char* oat_location,
                                                                std::string* error_msg) {
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
                                             !Runtime::Current()->IsCompiler(),
                                             error_msg));
   if (oat_file.get() == nullptr) {
@@ -1036,7 +1028,7 @@
     error_msgs->push_back(error_msg);
     return nullptr;
   }
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
                                             !Runtime::Current()->IsCompiler(),
                                             &error_msg));
   if (oat_file.get() == nullptr) {
@@ -1097,9 +1089,13 @@
     image_patch_delta = image_header->GetPatchDelta();
   }
   const OatHeader& oat_header = oat_file->GetOatHeader();
-  bool ret = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
-              && (oat_header.GetImagePatchDelta() == image_patch_delta)
-              && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin));
+  bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum);
+
+  // If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks.
+  if (!oat_file->IsPic()) {
+    ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta)
+              && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin);
+  }
   if (!ret) {
     *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)",
                               oat_file->GetLocation().c_str(),
@@ -1287,11 +1283,12 @@
   bool odex_checksum_verified = false;
   bool have_system_odex = false;
   {
-    // There is a high probability that these both these oat files map similar/the same address
+    // There is a high probability that both these oat files map similar/the same address
     // spaces so we must scope them like this so they each gets its turn.
     std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
+                                                         nullptr,
                                                          executable, &odex_error_msg));
-    if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
+    if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
                                                        &odex_checksum_verified,
                                                        &odex_error_msg)) {
       error_msgs->push_back(odex_error_msg);
@@ -1313,8 +1310,9 @@
   bool cache_checksum_verified = false;
   if (have_dalvik_cache) {
     std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
+                                                          nullptr,
                                                           executable, &cache_error_msg));
-    if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
+    if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
                                                         &cache_checksum_verified,
                                                         &cache_error_msg)) {
       error_msgs->push_back(cache_error_msg);
@@ -1390,7 +1388,7 @@
                                                   InstructionSet isa,
                                                   std::string* error_msg) {
   // We open it non-executable
-  std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, false, error_msg));
+  std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
   if (output.get() == nullptr) {
     return nullptr;
   }
@@ -1409,13 +1407,15 @@
                                                 const std::string& image_location,
                                                 InstructionSet isa,
                                                 std::string* error_msg) {
-  if (!Runtime::Current()->GetHeap()->HasImageSpace()) {
+  Runtime* runtime = Runtime::Current();
+  DCHECK(runtime != nullptr);
+  if (!runtime->GetHeap()->HasImageSpace()) {
     // We don't have an image space so there is no point in trying to patchoat.
     LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are "
                  << "running without an image. Attempting to use oat file for interpretation.";
     return GetInterpretedOnlyOat(input_oat, isa, error_msg);
   }
-  if (!Runtime::Current()->IsDex2OatEnabled()) {
+  if (!runtime->IsDex2OatEnabled()) {
     // We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
     // input_oat but make sure we only do interpretation on it's dex files.
     LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
@@ -1423,7 +1423,7 @@
     return GetInterpretedOnlyOat(input_oat, isa, error_msg);
   }
   Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
-  std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
+  std::string patchoat(runtime->GetPatchoatExecutable());
 
   std::string isa_arg("--instruction-set=");
   isa_arg += GetInstructionSetString(isa);
@@ -1445,10 +1445,11 @@
   LOG(INFO) << "Relocate Oat File: " << command_line;
   bool success = Exec(argv, error_msg);
   if (success) {
-    std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr,
-                                                  !Runtime::Current()->IsCompiler(), error_msg));
+    std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
+                                                  !runtime->IsCompiler(), error_msg));
     bool checksum_verified = false;
-    if (output.get() != nullptr && CheckOatFile(output.get(), isa, &checksum_verified, error_msg)) {
+    if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
+                                                error_msg)) {
       return output.release();
     } else if (output.get() != nullptr) {
       *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
@@ -1459,7 +1460,7 @@
                                 "but was unable to open output file '%s': %s",
                                 input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
     }
-  } else if (!Runtime::Current()->IsCompiler()) {
+  } else if (!runtime->IsCompiler()) {
     // patchoat failed which means we probably don't have enough room to place the output oat file,
     // instead of failing we should just run the interpreter from the dex files in the input oat.
     LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
@@ -1473,32 +1474,14 @@
   return nullptr;
 }
 
-int32_t ClassLinker::GetRequiredDelta(const OatFile* oat_file, InstructionSet isa) {
-  Runtime* runtime = Runtime::Current();
-  int32_t real_patch_delta;
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
-  CHECK(image_space != nullptr);
-  if (isa == Runtime::Current()->GetInstructionSet()) {
-    const ImageHeader& image_header = image_space->GetImageHeader();
-    real_patch_delta = image_header.GetPatchDelta();
-  } else {
-    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
-        image_space->GetImageLocation().c_str(), isa));
-    real_patch_delta = image_header->GetPatchDelta();
-  }
-  const OatHeader& oat_header = oat_file->GetOatHeader();
-  return real_patch_delta - oat_header.GetImagePatchDelta();
-}
-
-bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
                                bool* checksum_verified,
                                std::string* error_msg) {
   std::string compound_msg("Oat file failed to verify: ");
-  Runtime* runtime = Runtime::Current();
   uint32_t real_image_checksum;
   void* real_image_oat_offset;
   int32_t real_patch_delta;
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+  const gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
   if (image_space == nullptr) {
     *error_msg = "No image space present";
     return false;
@@ -1525,19 +1508,28 @@
                                  real_image_checksum, oat_image_checksum);
   }
 
-  void* oat_image_oat_offset =
-      reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
-  bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
-  if (!offset_verified) {
-    compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
-                                 real_image_oat_offset, oat_image_oat_offset);
-  }
+  bool offset_verified;
+  bool patch_delta_verified;
 
-  int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
-  bool patch_delta_verified = oat_patch_delta == real_patch_delta;
-  if (!patch_delta_verified) {
-    compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
-                                 real_patch_delta, oat_patch_delta);
+  if (!oat_file->IsPic()) {
+    void* oat_image_oat_offset =
+        reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
+    offset_verified = oat_image_oat_offset == real_image_oat_offset;
+    if (!offset_verified) {
+      compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
+                                   real_image_oat_offset, oat_image_oat_offset);
+    }
+
+    int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+    patch_delta_verified = oat_patch_delta == real_patch_delta;
+    if (!patch_delta_verified) {
+      compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
+                                   real_patch_delta, oat_patch_delta);
+    }
+  } else {
+    // If an oat file is PIC, we ignore offset and patching delta.
+    offset_verified = true;
+    patch_delta_verified = true;
   }
 
   bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
@@ -1554,7 +1546,7 @@
     return oat_file;
   }
 
-  return OatFile::Open(oat_location, oat_location, nullptr, !Runtime::Current()->IsCompiler(),
+  return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(),
                        error_msg);
 }
 
@@ -1675,25 +1667,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, 0, kRootStickyClass);
+    }
+    for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
+      root.VisitRoot(callback, arg, 0, 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, 0, 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));
+        class_table_.Erase(it);
+        class_table_.Insert(GcRoot<mirror::Class>(new_ref));
       }
     }
   }
@@ -1751,9 +1742,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 +1804,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 +1947,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 +1982,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 +2002,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 +2057,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 +2088,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 +2114,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 +2134,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 +2179,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 +2203,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 +2231,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.
@@ -3035,7 +3026,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 +3045,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 +3054,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 +3085,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 +3135,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 +3174,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 +3227,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 +3237,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 +3251,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 +3270,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 +3330,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 +3369,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 +3387,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 +3426,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]));
   }
 }
 
@@ -3872,7 +3876,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();
 }
@@ -4144,13 +4149,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 +4391,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 +4413,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 +4423,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 +4437,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
@@ -4535,7 +4558,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 +4570,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 +4699,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 +4800,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 +4881,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 +4914,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 +4922,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 +5019,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 +5033,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 +5094,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 +5107,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;
 }
 
@@ -5462,9 +5669,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 +5684,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 +5693,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 +5717,41 @@
   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);
+}
+
 }  // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 107a4b2..7fc394a 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"
@@ -49,8 +50,9 @@
 
 class InternTable;
 template<class T> class ObjectLock;
+class Runtime;
 class ScopedObjectAccessAlreadyRunnable;
-template<class T> class Handle;
+template<size_t kNumReferences> class PACKED(4) StackHandleScope;
 
 typedef bool (ClassVisitor)(mirror::Class* c, void* arg);
 
@@ -74,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_);
 
@@ -93,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_);
 
@@ -113,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_);
 
@@ -402,6 +406,17 @@
     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_);
+
  private:
   bool FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -431,7 +446,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_);
 
@@ -513,14 +528,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)
@@ -586,9 +603,8 @@
                                      std::string* error_msg)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
-  bool CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+  bool CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
                     bool* checksum_verified, std::string* error_msg);
-  int32_t GetRequiredDelta(const OatFile* oat_file, InstructionSet isa);
 
   // Note: will not register the oat file.
   const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
@@ -643,7 +659,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 +667,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 +689,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 +775,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_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index dc845c7..1f52c46 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2364,7 +2364,7 @@
 }
 
 void Dbg::ResumeVM() {
-  Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
+  Runtime::Current()->GetThreadList()->ResumeAllForDebugger();
 }
 
 JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspension) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index eaab1f4..131de2c 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -246,7 +246,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
@@ -459,7 +461,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_,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index c783ee4..31d79bf 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -413,11 +413,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 +450,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..54c4b09 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"
@@ -648,8 +649,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;
@@ -985,17 +987,30 @@
   // 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;
 };
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0ab6394..6ec0712 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -106,7 +106,7 @@
   delete entry;
 }
 
-ElfFile::ElfFile(File* file, bool writable, bool program_header_only)
+ElfFile::ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base)
   : file_(file),
     writable_(writable),
     program_header_only_(program_header_only),
@@ -124,13 +124,15 @@
     symtab_symbol_table_(nullptr),
     dynsym_symbol_table_(nullptr),
     jit_elf_image_(nullptr),
-    jit_gdb_entry_(nullptr) {
+    jit_gdb_entry_(nullptr),
+    requested_base_(requested_base) {
   CHECK(file != nullptr);
 }
 
 ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
-                       std::string* error_msg) {
-  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only));
+                       std::string* error_msg, uint8_t* requested_base) {
+  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only,
+                                    requested_base));
   int prot;
   int flags;
   if (writable) {
@@ -147,7 +149,8 @@
 }
 
 ElfFile* ElfFile::Open(File* file, int prot, int flags, std::string* error_msg) {
-  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false));
+  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false,
+                                    /*requested_base*/nullptr));
   if (!elf_file->Setup(prot, flags, error_msg)) {
     return nullptr;
   }
@@ -757,6 +760,8 @@
   }
   const Elf32_Sym* sym = FindDynamicSymbol(symbol_name);
   if (sym != nullptr) {
+    // TODO: we need to change this to calculate base_address_ in ::Open,
+    // otherwise it will be wrongly 0 if ::Load has not yet been called.
     return base_address_ + sym->st_value;
   } else {
     return nullptr;
@@ -1094,12 +1099,16 @@
     }
     size_t file_length = static_cast<size_t>(temp_file_length);
     if (!reserved) {
-      byte* reserve_base = ((program_header->p_vaddr != 0) ?
-                            reinterpret_cast<byte*>(program_header->p_vaddr) : nullptr);
+      uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr);
+      uint8_t* reserve_base_override = reserve_base;
+      // Override the base (e.g. when compiling with --compile-pic)
+      if (requested_base_ != nullptr) {
+        reserve_base_override = requested_base_;
+      }
       std::string reservation_name("ElfFile reservation for ");
       reservation_name += file_->GetPath();
       std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
-                                                           reserve_base,
+                                                           reserve_base_override,
                                                            GetLoadedSize(), PROT_NONE, false,
                                                            error_msg));
       if (reserve.get() == nullptr) {
@@ -1108,9 +1117,15 @@
         return false;
       }
       reserved = true;
-      if (reserve_base == nullptr) {
-        base_address_ = reserve->Begin();
-      }
+
+      // Base address is the difference of actual mapped location and the p_vaddr
+      base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve->Begin())
+                       - reinterpret_cast<uintptr_t>(reserve_base));
+      // By adding the p_vaddr of a section/symbol to base_address_ we will always get the
+      // dynamic memory address of where that object is actually mapped
+      //
+      // TODO: base_address_ needs to be calculated in ::Open, otherwise
+      // FindDynamicSymbolAddress returns the wrong values until Load is called.
       segments_.push_back(reserve.release());
     }
     // empty segment, nothing to map
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 985be76..9c0bc9a 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -40,7 +40,8 @@
 // ELFObjectFile.
 class ElfFile {
  public:
-  static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg);
+  static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+                       uint8_t* requested_base = nullptr);  // TODO: move arg to before error_msg.
   // Open with specific mmap flags, Always maps in the whole file, not just the
   // program header sections.
   static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
@@ -52,10 +53,12 @@
     return *file_;
   }
 
+  // The start of the memory map address range for this ELF file.
   byte* Begin() const {
     return map_->Begin();
   }
 
+  // The end of the memory map address range for this ELF file.
   byte* End() const {
     return map_->End();
   }
@@ -109,7 +112,7 @@
   bool Load(bool executable, std::string* error_msg);
 
  private:
-  ElfFile(File* file, bool writable, bool program_header_only);
+  ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
 
   bool Setup(int prot, int flags, std::string* error_msg);
 
@@ -200,6 +203,9 @@
   JITCodeEntry* jit_gdb_entry_;
   std::unique_ptr<ElfFile> gdb_file_mapping_;
   void GdbJITSupport();
+
+  // When not-null, override the base vaddr we memory map LOAD segments into.
+  uint8_t* requested_base_;
 };
 
 }  // namespace art
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/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/heap.cc b/runtime/gc/heap.cc
index 6af98cf..0b0200f 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"
@@ -1908,6 +1909,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();
@@ -2153,6 +2156,13 @@
   } else {
     LOG(FATAL) << "Invalid current allocator " << current_allocator_;
   }
+  if (IsGcConcurrent()) {
+    // Disable concurrent GC check so that we don't have spammy JNI requests.
+    // This gets recalculated in GrowForUtilization. It is important that it is disabled /
+    // calculated in the same thread so that there aren't any races that can cause it to become
+    // permanantly disabled. b/17942071
+    concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
+  }
   CHECK(collector != nullptr)
       << "Could not find garbage collector with collector_type="
       << static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type;
@@ -2960,9 +2970,6 @@
       self->IsHandlingStackOverflow()) {
     return;
   }
-  // We already have a request pending, no reason to start more until we update
-  // concurrent_start_bytes_.
-  concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
   JNIEnv* env = self->GetJniEnv();
   DCHECK(WellKnownClasses::java_lang_Daemons != nullptr);
   DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != nullptr);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index cf297bd..24f4f17 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);
   }
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 59630fe..dcb6392 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>
@@ -143,9 +144,7 @@
   }
 
   std::string oat_file_option_string("--oat-file=");
-  oat_file_option_string += image_filename;
-  oat_file_option_string.erase(oat_file_option_string.size() - 3);
-  oat_file_option_string += "oat";
+  oat_file_option_string += ImageHeader::GetOatLocationFromImageLocation(image_filename);
   arg_vector.push_back(oat_file_option_string);
 
   Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&arg_vector);
@@ -377,6 +376,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) {
@@ -434,6 +468,11 @@
             *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s",
                                       image_location, system_filename.c_str(),
                                       cache_filename.c_str(), reason.c_str());
+            // We failed to create files, remove any possibly garbage output.
+            // Since ImageCreationAllowed was true above, we are the zygote
+            // and therefore the only process expected to generate these for
+            // the device.
+            PruneDexCache(image_isa);
             return nullptr;
           }
         }
@@ -480,19 +519,23 @@
       return space;
     }
 
-    // If the /system file exists, it should be up-to-date, don't try to generate it. Same if it is
-    // a relocated copy from something in /system (i.e. checksum's match).
-    // Otherwise, log a warning and fall through to GenerateImage.
     if (relocated_version_used) {
-      LOG(FATAL) << "Attempted to use relocated version of " << image_location << " "
-                 << "at " << cache_filename << " generated from " << system_filename << " "
-                 << "but image failed to load: " << error_msg;
+      // Something is wrong with the relocated copy (even though checksums match). Cleanup.
+      // This can happen if the .oat is corrupt, since the above only checks the .art checksums.
+      // TODO: Check the oat file validity earlier.
+      *error_msg = StringPrintf("Attempted to use relocated version of %s at %s generated from %s "
+                                "but image failed to load: %s",
+                                image_location, cache_filename.c_str(), system_filename.c_str(),
+                                error_msg->c_str());
+      PruneDexCache(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.
       *error_msg = StringPrintf("Failed to load /system image '%s': %s",
                                 image_filename->c_str(), error_msg->c_str());
       return nullptr;
     } else {
+      // Otherwise, log a warning and fall through to GenerateImage.
       LOG(WARNING) << *error_msg;
     }
   }
@@ -508,8 +551,20 @@
   } else if (!GenerateImage(cache_filename, image_isa, error_msg)) {
     *error_msg = StringPrintf("Failed to generate image '%s': %s",
                               cache_filename.c_str(), error_msg->c_str());
+    // We failed to create files, remove any possibly garbage output.
+    // Since ImageCreationAllowed was true above, we are the zygote
+    // and therefore the only process expected to generate these for
+    // the device.
+    PruneDexCache(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.
+      PruneDexCache(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
@@ -629,6 +684,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));
 
@@ -653,7 +711,10 @@
   const ImageHeader& image_header = GetImageHeader();
   std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
 
+  CHECK(image_header.GetOatDataBegin() != nullptr);
+
   OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
+                                    image_header.GetOatFileBegin(),
                                     !Runtime::Current()->IsCompiler(), error_msg);
   if (oat_file == NULL) {
     *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
@@ -669,7 +730,7 @@
   }
   int32_t image_patch_delta = image_header.GetPatchDelta();
   int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
-  if (oat_patch_delta != image_patch_delta) {
+  if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {
     // We should have already relocated by this point. Bail out.
     *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
                               "in image %s", oat_patch_delta, image_patch_delta, GetName());
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..aee5586 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -27,11 +27,12 @@
 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) {
+  void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) const {
+    DCHECK(!IsNull());
     callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
+    DCHECK(!IsNull());
   }
 
   // This is only used by IrtIterator.
@@ -52,7 +53,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/image.cc b/runtime/image.cc
index 478b486..2a69128 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', '0', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
@@ -35,7 +35,8 @@
                          uint32_t oat_file_begin,
                          uint32_t oat_data_begin,
                          uint32_t oat_data_end,
-                         uint32_t oat_file_end)
+                         uint32_t oat_file_end,
+                         bool compile_pic)
   : image_begin_(image_begin),
     image_size_(image_size),
     image_bitmap_offset_(image_bitmap_offset),
@@ -46,7 +47,8 @@
     oat_data_end_(oat_data_end),
     oat_file_end_(oat_file_end),
     patch_delta_(0),
-    image_roots_(image_roots) {
+    image_roots_(image_roots),
+    compile_pic_(compile_pic) {
   CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
   CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
   CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
diff --git a/runtime/image.h b/runtime/image.h
index 424a40b..a77aec4 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -28,7 +28,7 @@
 // header of image files written by ImageWriter, read and validated by Space.
 class PACKED(4) ImageHeader {
  public:
-  ImageHeader() {}
+  ImageHeader() : compile_pic_(0) {}
 
   ImageHeader(uint32_t image_begin,
               uint32_t image_size_,
@@ -39,7 +39,8 @@
               uint32_t oat_file_begin,
               uint32_t oat_data_begin,
               uint32_t oat_data_end,
-              uint32_t oat_file_end);
+              uint32_t oat_file_end,
+              bool compile_pic_);
 
   bool IsValid() const;
   const char* GetMagic() const;
@@ -105,6 +106,7 @@
   enum ImageRoot {
     kResolutionMethod,
     kImtConflictMethod,
+    kImtUnimplementedMethod,
     kDefaultImt,
     kCalleeSaveMethod,
     kRefsOnlySaveMethod,
@@ -120,6 +122,10 @@
 
   void RelocateImage(off_t delta);
 
+  bool CompilePic() const {
+    return compile_pic_ != 0;
+  }
+
  private:
   static const byte kImageMagic[4];
   static const byte kImageVersion[4];
@@ -161,6 +167,9 @@
   // Absolute address of an Object[] of objects needed to reinitialize from an image.
   uint32_t image_roots_;
 
+  // Boolean (0 or 1) to denote if the image was compiled with --compile-pic option
+  const uint32_t compile_pic_;
+
   friend class ImageWriter;
 };
 
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index c66f99e..95c622a 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);
       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,39 @@
   RemoveWeak(s);
 }
 
-static mirror::String* LookupStringFromImage(mirror::String* s)
+void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space) {
+  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 +189,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 +214,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 +258,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, 0, kRootInternedString);
+  }
+  for (auto& intern : post_zygote_table_) {
+    intern.VisitRoot(callback, arg, 0, 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/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index e49a408..10a017c 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -181,8 +181,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 +264,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/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/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..7e20076 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);
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..b02f456 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!
@@ -329,6 +333,14 @@
   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);
   return pc - reinterpret_cast<uintptr_t>(code);
@@ -399,7 +411,7 @@
 }
 
 inline const DexFile* ArtMethod::GetDexFile() {
-  return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache()->GetDexFile();
+  return GetDexCache()->GetDexFile();
 }
 
 inline const char* ArtMethod::GetDeclaringClassDescriptor() {
@@ -519,11 +531,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 +547,21 @@
   return interface_method;
 }
 
+inline void ArtMethod::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
+                        new_dex_cache_strings);
+}
+
+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);
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 27499c2..5b833b9 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -89,21 +89,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 +103,6 @@
   return num_registers;
 }
 
-bool ArtMethod::IsProxyMethod() {
-  return GetDeclaringClass()->IsProxyClass();
-}
-
 ArtMethod* ArtMethod::FindOverriddenMethod() {
   if (IsStatic()) {
     return NULL;
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index abfdd42..36bb9e0 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -64,7 +64,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 +168,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,7 +196,7 @@
   // 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.
@@ -216,11 +219,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_);
@@ -409,6 +412,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_);
@@ -536,9 +541,11 @@
   static GcRoot<Class> java_lang_reflect_ArtMethod_;
 
  private:
-  ObjectArray<ArtMethod>* GetDexCacheResolvedMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ObjectArray<ArtMethod>* GetDexCacheResolvedMethods()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ObjectArray<Class>* GetDexCacheResolvedTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ALWAYS_INLINE ObjectArray<Class>* GetDexCacheResolvedTypes()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   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..0d109e5 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() {
@@ -733,6 +724,24 @@
   }
 }
 
+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();
+    }
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 5b8eb82..4ef6ea0 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -499,7 +499,10 @@
   if (GetDexCache() == dex_cache) {
     for (size_t i = 0; i < NumVirtualMethods(); ++i) {
       ArtMethod* method = GetVirtualMethod(i);
-      if (method->GetDexMethodIndex() == dex_method_idx) {
+      if (method->GetDexMethodIndex() == dex_method_idx &&
+          // A miranda method may have a different DexCache and is always created by linking,
+          // never *declared* in the class.
+          !method->IsMiranda()) {
         return method;
       }
     }
@@ -748,24 +751,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());
@@ -826,22 +811,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()) {
@@ -853,9 +837,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
@@ -864,7 +849,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_);
   }
 
@@ -873,10 +858,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);
@@ -884,17 +871,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 03a8563..5b87cbd 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -67,6 +67,7 @@
 struct ClassOffsets;
 class Signature;
 class StringPiece;
+template<size_t kNumReferences> class PACKED(4) StackHandleScope;
 
 namespace mirror {
 
@@ -215,46 +216,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;
   }
 
@@ -582,7 +583,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 +600,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.
@@ -638,12 +639,13 @@
 
   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 +654,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 +671,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 +683,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 +692,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 +717,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 +786,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_);
@@ -968,7 +966,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 +985,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.
@@ -1036,8 +1034,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_;
 
@@ -1150,11 +1146,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_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/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 01c8978..a8b297d 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);
@@ -287,9 +287,11 @@
 
 template <const bool kVerboseLogging, const bool kReasonLogging>
 static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
-                                   InstructionSet target_instruction_set) {
+                                   InstructionSet target_instruction_set,
+                                   bool* oat_is_pic) {
   std::string error_msg;
   std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
+                                                        nullptr,
                                                         false, &error_msg));
   if (oat_file.get() == nullptr) {
     if (kReasonLogging) {
@@ -299,6 +301,11 @@
     error_msg.clear();
     return kDexoptNeeded;
   }
+
+  // Pass-up the information about if this is PIC.
+  // TODO: Refactor this function to be less complicated.
+  *oat_is_pic = oat_file->IsPic();
+
   bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
   uint32_t location_checksum = 0;
   const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
@@ -526,10 +533,16 @@
   // Lets try the cache first (since we want to load from there since thats where the relocated
   // versions will be).
   if (have_cache_filename && !force_system_only) {
+    bool oat_is_pic;
     // We can use the dalvik-cache if we find a good file.
     dalvik_cache_decision =
         IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename,
-                                                               target_instruction_set);
+                                                               target_instruction_set, &oat_is_pic);
+
+    // Apps that are compiled with --compile-pic never need to be patchoat-d
+    if (oat_is_pic && dalvik_cache_decision == kPatchoatNeeded) {
+      dalvik_cache_decision = kUpToDate;
+    }
     // We will only return DexOptNeeded if both the cache and system return it.
     if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
       CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
@@ -539,12 +552,18 @@
     // We couldn't find one thats easy. We should now try the system.
   }
 
+  bool oat_is_pic;
   jbyte system_decision =
       IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename,
-                                                             target_instruction_set);
+                                                             target_instruction_set, &oat_is_pic);
   CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
       << "May not return PatchoatNeeded when patching is disabled.";
 
+  // Apps that are compiled with --compile-pic never need to be patchoat-d
+  if (oat_is_pic && system_decision == kPatchoatNeeded) {
+    system_decision = kUpToDate;
+  }
+
   if (require_system_version && system_decision == kPatchoatNeeded
                              && dalvik_cache_decision == kUpToDate) {
     // We have a version from system relocated to the cache. Return it.
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index fc1018a..4b1236a 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -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/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..9f77e55 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -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 ede108c..9d11de8 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -470,6 +470,12 @@
   return sizeof(OatHeader) + key_value_store_size_;
 }
 
+bool OatHeader::IsPic() const {
+  const char* pic_string = GetStoreValueByKey(OatHeader::kPicKey);
+  static const char kTrue[] = "true";
+  return (pic_string != nullptr && strncmp(pic_string, kTrue, sizeof(kTrue)) == 0);
+}
+
 void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
   char* data_ptr = reinterpret_cast<char*>(&key_value_store_);
   if (key_value_store != nullptr) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 6d5fefe..a5cb4bc 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -35,6 +35,7 @@
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDex2OatHostKey = "dex2oat-host";
+  static constexpr const char* kPicKey = "pic";
 
   static OatHeader* Create(InstructionSet instruction_set,
                            const InstructionSetFeatures& instruction_set_features,
@@ -103,6 +104,7 @@
   bool GetStoreKeyValuePairByIndex(size_t index, const char** key, const char** value) const;
 
   size_t GetHeaderSize() const;
+  bool IsPic() const;
 
  private:
   OatHeader(InstructionSet instruction_set,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 025f87d..9ba860c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -67,10 +67,11 @@
 OatFile* OatFile::Open(const std::string& filename,
                        const std::string& location,
                        byte* requested_base,
+                       uint8_t* oat_file_begin,
                        bool executable,
                        std::string* error_msg) {
   CHECK(!filename.empty()) << location;
-  CheckLocation(filename);
+  CheckLocation(location);
   std::unique_ptr<OatFile> ret;
   if (kUsePortableCompiler && executable) {
     // If we are using PORTABLE, use dlopen to deal with relocations.
@@ -91,7 +92,8 @@
       *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
       return nullptr;
     }
-    ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg));
+    ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
+                          error_msg));
 
     // It would be nice to unlink here. But we might have opened the file created by the
     // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
@@ -102,12 +104,12 @@
 
 OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
   CheckLocation(location);
-  return OpenElfFile(file, location, NULL, true, false, error_msg);
+  return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
 }
 
 OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
   CheckLocation(location);
-  return OpenElfFile(file, location, NULL, false, false, error_msg);
+  return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
 }
 
 OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
@@ -125,11 +127,13 @@
 OatFile* OatFile::OpenElfFile(File* file,
                               const std::string& location,
                               byte* requested_base,
+                              uint8_t* oat_file_begin,
                               bool writable,
                               bool executable,
                               std::string* error_msg) {
   std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
-  bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
+  bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
+                                       error_msg);
   if (!success) {
     CHECK(!error_msg->empty());
     return nullptr;
@@ -188,9 +192,12 @@
   return Setup(error_msg);
 }
 
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, uint8_t* oat_file_begin,
+                          bool writable, bool executable,
                           std::string* error_msg) {
-  elf_file_.reset(ElfFile::Open(file, writable, true, error_msg));
+  // TODO: rename requested_base to oat_data_begin
+  elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
+                                oat_file_begin));
   if (elf_file_.get() == nullptr) {
     DCHECK(!error_msg->empty());
     return false;
@@ -599,4 +606,9 @@
   method->SetNativeGcMap(GetNativeGcMap());
 }
 
+bool OatFile::IsPic() const {
+  return GetOatHeader().IsPic();
+  // TODO: Check against oat_patches. b/18144996
+}
+
 }  // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index e5cd6ec..488988e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -49,6 +49,7 @@
   static OatFile* Open(const std::string& filename,
                        const std::string& location,
                        byte* requested_base,
+                       uint8_t* oat_file_begin,
                        bool executable,
                        std::string* error_msg);
 
@@ -72,6 +73,8 @@
     return is_executable_;
   }
 
+  bool IsPic() const;
+
   ElfFile* GetElfFile() const {
     CHECK_NE(reinterpret_cast<uintptr_t>(elf_file_.get()), reinterpret_cast<uintptr_t>(nullptr))
         << "Cannot get an elf file from " << GetLocation();
@@ -303,13 +306,16 @@
   static OatFile* OpenElfFile(File* file,
                               const std::string& location,
                               byte* requested_base,
+                              uint8_t* oat_file_begin,  // Override base if not null
                               bool writable,
                               bool executable,
                               std::string* error_msg);
 
   explicit OatFile(const std::string& filename, bool executable);
   bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
-  bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+  bool ElfFileOpen(File* file, byte* requested_base,
+                   uint8_t* oat_file_begin,  // Override where the file is loaded to if not null
+                   bool writable, bool executable,
                    std::string* error_msg);
   bool Setup(std::string* error_msg);
 
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 98eeda7..d4713a0 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -229,7 +229,7 @@
                                       reinterpret_cast<mirror::Object*>(GetVReg(m, 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));
           } else {
@@ -237,14 +237,14 @@
           }
           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));
           }
           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));
           } else {
@@ -252,7 +252,7 @@
           }
           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));
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..053769a 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,14 @@
 
   started_ = true;
 
+  if (IsZygote()) {
+    ScopedObjectAccess soa(self);
+    Runtime::Current()->GetInternTable()->AddImageStringsToTable(heap_->GetImageSpace());
+    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 +444,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 +527,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.
@@ -881,8 +894,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;
@@ -1155,6 +1167,9 @@
   if (HasImtConflictMethod()) {
     imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
   }
+  if (!imt_unimplemented_method_.IsNull()) {
+    imt_unimplemented_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
+  }
   if (HasDefaultImt()) {
     default_imt_.VisitRoot(callback, arg, 0, kRootVMInternal);
   }
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8cfa8aa..60cb529 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -310,6 +310,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 +319,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 +519,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 +645,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/thread.cc b/runtime/thread.cc
index 45d799d..d231525 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;
 }
 
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 011bf96..24ff92e 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -625,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).
@@ -716,6 +717,55 @@
   VLOG(threads) << *self << " self-reviving (debugger)";
 }
 
+void ThreadList::ResumeAllForDebugger() {
+  Thread* self = Thread::Current();
+  Thread* debug_thread = Dbg::GetDebugThread();
+  bool needs_resume = false;
+
+  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_);
+      needs_resume = (debug_suspend_all_count_ > 0);
+      if (needs_resume) {
+        --suspend_all_count_;
+        --debug_suspend_all_count_;
+        // 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);
+        }
+      } else {
+        // We've been asked to resume all threads without being asked to
+        // suspend them all before. Let's print a warning.
+        LOG(WARNING) << "Debugger attempted to resume all threads without "
+                     << "having suspended them all before.";
+      }
+    }
+  }
+
+  if (needs_resume) {
+    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();
 
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index bb4f775..9f515a8 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -104,6 +104,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_);
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..5bdbba8 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"
@@ -280,6 +281,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').
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index d6c90e1..1b2c3ee 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"));
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5df4d6e..c88a295 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) {
@@ -1951,7 +1968,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 +2030,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:
@@ -2986,7 +3011,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;
+              }
             }
           }
         }
@@ -3312,17 +3342,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;
 }
 
@@ -3335,7 +3380,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
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/sigchainlib/Android.mk b/sigchainlib/Android.mk
index d86735d..e52adfc 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -22,10 +22,10 @@
 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)
@@ -37,7 +37,7 @@
 LOCAL_IS_HOST_MODULE := true
 LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
 LOCAL_CLANG = $(ART_HOST_CLANG)
-LOCAL_SRC_FILES := sigchain.cc
+LOCAL_SRC_FILES := sigchain_dummy.cc
 LOCAL_MODULE:= libsigchain
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_LDLIBS = -ldl
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 4991891..4f16c7f 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,10 +154,9 @@
   }
 }
 
-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
@@ -192,7 +190,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 +222,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/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/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/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/117-nopatchoat/expected.txt b/test/117-nopatchoat/expected.txt
index a1293ae..5cc02d1 100644
--- a/test/117-nopatchoat/expected.txt
+++ b/test/117-nopatchoat/expected.txt
@@ -1,9 +1,9 @@
 Run without dex2oat/patchoat
-dex2oat & patchoat are disabled, has oat is true, has executable oat is false.
+dex2oat & patchoat are disabled, has oat is true, has executable oat is expected.
 This is a function call
 Run with dexoat/patchoat
-dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+dex2oat & patchoat are enabled, has oat is true, has executable oat is expected.
 This is a function call
 Run default
-dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+dex2oat & patchoat are enabled, has oat is true, has executable oat is expected.
 This is a function call
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index 5994653..da276f2 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -24,18 +24,41 @@
 
 class NoPatchoatTest {
  public:
-  static bool hasExecutableOat(jclass cls) {
+  static const OatFile::OatDexFile* getOatDexFile(jclass cls) {
     ScopedObjectAccess soa(Thread::Current());
     mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
     const DexFile& dex_file = klass->GetDexFile();
+
     const OatFile::OatDexFile* oat_dex_file =
         Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
+
+    return oat_dex_file;
+  }
+
+  static bool hasExecutableOat(jclass cls) {
+    const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls);
+
     return oat_dex_file != nullptr && oat_dex_file->GetOatFile()->IsExecutable();
   }
+
+  static bool isPic(jclass cls) {
+    const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls);
+
+    if (oat_dex_file == nullptr) {
+      return false;
+    }
+
+    const OatFile* oat_file = oat_dex_file->GetOatFile();
+    return oat_file->IsPic();
+  }
 };
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasExecutableOat(JNIEnv*, jclass cls) {
   return NoPatchoatTest::hasExecutableOat(cls);
 }
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isPic(JNIEnv*, jclass cls) {
+  return NoPatchoatTest::isPic(cls);
+}
+
 }  // namespace art
diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java
index f3f91ce..7bc9dbb 100644
--- a/test/117-nopatchoat/src/Main.java
+++ b/test/117-nopatchoat/src/Main.java
@@ -16,9 +16,14 @@
 
 public class Main {
   public static void main(String[] args) {
+    boolean executable_correct = (isPic() ?
+                                  hasExecutableOat() == true :
+                                  hasExecutableOat() == isDex2OatEnabled());
+
     System.out.println(
         "dex2oat & patchoat are " + ((isDex2OatEnabled()) ? "enabled" : "disabled") +
-        ", has oat is " + hasOat() + ", has executable oat is " + hasExecutableOat() + ".");
+        ", has oat is " + hasOat() + ", has executable oat is " + (
+        executable_correct ? "expected" : "not expected") + ".");
 
     if (!hasOat() && isDex2OatEnabled()) {
       throw new Error("Application with dex2oat enabled runs without an oat file");
@@ -42,6 +47,8 @@
 
   private native static boolean isDex2OatEnabled();
 
+  private native static boolean isPic();
+
   private native static boolean hasOat();
 
   private native static boolean hasExecutableOat();
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/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/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 7253a2b..37c579d 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -23,8 +23,10 @@
 FLAGS=""
 COMPILER_FLAGS=""
 BUILD_BOOT_OPT=""
+SECONDARY_DEX=""
 exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
 main="Main"
+DEX_VERIFY=""
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -91,6 +93,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"
@@ -154,7 +159,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 +190,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 +202,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