Merge "Clear inline caches at each full GC."
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 239bc59..6075cd6 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -187,7 +187,9 @@
   }
 }
 
-void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) {
+void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind,
+                                              InstructionSet isa,
+                                              size_t number_of_threads) {
   compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
                                             verification_results_.get(),
                                             method_inliner_map_.get(),
@@ -198,7 +200,7 @@
                                             GetImageClasses(),
                                             GetCompiledClasses(),
                                             GetCompiledMethods(),
-                                            /* thread_count */ 2,
+                                            number_of_threads,
                                             /* dump_stats */ true,
                                             /* dump_passes */ true,
                                             timer_.get(),
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 7e0fbab..9552143 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -93,7 +93,7 @@
                             const char* method_name, const char* signature)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa);
+  void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads = 2U);
 
   void ReserveImageSpace();
 
diff --git a/compiler/debug/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h
index 58502a3..b4a4d63 100644
--- a/compiler/debug/dwarf/debug_line_opcode_writer.h
+++ b/compiler/debug/dwarf/debug_line_opcode_writer.h
@@ -36,7 +36,7 @@
 
  public:
   static constexpr int kOpcodeBase = 13;
-  static constexpr bool kDefaultIsStmt = true;
+  static constexpr bool kDefaultIsStmt = false;
   static constexpr int kLineBase = -5;
   static constexpr int kLineRange = 14;
 
@@ -81,8 +81,11 @@
     this->PushUleb128(column);
   }
 
-  void NegateStmt() {
-    this->PushUint8(DW_LNS_negate_stmt);
+  void SetIsStmt(bool is_stmt) {
+    if (is_stmt_ != is_stmt) {
+      this->PushUint8(DW_LNS_negate_stmt);
+      is_stmt_ = is_stmt;
+    }
   }
 
   void SetBasicBlock() {
@@ -112,6 +115,7 @@
     current_address_ = 0;
     current_file_ = 1;
     current_line_ = 1;
+    is_stmt_ = kDefaultIsStmt;
   }
 
   // Uncoditionally set address using the long encoding.
@@ -227,7 +231,8 @@
         code_factor_bits_(codeFactorBits),
         current_address_(0),
         current_file_(1),
-        current_line_(1) {
+        current_line_(1),
+        is_stmt_(kDefaultIsStmt) {
   }
 
  private:
@@ -244,6 +249,7 @@
   uint64_t current_address_;
   int current_file_;
   int current_line_;
+  bool is_stmt_;
   std::vector<uintptr_t> patch_locations_;
 
   DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index e455d0d..2ba3af5 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -217,7 +217,9 @@
   DW_CHECK_NEXT("Advance Line by 2 to 3");
   opcodes.SetColumn(4);
   DW_CHECK_NEXT("Set column to 4");
-  opcodes.NegateStmt();
+  opcodes.SetIsStmt(true);
+  DW_CHECK_NEXT("Set is_stmt to 1");
+  opcodes.SetIsStmt(false);
   DW_CHECK_NEXT("Set is_stmt to 0");
   opcodes.SetBasicBlock();
   DW_CHECK_NEXT("Set basic block");
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index 41bfe79..e2f0a65 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -62,7 +62,7 @@
     InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86;
     ScratchFile file;
     FileOutputStream output_stream(file.GetFile());
-    ElfBuilder<ElfTypes> builder(isa, &output_stream);
+    ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream);
     builder.Start();
     if (!debug_info_data_.empty()) {
       builder.WriteSection(".debug_info", &debug_info_data_);
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index d3859ca..11be4e9 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -184,6 +184,10 @@
 
       // Generate mapping opcodes from PC to Java lines.
       if (file_index != 0) {
+        // If the method was not compiled as native-debuggable, we still generate all available
+        // lines, but we try to prevent the debugger from stepping and setting breakpoints since
+        // the information is too inaccurate for that (breakpoints would be set after the calls).
+        const bool default_is_stmt = mi->is_native_debuggable;
         bool first = true;
         for (SrcMapElem pc2dex : pc2dex_map) {
           uint32_t pc = pc2dex.from_;
@@ -205,13 +209,14 @@
                 // Assume that any preceding code is prologue.
                 int first_line = dex2line_map.front().line_;
                 // Prologue is not a sensible place for a breakpoint.
-                opcodes.NegateStmt();
+                opcodes.SetIsStmt(false);
                 opcodes.AddRow(method_address, first_line);
-                opcodes.NegateStmt();
                 opcodes.SetPrologueEnd();
               }
+              opcodes.SetIsStmt(default_is_stmt);
               opcodes.AddRow(method_address + pc, line);
             } else if (line != opcodes.CurrentLine()) {
+              opcodes.SetIsStmt(default_is_stmt);
               opcodes.AddRow(method_address + pc, line);
             }
           }
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 01bd679..79069d1 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -91,24 +91,34 @@
 
 std::vector<uint8_t> MakeMiniDebugInfo(
     InstructionSet isa,
+    const InstructionSetFeatures* features,
     size_t rodata_size,
     size_t text_size,
     const ArrayRef<const MethodDebugInfo>& method_infos) {
   if (Is64BitInstructionSet(isa)) {
-    return MakeMiniDebugInfoInternal<ElfTypes64>(isa, rodata_size, text_size, method_infos);
+    return MakeMiniDebugInfoInternal<ElfTypes64>(isa,
+                                                 features,
+                                                 rodata_size,
+                                                 text_size,
+                                                 method_infos);
   } else {
-    return MakeMiniDebugInfoInternal<ElfTypes32>(isa, rodata_size, text_size, method_infos);
+    return MakeMiniDebugInfoInternal<ElfTypes32>(isa,
+                                                 features,
+                                                 rodata_size,
+                                                 text_size,
+                                                 method_infos);
   }
 }
 
 template <typename ElfTypes>
 static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal(
+    const InstructionSet isa,
+    const InstructionSetFeatures* features,
     const MethodDebugInfo& method_info) {
-  const InstructionSet isa = method_info.compiled_method->GetInstructionSet();
   std::vector<uint8_t> buffer;
   buffer.reserve(KB);
   VectorOutputStream out("Debug ELF file", &buffer);
-  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
   // No program headers since the ELF file is not linked and has no allocated sections.
   builder->Start(false /* write_program_headers */);
   WriteDebugInfo(builder.get(),
@@ -124,23 +134,26 @@
   return ArrayRef<const uint8_t>(result, buffer.size());
 }
 
-ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info) {
-  const InstructionSet isa = method_info.compiled_method->GetInstructionSet();
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const InstructionSet isa,
+                                                   const InstructionSetFeatures* features,
+                                                   const MethodDebugInfo& method_info) {
   if (Is64BitInstructionSet(isa)) {
-    return WriteDebugElfFileForMethodInternal<ElfTypes64>(method_info);
+    return WriteDebugElfFileForMethodInternal<ElfTypes64>(isa, features, method_info);
   } else {
-    return WriteDebugElfFileForMethodInternal<ElfTypes32>(method_info);
+    return WriteDebugElfFileForMethodInternal<ElfTypes32>(isa, features, method_info);
   }
 }
 
 template <typename ElfTypes>
 static ArrayRef<const uint8_t> WriteDebugElfFileForClassesInternal(
-    const InstructionSet isa, const ArrayRef<mirror::Class*>& types)
+    const InstructionSet isa,
+    const InstructionSetFeatures* features,
+    const ArrayRef<mirror::Class*>& types)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   std::vector<uint8_t> buffer;
   buffer.reserve(KB);
   VectorOutputStream out("Debug ELF file", &buffer);
-  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
   // No program headers since the ELF file is not linked and has no allocated sections.
   builder->Start(false /* write_program_headers */);
   ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
@@ -159,11 +172,12 @@
 }
 
 ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
+                                                    const InstructionSetFeatures* features,
                                                     const ArrayRef<mirror::Class*>& types) {
   if (Is64BitInstructionSet(isa)) {
-    return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, types);
+    return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, features, types);
   } else {
-    return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, types);
+    return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, features, types);
   }
 }
 
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 103b501..d364521 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -37,13 +37,17 @@
                     bool write_oat_patches);
 
 std::vector<uint8_t> MakeMiniDebugInfo(InstructionSet isa,
+                                       const InstructionSetFeatures* features,
                                        size_t rodata_section_size,
                                        size_t text_section_size,
                                        const ArrayRef<const MethodDebugInfo>& method_infos);
 
-ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info);
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const InstructionSet isa,
+                                                   const InstructionSetFeatures* features,
+                                                   const MethodDebugInfo& method_info);
 
 ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
+                                                    const InstructionSetFeatures* features,
                                                     const ArrayRef<mirror::Class*>& types)
     SHARED_REQUIRES(Locks::mutator_lock_);
 
diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h
index 5c7d1c7..fb63d62 100644
--- a/compiler/debug/elf_gnu_debugdata_writer.h
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -79,13 +79,14 @@
 template <typename ElfTypes>
 static std::vector<uint8_t> MakeMiniDebugInfoInternal(
     InstructionSet isa,
+    const InstructionSetFeatures* features,
     size_t rodata_section_size,
     size_t text_section_size,
     const ArrayRef<const MethodDebugInfo>& method_infos) {
   std::vector<uint8_t> buffer;
   buffer.reserve(KB);
   VectorOutputStream out("Mini-debug-info ELF file", &buffer);
-  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
   builder->Start();
   // Mirror .rodata and .text as NOBITS sections.
   // It is needed to detected relocations after compression.
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index 6b3dd8c..bb09f7e 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -30,6 +30,7 @@
   uint32_t access_flags;
   const DexFile::CodeItem* code_item;
   bool deduped;
+  bool is_native_debuggable;
   uintptr_t low_pc;
   uintptr_t high_pc;
   CompiledMethod* compiled_method;
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index ad4ddad..8f5d3ae 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -101,6 +101,14 @@
     false,  // kIntrinsicCas
     false,  // kIntrinsicUnsafeGet
     false,  // kIntrinsicUnsafePut
+    false,  // kIntrinsicUnsafeGetAndAddInt,
+    false,  // kIntrinsicUnsafeGetAndAddLong,
+    false,  // kIntrinsicUnsafeGetAndSetInt,
+    false,  // kIntrinsicUnsafeGetAndSetLong,
+    false,  // kIntrinsicUnsafeGetAndSetObject,
+    false,  // kIntrinsicUnsafeLoadFence,
+    false,  // kIntrinsicUnsafeStoreFence,
+    false,  // kIntrinsicUnsafeFullFence,
     true,   // kIntrinsicSystemArrayCopyCharArray
     true,   // kIntrinsicSystemArrayCopy
 };
@@ -177,6 +185,14 @@
 static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static");
 static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet must not be static");
 static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddInt], "UnsafeGetAndAddInt must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddLong], "UnsafeGetAndAddLong must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetInt], "UnsafeGetAndSetInt must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetLong], "UnsafeGetAndSetLong must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetObject], "UnsafeGetAndSetObject must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeLoadFence], "UnsafeLoadFence must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeStoreFence], "UnsafeStoreFence must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeFullFence], "UnsafeFullFence must not be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
               "SystemArrayCopyCharArray must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopy],
@@ -318,6 +334,14 @@
     "putObject",             // kNameCachePutObject
     "putObjectVolatile",     // kNameCachePutObjectVolatile
     "putOrderedObject",      // kNameCachePutOrderedObject
+    "getAndAddInt",          // kNameCacheGetAndAddInt,
+    "getAndAddLong",         // kNameCacheGetAndAddLong,
+    "getAndSetInt",          // kNameCacheGetAndSetInt,
+    "getAndSetLong",         // kNameCacheGetAndSetLong,
+    "getAndSetObject",       // kNameCacheGetAndSetObject,
+    "loadFence",             // kNameCacheLoadFence,
+    "storeFence",            // kNameCacheStoreFence,
+    "fullFence",             // kNameCacheFullFence,
     "arraycopy",             // kNameCacheArrayCopy
     "bitCount",              // kNameCacheBitCount
     "compare",               // kNameCacheCompare
@@ -404,10 +428,14 @@
         kClassCacheJavaLangObject, kClassCacheJavaLangObject } },
     // kProtoCacheObjectJ_I
     { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
+    // kProtoCacheObjectJI_I
+    { kClassCacheInt, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
     // kProtoCacheObjectJI_V
     { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
     // kProtoCacheObjectJ_J
     { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
+    // kProtoCacheObjectJJ_J
+    { kClassCacheLong, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
     // kProtoCacheObjectJJ_V
     { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
     // kProtoCacheObjectJ_Object
@@ -415,6 +443,9 @@
     // kProtoCacheObjectJObject_V
     { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong,
         kClassCacheJavaLangObject } },
+    // kProtoCacheObjectJObject_Object
+    { kClassCacheJavaLangObject, 3, { kClassCacheJavaLangObject, kClassCacheLong,
+        kClassCacheJavaLangObject } },
     // kProtoCacheCharArrayICharArrayII_V
     { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt,
         kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} },
@@ -609,6 +640,16 @@
     UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject),
 #undef UNSAFE_GET_PUT
 
+    // 1.8
+    INTRINSIC(SunMiscUnsafe, GetAndAddInt, ObjectJI_I, kIntrinsicUnsafeGetAndAddInt, 0),
+    INTRINSIC(SunMiscUnsafe, GetAndAddLong, ObjectJJ_J, kIntrinsicUnsafeGetAndAddLong, 0),
+    INTRINSIC(SunMiscUnsafe, GetAndSetInt, ObjectJI_I, kIntrinsicUnsafeGetAndSetInt, 0),
+    INTRINSIC(SunMiscUnsafe, GetAndSetLong, ObjectJJ_J, kIntrinsicUnsafeGetAndSetLong, 0),
+    INTRINSIC(SunMiscUnsafe, GetAndSetObject, ObjectJObject_Object, kIntrinsicUnsafeGetAndSetObject, 0),
+    INTRINSIC(SunMiscUnsafe, LoadFence, _V, kIntrinsicUnsafeLoadFence, 0),
+    INTRINSIC(SunMiscUnsafe, StoreFence, _V, kIntrinsicUnsafeStoreFence, 0),
+    INTRINSIC(SunMiscUnsafe, FullFence, _V, kIntrinsicUnsafeFullFence, 0),
+
     INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
               0),
     INTRINSIC(JavaLangSystem, ArrayCopy, ObjectIObjectII_V , kIntrinsicSystemArrayCopy,
@@ -815,6 +856,14 @@
     case kIntrinsicRotateRight:
     case kIntrinsicRotateLeft:
     case kIntrinsicSignum:
+    case kIntrinsicUnsafeGetAndAddInt:
+    case kIntrinsicUnsafeGetAndAddLong:
+    case kIntrinsicUnsafeGetAndSetInt:
+    case kIntrinsicUnsafeGetAndSetLong:
+    case kIntrinsicUnsafeGetAndSetObject:
+    case kIntrinsicUnsafeLoadFence:
+    case kIntrinsicUnsafeStoreFence:
+    case kIntrinsicUnsafeFullFence:
     case kIntrinsicSystemArrayCopy:
       return false;   // not implemented in quick.
     default:
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index b465db2..34b56cd 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -227,6 +227,14 @@
       kNameCachePutObject,
       kNameCachePutObjectVolatile,
       kNameCachePutOrderedObject,
+      kNameCacheGetAndAddInt,
+      kNameCacheGetAndAddLong,
+      kNameCacheGetAndSetInt,
+      kNameCacheGetAndSetLong,
+      kNameCacheGetAndSetObject,
+      kNameCacheLoadFence,
+      kNameCacheStoreFence,
+      kNameCacheFullFence,
       kNameCacheArrayCopy,
       kNameCacheBitCount,
       kNameCacheCompare,
@@ -282,11 +290,14 @@
       kProtoCacheObjectJJJ_Z,
       kProtoCacheObjectJObjectObject_Z,
       kProtoCacheObjectJ_I,
+      kProtoCacheObjectJI_I,
       kProtoCacheObjectJI_V,
       kProtoCacheObjectJ_J,
+      kProtoCacheObjectJJ_J,
       kProtoCacheObjectJJ_V,
       kProtoCacheObjectJ_Object,
       kProtoCacheObjectJObject_V,
+      kProtoCacheObjectJObject_Object,
       kProtoCacheCharArrayICharArrayII_V,
       kProtoCacheObjectIObjectII_V,
       kProtoCacheIICharArrayI_V,
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a220959..4db82a6 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -159,10 +159,16 @@
   size_t GetInlineDepthLimit() const {
     return inline_depth_limit_;
   }
+  void SetInlineDepthLimit(size_t limit) {
+    inline_depth_limit_ = limit;
+  }
 
   size_t GetInlineMaxCodeUnits() const {
     return inline_max_code_units_;
   }
+  void SetInlineMaxCodeUnits(size_t units) {
+    inline_max_code_units_ = units;
+  }
 
   double GetTopKProfileThreshold() const {
     return top_k_profile_threshold_;
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index f7da609..943e2a8 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "arch/instruction_set.h"
+#include "arch/mips/instruction_set_features_mips.h"
 #include "base/bit_utils.h"
 #include "base/casts.h"
 #include "base/unix_file/fd_file.h"
@@ -392,8 +393,9 @@
     }
   };
 
-  ElfBuilder(InstructionSet isa, OutputStream* output)
+  ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output)
       : isa_(isa),
+        features_(features),
         stream_(output),
         rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
         text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0),
@@ -818,6 +820,7 @@
   }
 
   InstructionSet isa_;
+  const InstructionSetFeatures* features_;
 
   ErrorDelayingOutputStream stream_;
 
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 19346ec..e35662d 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -51,10 +51,12 @@
 class DebugInfoTask : public Task {
  public:
   DebugInfoTask(InstructionSet isa,
+                const InstructionSetFeatures* features,
                 size_t rodata_section_size,
                 size_t text_section_size,
                 const ArrayRef<const debug::MethodDebugInfo>& method_infos)
       : isa_(isa),
+        instruction_set_features_(features),
         rodata_section_size_(rodata_section_size),
         text_section_size_(text_section_size),
         method_infos_(method_infos) {
@@ -62,6 +64,7 @@
 
   void Run(Thread*) {
     result_ = debug::MakeMiniDebugInfo(isa_,
+                                       instruction_set_features_,
                                        rodata_section_size_,
                                        text_section_size_,
                                        method_infos_);
@@ -73,6 +76,7 @@
 
  private:
   InstructionSet isa_;
+  const InstructionSetFeatures* instruction_set_features_;
   size_t rodata_section_size_;
   size_t text_section_size_;
   const ArrayRef<const debug::MethodDebugInfo>& method_infos_;
@@ -83,6 +87,7 @@
 class ElfWriterQuick FINAL : public ElfWriter {
  public:
   ElfWriterQuick(InstructionSet instruction_set,
+                 const InstructionSetFeatures* features,
                  const CompilerOptions* compiler_options,
                  File* elf_file);
   ~ElfWriterQuick();
@@ -107,6 +112,7 @@
                                std::vector<uint8_t>* buffer);
 
  private:
+  const InstructionSetFeatures* instruction_set_features_;
   const CompilerOptions* const compiler_options_;
   File* const elf_file_;
   size_t rodata_size_;
@@ -121,27 +127,36 @@
 };
 
 std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
+                                                const InstructionSetFeatures* features,
                                                 const CompilerOptions* compiler_options,
                                                 File* elf_file) {
   if (Is64BitInstructionSet(instruction_set)) {
-    return MakeUnique<ElfWriterQuick<ElfTypes64>>(instruction_set, compiler_options, elf_file);
+    return MakeUnique<ElfWriterQuick<ElfTypes64>>(instruction_set,
+                                                  features,
+                                                  compiler_options,
+                                                  elf_file);
   } else {
-    return MakeUnique<ElfWriterQuick<ElfTypes32>>(instruction_set, compiler_options, elf_file);
+    return MakeUnique<ElfWriterQuick<ElfTypes32>>(instruction_set,
+                                                  features,
+                                                  compiler_options,
+                                                  elf_file);
   }
 }
 
 template <typename ElfTypes>
 ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set,
+                                         const InstructionSetFeatures* features,
                                          const CompilerOptions* compiler_options,
                                          File* elf_file)
     : ElfWriter(),
+      instruction_set_features_(features),
       compiler_options_(compiler_options),
       elf_file_(elf_file),
       rodata_size_(0u),
       text_size_(0u),
       bss_size_(0u),
       output_stream_(MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file))),
-      builder_(new ElfBuilder<ElfTypes>(instruction_set, output_stream_.get())) {}
+      builder_(new ElfBuilder<ElfTypes>(instruction_set, features, output_stream_.get())) {}
 
 template <typename ElfTypes>
 ElfWriterQuick<ElfTypes>::~ElfWriterQuick() {}
@@ -205,7 +220,11 @@
     // Prepare the mini-debug-info in background while we do other I/O.
     Thread* self = Thread::Current();
     debug_info_task_ = std::unique_ptr<DebugInfoTask>(
-        new DebugInfoTask(builder_->GetIsa(), rodata_size_, text_size_, method_infos));
+        new DebugInfoTask(builder_->GetIsa(),
+                          instruction_set_features_,
+                          rodata_size_,
+                          text_size_,
+                          method_infos));
     debug_info_thread_pool_ = std::unique_ptr<ThreadPool>(
         new ThreadPool("Mini-debug-info writer", 1));
     debug_info_thread_pool_->AddTask(self, debug_info_task_.get());
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 347d372..3d5dd39 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -26,8 +26,10 @@
 namespace art {
 
 class CompilerOptions;
+class InstructionSetFeatures;
 
 std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
+                                                const InstructionSetFeatures* features,
                                                 const CompilerOptions* compiler_options,
                                                 File* elf_file);
 
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 5763cec..91579e9 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -24,6 +24,7 @@
 #include "class_linker-inl.h"
 #include "common_compiler_test.h"
 #include "debug/method_debug_info.h"
+#include "driver/compiler_options.h"
 #include "elf_writer.h"
 #include "elf_writer_quick.h"
 #include "gc/space/image_space.h"
@@ -48,8 +49,12 @@
 };
 
 void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
-  // TODO: Test does not currently work with optimizing.
-  CreateCompilerDriver(Compiler::kQuick, kRuntimeISA);
+  CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
+
+  // Set inline filter values.
+  compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit);
+  compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   // Enable write for dex2dex.
   for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
@@ -99,6 +104,7 @@
       const std::vector<const DexFile*>& dex_files = class_linker->GetBootClassPath();
       std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
           compiler_driver_->GetInstructionSet(),
+          compiler_driver_->GetInstructionSetFeatures(),
           &compiler_driver_->GetCompilerOptions(),
           oat_file.GetFile());
       elf_writer->Start();
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 871435b..b1b971f 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -266,17 +266,9 @@
                      << PrettyDuration(NanoTime() - compress_start_time);
     }
 
-    // Write header first, as uncompressed.
-    image_header->data_size_ = data_size;
-    if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) {
-      PLOG(ERROR) << "Failed to write image file header " << image_filename;
-      image_file->Erase();
-      return false;
-    }
-
     // Write out the image + fields + methods.
     const bool is_compressed = compressed_data != nullptr;
-    if (!image_file->WriteFully(image_data_to_write, data_size)) {
+    if (!image_file->PwriteFully(image_data_to_write, data_size, sizeof(ImageHeader))) {
       PLOG(ERROR) << "Failed to write image file data " << image_filename;
       image_file->Erase();
       return false;
@@ -291,13 +283,33 @@
     if (!is_compressed) {
       CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
     }
-    if (!image_file->Write(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
-                           bitmap_section.Size(),
-                           bitmap_position_in_file)) {
+    if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
+                                 bitmap_section.Size(),
+                                 bitmap_position_in_file)) {
       PLOG(ERROR) << "Failed to write image file " << image_filename;
       image_file->Erase();
       return false;
     }
+
+    int err = image_file->Flush();
+    if (err < 0) {
+      PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err;
+      image_file->Erase();
+      return false;
+    }
+
+    // Write header last in case the compiler gets killed in the middle of image writing.
+    // We do not want to have a corrupted image with a valid header.
+    // The header is uncompressed since it contains whether the image is compressed or not.
+    image_header->data_size_ = data_size;
+    if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_->Begin()),
+                                 sizeof(ImageHeader),
+                                 0)) {
+      PLOG(ERROR) << "Failed to write image file header " << image_filename;
+      image_file->Erase();
+      return false;
+    }
+
     CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
              static_cast<size_t>(image_file->GetLength()));
     if (image_file->FlushCloseOrErase() != 0) {
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index dba9dd7..f204b28 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -443,7 +443,7 @@
 
   static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
 
-  uintptr_t NativeOffsetInImage(void* obj);
+  uintptr_t NativeOffsetInImage(void* obj) SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Location of where the object will be when the image is loaded at runtime.
   template <typename T>
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index eeb2576..cda2e27 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -69,7 +69,8 @@
   DCHECK(jit_compiler != nullptr);
   if (jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo()) {
     const ArrayRef<mirror::Class*> types_array(types, count);
-    ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForClasses(kRuntimeISA, types_array);
+    ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForClasses(
+        kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array);
     CreateJITCodeEntry(std::unique_ptr<const uint8_t[]>(elf_file.data()), elf_file.size());
   }
 }
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index bc134fe..533dccf 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -42,6 +42,9 @@
   CompilerOptions* GetCompilerOptions() const {
     return compiler_options_.get();
   }
+  CompilerDriver* GetCompilerDriver() const {
+    return compiler_driver_.get();
+  }
 
  private:
   std::unique_ptr<CompilerOptions> compiler_options_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 14fd105..d22044a 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -176,6 +176,7 @@
                   bool verify) {
     std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
         compiler_driver_->GetInstructionSet(),
+        compiler_driver_->GetInstructionSetFeatures(),
         &compiler_driver_->GetCompilerOptions(),
         file);
     elf_writer->Start();
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index c60b02a..50f5aba 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -806,7 +806,8 @@
         }
       }
 
-      if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) {
+      const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions();
+      if (compiler_options.GenerateAnyDebugInfo()) {
         // Record debug information for this function if we are doing that.
         const uint32_t quick_code_start = quick_code_offset -
             writer_->oat_header_->GetExecutableOffset() - thumb_offset;
@@ -817,6 +818,7 @@
             it.GetMethodAccessFlags(),
             it.GetMethodCodeItem(),
             deduped,
+            compiler_options.GetNativeDebuggable(),
             quick_code_start,
             quick_code_start + code_size,
             compiled_method});
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index f8a9a94..b95ece5 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -94,6 +94,7 @@
   void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
   void SimplifyIsNaN(HInvoke* invoke);
   void SimplifyFP2Int(HInvoke* invoke);
+  void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
 
   OptimizingCompilerStats* stats_;
   bool simplification_occurred_ = false;
@@ -1594,6 +1595,12 @@
   invoke->ReplaceWithExceptInReplacementAtIndex(select, 0);  // false at index 0
 }
 
+void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) {
+  uint32_t dex_pc = invoke->GetDexPc();
+  HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc);
+  invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier);
+}
+
 void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
   switch (instruction->GetIntrinsic()) {
     case Intrinsics::kStringEquals:
@@ -1626,6 +1633,15 @@
     case Intrinsics::kDoubleDoubleToLongBits:
       SimplifyFP2Int(instruction);
       break;
+    case Intrinsics::kUnsafeLoadFence:
+      SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny);
+      break;
+    case Intrinsics::kUnsafeStoreFence:
+      SimplifyMemBarrier(instruction, MemBarrierKind::kAnyStore);
+      break;
+    case Intrinsics::kUnsafeFullFence:
+      SimplifyMemBarrier(instruction, MemBarrierKind::kAnyAny);
+      break;
     default:
       break;
   }
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 3ed0278..5d4c4e2 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -472,6 +472,24 @@
       break;
     }
 
+    // 1.8.
+    case kIntrinsicUnsafeGetAndAddInt:
+      return Intrinsics::kUnsafeGetAndAddInt;
+    case kIntrinsicUnsafeGetAndAddLong:
+      return Intrinsics::kUnsafeGetAndAddLong;
+    case kIntrinsicUnsafeGetAndSetInt:
+      return Intrinsics::kUnsafeGetAndSetInt;
+    case kIntrinsicUnsafeGetAndSetLong:
+      return Intrinsics::kUnsafeGetAndSetLong;
+    case kIntrinsicUnsafeGetAndSetObject:
+      return Intrinsics::kUnsafeGetAndSetObject;
+    case kIntrinsicUnsafeLoadFence:
+      return Intrinsics::kUnsafeLoadFence;
+    case kIntrinsicUnsafeStoreFence:
+      return Intrinsics::kUnsafeStoreFence;
+    case kIntrinsicUnsafeFullFence:
+      return Intrinsics::kUnsafeFullFence;
+
     // Virtual cases.
 
     case kIntrinsicReferenceGetReferent:
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 0cec5cc..3da8285 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -231,7 +231,10 @@
 UNREACHABLE_INTRINSIC(Arch, IntegerCompare)         \
 UNREACHABLE_INTRINSIC(Arch, LongCompare)            \
 UNREACHABLE_INTRINSIC(Arch, IntegerSignum)          \
-UNREACHABLE_INTRINSIC(Arch, LongSignum)
+UNREACHABLE_INTRINSIC(Arch, LongSignum)             \
+UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence)        \
+UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence)       \
+UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence)
 
 }  // namespace art
 
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 69c9708..c7d9a0d 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -2002,6 +2002,13 @@
 UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
 UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
 
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
+
 UNREACHABLE_INTRINSICS(ARM)
 
 #undef __
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 934b427..8a44441 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1953,6 +1953,13 @@
 UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
 UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
 
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
+
 UNREACHABLE_INTRINSICS(ARM64)
 
 #undef __
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index b8933e1..dd9294d 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -128,6 +128,14 @@
   V(UnsafePutLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
   V(UnsafePutLongOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
   V(UnsafePutLongVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeGetAndAddInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeGetAndAddLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeGetAndSetInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeGetAndSetLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeGetAndSetObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeLoadFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeStoreFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+  V(UnsafeFullFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
   V(ReferenceGetReferent, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow)
 
 #endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 710df0a..e159701 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1832,9 +1832,14 @@
 UNIMPLEMENTED_INTRINSIC(MIPS, MathTan)
 UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh)
 
-UNREACHABLE_INTRINSICS(MIPS)
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject)
 
-#undef UNIMPLEMENTED_INTRINSIC
+UNREACHABLE_INTRINSICS(MIPS)
 
 #undef __
 
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 617844b..0dc602f 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1729,6 +1729,13 @@
 UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerLowestOneBit)
 UNIMPLEMENTED_INTRINSIC(MIPS64, LongLowestOneBit)
 
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject)
+
 UNREACHABLE_INTRINSICS(MIPS64)
 
 #undef __
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 9a2dc41..4c802c0 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2637,6 +2637,13 @@
 UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit)
 UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit)
 
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject)
+
 UNREACHABLE_INTRINSICS(X86)
 
 #undef __
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 75204b4..41095cd 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2715,6 +2715,13 @@
 UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
 UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
 
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetObject)
+
 UNREACHABLE_INTRINSICS(X86_64)
 
 #undef __
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b684cc6..ecb690f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
 #include "base/arena_object.h"
 #include "base/stl_util.h"
 #include "dex/compiler_enums.h"
+#include "dex_instruction-inl.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "handle.h"
 #include "handle_scope.h"
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c1b4d24..42f22af 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -913,7 +913,8 @@
     return false;
   }
 
-  if (GetCompilerDriver()->GetCompilerOptions().GetGenerateDebugInfo()) {
+  const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
+  if (compiler_options.GetGenerateDebugInfo()) {
     const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code);
     const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode());
     CompiledMethod compiled_method(
@@ -936,11 +937,15 @@
         access_flags,
         code_item,
         false,  // deduped.
+        compiler_options.GetNativeDebuggable(),
         code_address,
         code_address + code_allocator.GetSize(),
         &compiled_method
     };
-    ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForMethod(method_debug_info);
+    ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForMethod(
+        GetCompilerDriver()->GetInstructionSet(),
+        GetCompilerDriver()->GetInstructionSetFeatures(),
+        method_debug_info);
     CreateJITCodeEntryForAddress(code_address,
                                  std::unique_ptr<const uint8_t[]>(elf_file.data()),
                                  elf_file.size());
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ea19059..44e7fc9 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1034,9 +1034,11 @@
     key_value_store_->Put(
         OatHeader::kDebuggableKey,
         compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
-    key_value_store_->Put(
-        OatHeader::kExtractOnlyKey,
-        compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+    if (compiler_options_->IsExtractOnly()) {
+      key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue);
+    } else if (UseProfileGuidedCompilation()) {
+      key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kProfileGuideCompiledValue);
+    }
   }
 
   // Parse the arguments from the command line. In case of an unrecognized option or impossible
@@ -1891,13 +1893,6 @@
     return success;
   }
 
-  bool ShouldCompileBasedOnProfiles() const {
-    DCHECK(UseProfileGuidedCompilation());
-    // If we are given a profile, compile only if we have some data in it.
-    return (profile_compilation_info_ != nullptr) &&
-        (profile_compilation_info_->GetNumberOfMethods() != 0);
-  }
-
  private:
   template <typename T>
   static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
@@ -2105,8 +2100,10 @@
     elf_writers_.reserve(oat_files_.size());
     oat_writers_.reserve(oat_files_.size());
     for (const std::unique_ptr<File>& oat_file : oat_files_) {
-      elf_writers_.emplace_back(
-          CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get()));
+      elf_writers_.emplace_back(CreateElfWriterQuick(instruction_set_,
+                                                     instruction_set_features_.get(),
+                                                     compiler_options_.get(),
+                                                     oat_file.get()));
       elf_writers_.back()->Start();
       oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_));
     }
@@ -2593,16 +2590,11 @@
   // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
   dex2oat->ParseArgs(argc, argv);
 
-  // Process profile information and assess if we need to do a profile guided compilation.
+  // If needed, process profile information for profile guided compilation.
   // This operation involves I/O.
   if (dex2oat->UseProfileGuidedCompilation()) {
-    if (dex2oat->LoadProfile()) {
-      if (!dex2oat->ShouldCompileBasedOnProfiles()) {
-        LOG(INFO) << "Skipped compilation because of insignificant profile delta";
-        return EXIT_SUCCESS;
-      }
-    } else {
-      LOG(WARNING) << "Failed to process profile files";
+    if (!dex2oat->LoadProfile()) {
+      LOG(ERROR) << "Failed to process profile file";
       return EXIT_FAILURE;
     }
   }
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index c187536..6c6c807 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -98,6 +98,7 @@
   return ret;
 }
 
+template <typename ElfTypes>
 class OatSymbolizer FINAL {
  public:
   OatSymbolizer(const OatFile* oat_file, const std::string& output_name) :
@@ -115,11 +116,13 @@
 
   bool Symbolize() {
     const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet();
+    const InstructionSetFeatures* features = InstructionSetFeatures::FromBitmap(
+        isa, oat_file_->GetOatHeader().GetInstructionSetFeaturesBitmap());
 
     File* elf_file = OS::CreateEmptyFile(output_name_.c_str());
     std::unique_ptr<BufferedOutputStream> output_stream(
         MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file)));
-    builder_.reset(new ElfBuilder<ElfTypes32>(isa, output_stream.get()));
+    builder_.reset(new ElfBuilder<ElfTypes>(isa, features, output_stream.get()));
 
     builder_->Start();
 
@@ -149,14 +152,14 @@
         elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize());
     builder_->WriteDynamicSection();
 
-    Walk(&art::OatSymbolizer::RegisterForDedup);
+    Walk(&art::OatSymbolizer<ElfTypes>::RegisterForDedup);
 
     NormalizeState();
 
     strtab->Start();
     strtab->Write("");  // strtab should start with empty string.
     AddTrampolineSymbols();
-    Walk(&art::OatSymbolizer::AddSymbol);
+    Walk(&art::OatSymbolizer<ElfTypes>::AddSymbol);
     strtab->End();
 
     symtab->Start();
@@ -344,7 +347,7 @@
   }
 
   const OatFile* oat_file_;
-  std::unique_ptr<ElfBuilder<ElfTypes32> > builder_;
+  std::unique_ptr<ElfBuilder<ElfTypes> > builder_;
   std::unordered_map<uint32_t, uint32_t> state_;
   const std::string output_name_;
 };
@@ -2542,8 +2545,17 @@
     return EXIT_FAILURE;
   }
 
-  OatSymbolizer oat_symbolizer(oat_file, output_name);
-  if (!oat_symbolizer.Symbolize()) {
+  bool result;
+  // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF
+  // files for 64-bit code in the past.
+  if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) {
+    OatSymbolizer<ElfTypes64> oat_symbolizer(oat_file, output_name);
+    result = oat_symbolizer.Symbolize();
+  } else {
+    OatSymbolizer<ElfTypes32> oat_symbolizer(oat_file, output_name);
+    result = oat_symbolizer.Symbolize();
+  }
+  if (!result) {
     fprintf(stderr, "Failed to symbolize\n");
     return EXIT_FAILURE;
   }
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 12d6d8f..ebe89bb 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -463,6 +463,12 @@
       interface_method->VisitRoots(visitor, pointer_size);
     }
     visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
+    if (!IsNative()) {
+      ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
+      if (profiling_info != nullptr) {
+        profiling_info->VisitRoots(visitor);
+      }
+    }
   }
 }
 
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index a60f31e..f97ad51 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -377,7 +377,7 @@
 
   Runtime* runtime = Runtime::Current();
   const void* existing_entry_point = GetEntryPointFromQuickCompiledCode();
-  DCHECK(existing_entry_point != nullptr);
+  CHECK(existing_entry_point != nullptr) << PrettyMethod(this) << "@" << this;
   ClassLinker* class_linker = runtime->GetClassLinker();
 
   if (class_linker->IsQuickGenericJniStub(existing_entry_point)) {
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 4672948..e4097dd 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -234,21 +234,34 @@
   return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
 }
 
-bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
+template <bool kUseOffset>
+bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) {
   DCHECK(!read_only_mode_);
-  const char* ptr = static_cast<const char*>(buffer);
   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
+  DCHECK(kUseOffset || offset == 0u);
+  const char* ptr = static_cast<const char*>(buffer);
   while (byte_count > 0) {
-    ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
+    ssize_t bytes_written = kUseOffset
+        ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset))
+        : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
     if (bytes_written == -1) {
       return false;
     }
     byte_count -= bytes_written;  // Reduce the number of remaining bytes.
     ptr += bytes_written;  // Move the buffer forward.
+    offset += static_cast<size_t>(bytes_written);
   }
   return true;
 }
 
+bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
+  return WriteFullyGeneric<true>(buffer, byte_count, offset);
+}
+
+bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
+  return WriteFullyGeneric<false>(buffer, byte_count, 0u);
+}
+
 bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
   DCHECK(!read_only_mode_);
   off_t off = static_cast<off_t>(offset);
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 8040afe..16cd44f 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -79,6 +79,7 @@
   bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
   bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
   bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
+  bool PwriteFully(const void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
 
   // Copy data from another file.
   bool Copy(FdFile* input_file, int64_t offset, int64_t size);
@@ -119,6 +120,9 @@
   GuardState guard_state_;
 
  private:
+  template <bool kUseOffset>
+  bool WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset);
+
   int fd_;
   std::string file_path_;
   bool auto_close_;
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index ecf607c..9bc87e5 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -110,6 +110,34 @@
   ASSERT_EQ(file.Close(), 0);
 }
 
+TEST_F(FdFileTest, ReadWriteFullyWithOffset) {
+  // New scratch file, zero-length.
+  art::ScratchFile tmp;
+  FdFile file;
+  ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDWR));
+  EXPECT_GE(file.Fd(), 0);
+  EXPECT_TRUE(file.IsOpened());
+
+  const char* test_string = "This is a test string";
+  size_t length = strlen(test_string) + 1;
+  const size_t offset = 12;
+  std::unique_ptr<char[]> offset_read_string(new char[length]);
+  std::unique_ptr<char[]> read_string(new char[length]);
+
+  // Write scratch data to file that we can read back into.
+  EXPECT_TRUE(file.PwriteFully(test_string, length, offset));
+  ASSERT_EQ(file.Flush(), 0);
+
+  // Test reading both the offsets.
+  EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset));
+  EXPECT_STREQ(test_string, &offset_read_string[0]);
+
+  EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u));
+  EXPECT_NE(memcmp(&read_string[0], test_string, length), 0);
+
+  ASSERT_EQ(file.Close(), 0);
+}
+
 TEST_F(FdFileTest, Copy) {
   art::ScratchFile src_tmp;
   FdFile src;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index a13a2e3..d51a1f7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1207,217 +1207,222 @@
   Thread* const self = Thread::Current();
   gc::Heap* const heap = Runtime::Current()->GetHeap();
   const ImageHeader& header = space->GetImageHeader();
-  // Add image classes into the class table for the class loader, and fixup the dex caches and
-  // class loader fields.
-  WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
-  ClassTable* table = InsertClassTableForClassLoader(class_loader.Get());
-  // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
-  // rely on clobering the dex cache arrays in the image to forward to bss.
-  size_t num_dex_caches_with_bss_arrays = 0;
-  const size_t num_dex_caches = dex_caches->GetLength();
-  for (size_t i = 0; i < num_dex_caches; i++) {
-    mirror::DexCache* const dex_cache = dex_caches->Get(i);
-    const DexFile* const dex_file = dex_cache->GetDexFile();
-    const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-    if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-      ++num_dex_caches_with_bss_arrays;
-    }
-  }
-  *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
-  if (*out_forward_dex_cache_array) {
-    if (num_dex_caches_with_bss_arrays != num_dex_caches) {
-      // Reject application image since we cannot forward only some of the dex cache arrays.
-      // TODO: We could get around this by having a dedicated forwarding slot. It should be an
-      // uncommon case.
-      *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
-                                    num_dex_caches_with_bss_arrays,
-                                    num_dex_caches);
-      return false;
-    }
-  }
-  // Only add the classes to the class loader after the points where we can return false.
-  for (size_t i = 0; i < num_dex_caches; i++) {
-    mirror::DexCache* const dex_cache = dex_caches->Get(i);
-    const DexFile* const dex_file = dex_cache->GetDexFile();
-    const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-    if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-    // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
-      // copy over the arrays.
-      DCHECK(dex_file != nullptr);
-      const size_t num_strings = dex_file->NumStringIds();
-      const size_t num_types = dex_file->NumTypeIds();
-      const size_t num_methods = dex_file->NumMethodIds();
-      const size_t num_fields = dex_file->NumFieldIds();
-      CHECK_EQ(num_strings, dex_cache->NumStrings());
-      CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
-      CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
-      CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
-      DexCacheArraysLayout layout(image_pointer_size_, dex_file);
-      uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
-      // The space is not yet visible to the GC, we can avoid the read barriers and use std::copy_n.
-      if (num_strings != 0u) {
-        GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings();
-        GcRoot<mirror::String>* const strings =
-            reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
-        for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) {
-          DCHECK(strings[j].IsNull());
-        }
-        std::copy_n(image_resolved_strings, num_strings, strings);
-        dex_cache->SetStrings(strings);
-      }
-      if (num_types != 0u) {
-        GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
-        GcRoot<mirror::Class>* const types =
-            reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
-        for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
-          DCHECK(types[j].IsNull());
-        }
-        std::copy_n(image_resolved_types, num_types, types);
-        // Store a pointer to the new location for fast ArtMethod patching without requiring map.
-        // This leaves random garbage at the start of the dex cache array, but nobody should ever
-        // read from it again.
-        *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types;
-        dex_cache->SetResolvedTypes(types);
-      }
-      if (num_methods != 0u) {
-        ArtMethod** const methods = reinterpret_cast<ArtMethod**>(
-            raw_arrays + layout.MethodsOffset());
-        ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods();
-        for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) {
-          DCHECK(methods[j] == nullptr);
-        }
-        std::copy_n(image_resolved_methods, num_methods, methods);
-        // Store a pointer to the new location for fast ArtMethod patching without requiring map.
-        *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods;
-        dex_cache->SetResolvedMethods(methods);
-      }
-      if (num_fields != 0u) {
-        ArtField** const fields = reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
-        for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) {
-          DCHECK(fields[j] == nullptr);
-        }
-        std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
-        dex_cache->SetResolvedFields(fields);
+  {
+    // Add image classes into the class table for the class loader, and fixup the dex caches and
+    // class loader fields.
+    WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+    ClassTable* table = InsertClassTableForClassLoader(class_loader.Get());
+    // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
+    // rely on clobering the dex cache arrays in the image to forward to bss.
+    size_t num_dex_caches_with_bss_arrays = 0;
+    const size_t num_dex_caches = dex_caches->GetLength();
+    for (size_t i = 0; i < num_dex_caches; i++) {
+      mirror::DexCache* const dex_cache = dex_caches->Get(i);
+      const DexFile* const dex_file = dex_cache->GetDexFile();
+      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
+      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
+        ++num_dex_caches_with_bss_arrays;
       }
     }
-    {
-      WriterMutexLock mu2(self, dex_lock_);
-      // Make sure to do this after we update the arrays since we store the resolved types array
-      // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
-      // BSS.
-      mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self,
-                                                                *dex_file,
-                                                                /*allow_failure*/true);
-      CHECK(existing_dex_cache == nullptr);
-      StackHandleScope<1> hs3(self);
-      RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache));
+    *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
+    if (*out_forward_dex_cache_array) {
+      if (num_dex_caches_with_bss_arrays != num_dex_caches) {
+        // Reject application image since we cannot forward only some of the dex cache arrays.
+        // TODO: We could get around this by having a dedicated forwarding slot. It should be an
+        // uncommon case.
+        *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
+                                      num_dex_caches_with_bss_arrays,
+                                      num_dex_caches);
+        return false;
+      }
     }
-    GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
-    const size_t num_types = dex_cache->NumResolvedTypes();
-    if (new_class_set == nullptr) {
-      for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
-        // The image space is not yet added to the heap, avoid read barriers.
-        mirror::Class* klass = types[j].Read();
-        if (klass != nullptr) {
-          DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
-          // Update the class loader from the one in the image class loader to the one that loaded
-          // the app image.
-          klass->SetClassLoader(class_loader.Get());
-          // The resolved type could be from another dex cache, go through the dex cache just in
-          // case. May be null for array classes.
-          if (klass->GetDexCacheStrings() != nullptr) {
-            DCHECK(!klass->IsArrayClass());
-            klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
+    // Only add the classes to the class loader after the points where we can return false.
+    for (size_t i = 0; i < num_dex_caches; i++) {
+      mirror::DexCache* const dex_cache = dex_caches->Get(i);
+      const DexFile* const dex_file = dex_cache->GetDexFile();
+      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
+      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
+      // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
+        // copy over the arrays.
+        DCHECK(dex_file != nullptr);
+        const size_t num_strings = dex_file->NumStringIds();
+        const size_t num_types = dex_file->NumTypeIds();
+        const size_t num_methods = dex_file->NumMethodIds();
+        const size_t num_fields = dex_file->NumFieldIds();
+        CHECK_EQ(num_strings, dex_cache->NumStrings());
+        CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
+        CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
+        CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
+        DexCacheArraysLayout layout(image_pointer_size_, dex_file);
+        uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
+        // The space is not yet visible to the GC, we can avoid the read barriers and use
+        // std::copy_n.
+        if (num_strings != 0u) {
+          GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings();
+          GcRoot<mirror::String>* const strings =
+              reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
+          for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) {
+            DCHECK(strings[j].IsNull());
           }
-          // If there are multiple dex caches, there may be the same class multiple times
-          // in different dex caches. Check for this since inserting will add duplicates
-          // otherwise.
-          if (num_dex_caches > 1) {
-            mirror::Class* existing = table->LookupByDescriptor(klass);
-            if (existing != nullptr) {
-              DCHECK_EQ(existing, klass) << PrettyClass(klass);
+          std::copy_n(image_resolved_strings, num_strings, strings);
+          dex_cache->SetStrings(strings);
+        }
+        if (num_types != 0u) {
+          GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
+          GcRoot<mirror::Class>* const types =
+              reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
+          for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
+            DCHECK(types[j].IsNull());
+          }
+          std::copy_n(image_resolved_types, num_types, types);
+          // Store a pointer to the new location for fast ArtMethod patching without requiring map.
+          // This leaves random garbage at the start of the dex cache array, but nobody should ever
+          // read from it again.
+          *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types;
+          dex_cache->SetResolvedTypes(types);
+        }
+        if (num_methods != 0u) {
+          ArtMethod** const methods = reinterpret_cast<ArtMethod**>(
+              raw_arrays + layout.MethodsOffset());
+          ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods();
+          for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) {
+            DCHECK(methods[j] == nullptr);
+          }
+          std::copy_n(image_resolved_methods, num_methods, methods);
+          // Store a pointer to the new location for fast ArtMethod patching without requiring map.
+          *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods;
+          dex_cache->SetResolvedMethods(methods);
+        }
+        if (num_fields != 0u) {
+          ArtField** const fields =
+              reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
+          for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) {
+            DCHECK(fields[j] == nullptr);
+          }
+          std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
+          dex_cache->SetResolvedFields(fields);
+        }
+      }
+      {
+        WriterMutexLock mu2(self, dex_lock_);
+        // Make sure to do this after we update the arrays since we store the resolved types array
+        // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
+        // BSS.
+        mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self,
+                                                                  *dex_file,
+                                                                  /*allow_failure*/true);
+        CHECK(existing_dex_cache == nullptr);
+        StackHandleScope<1> hs3(self);
+        RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache));
+      }
+      GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
+      const size_t num_types = dex_cache->NumResolvedTypes();
+      if (new_class_set == nullptr) {
+        for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
+          // The image space is not yet added to the heap, avoid read barriers.
+          mirror::Class* klass = types[j].Read();
+          if (klass != nullptr) {
+            DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+            // Update the class loader from the one in the image class loader to the one that loaded
+            // the app image.
+            klass->SetClassLoader(class_loader.Get());
+            // The resolved type could be from another dex cache, go through the dex cache just in
+            // case. May be null for array classes.
+            if (klass->GetDexCacheStrings() != nullptr) {
+              DCHECK(!klass->IsArrayClass());
+              klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
+            }
+            // If there are multiple dex caches, there may be the same class multiple times
+            // in different dex caches. Check for this since inserting will add duplicates
+            // otherwise.
+            if (num_dex_caches > 1) {
+              mirror::Class* existing = table->LookupByDescriptor(klass);
+              if (existing != nullptr) {
+                DCHECK_EQ(existing, klass) << PrettyClass(klass);
+              } else {
+                table->Insert(klass);
+              }
             } else {
               table->Insert(klass);
             }
-          } else {
-            table->Insert(klass);
-          }
-          // Double checked VLOG to avoid overhead.
-          if (VLOG_IS_ON(image)) {
-            VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus();
-            if (!klass->IsArrayClass()) {
-              VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation();
-            }
-            VLOG(image) << "Direct methods";
-            for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
-              VLOG(image) << PrettyMethod(&m);
-            }
-            VLOG(image) << "Virtual methods";
-            for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
-              VLOG(image) << PrettyMethod(&m);
+            // Double checked VLOG to avoid overhead.
+            if (VLOG_IS_ON(image)) {
+              VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus();
+              if (!klass->IsArrayClass()) {
+                VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation();
+              }
+              VLOG(image) << "Direct methods";
+              for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
+                VLOG(image) << PrettyMethod(&m);
+              }
+              VLOG(image) << "Virtual methods";
+              for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
+                VLOG(image) << PrettyMethod(&m);
+              }
             }
           }
         }
       }
-    }
-    if (kIsDebugBuild) {
-      for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
-        // The image space is not yet added to the heap, avoid read barriers.
-        mirror::Class* klass = types[j].Read();
-        if (klass != nullptr) {
-          DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
-          if (kIsDebugBuild) {
-            if (new_class_set != nullptr)   {
-              auto it = new_class_set->Find(GcRoot<mirror::Class>(klass));
-              DCHECK(it != new_class_set->end());
-              DCHECK_EQ(it->Read(), klass);
-              mirror::Class* super_class = klass->GetSuperClass();
-              if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
-                auto it2 = new_class_set->Find(GcRoot<mirror::Class>(super_class));
-                DCHECK(it2 != new_class_set->end());
-                DCHECK_EQ(it2->Read(), super_class);
-              }
-            } else {
-              DCHECK_EQ(table->LookupByDescriptor(klass), klass);
-              mirror::Class* super_class = klass->GetSuperClass();
-              if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
-                CHECK_EQ(table->LookupByDescriptor(super_class), super_class);
+      if (kIsDebugBuild) {
+        for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
+          // The image space is not yet added to the heap, avoid read barriers.
+          mirror::Class* klass = types[j].Read();
+          if (klass != nullptr) {
+            DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+            if (kIsDebugBuild) {
+              if (new_class_set != nullptr)   {
+                auto it = new_class_set->Find(GcRoot<mirror::Class>(klass));
+                DCHECK(it != new_class_set->end());
+                DCHECK_EQ(it->Read(), klass);
+                mirror::Class* super_class = klass->GetSuperClass();
+                if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
+                  auto it2 = new_class_set->Find(GcRoot<mirror::Class>(super_class));
+                  DCHECK(it2 != new_class_set->end());
+                  DCHECK_EQ(it2->Read(), super_class);
+                }
+              } else {
+                DCHECK_EQ(table->LookupByDescriptor(klass), klass);
+                mirror::Class* super_class = klass->GetSuperClass();
+                if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
+                  CHECK_EQ(table->LookupByDescriptor(super_class), super_class);
+                }
               }
             }
-          }
-          if (kIsDebugBuild) {
-            for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
-              const void* code = m.GetEntryPointFromQuickCompiledCode();
-              const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
-              if (!IsQuickResolutionStub(code) &&
-                  !IsQuickGenericJniStub(code) &&
-                  !IsQuickToInterpreterBridge(code) &&
-                  !m.IsNative()) {
-                DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+            if (kIsDebugBuild) {
+              for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
+                const void* code = m.GetEntryPointFromQuickCompiledCode();
+                const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
+                if (!IsQuickResolutionStub(code) &&
+                    !IsQuickGenericJniStub(code) &&
+                    !IsQuickToInterpreterBridge(code) &&
+                    !m.IsNative()) {
+                  DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+                }
               }
-            }
-            for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
-              const void* code = m.GetEntryPointFromQuickCompiledCode();
-              const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
-              if (!IsQuickResolutionStub(code) &&
-                  !IsQuickGenericJniStub(code) &&
-                  !IsQuickToInterpreterBridge(code) &&
-                  !m.IsNative()) {
-                DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+              for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
+                const void* code = m.GetEntryPointFromQuickCompiledCode();
+                const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
+                if (!IsQuickResolutionStub(code) &&
+                    !IsQuickGenericJniStub(code) &&
+                    !IsQuickToInterpreterBridge(code) &&
+                    !m.IsNative()) {
+                  DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+                }
               }
             }
           }
         }
       }
     }
-  }
-  if (*out_forward_dex_cache_array) {
-    FixupArtMethodArrayVisitor visitor(header);
-    header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
-        &visitor,
-        space->Begin(),
-        sizeof(void*));
-    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
+    if (*out_forward_dex_cache_array) {
+      ScopedTrace timing("Fixup ArtMethod dex cache arrays");
+      FixupArtMethodArrayVisitor visitor(header);
+      header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
+          &visitor,
+          space->Begin(),
+          sizeof(void*));
+      Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
+    }
   }
   return true;
 }
@@ -7709,7 +7714,10 @@
       }
       ++num_resolved;
       DCHECK(!klass->IsProxyClass());
-      DCHECK(klass->IsResolved());
+      if (!klass->IsResolved()) {
+        DCHECK(klass->IsErroneous());
+        continue;
+      }
       mirror::DexCache* klass_dex_cache = klass->GetDexCache();
       if (klass_dex_cache == dex_cache) {
         const size_t class_def_idx = klass->GetDexClassDefIndex();
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 3df9101..729957f 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -406,6 +406,7 @@
   int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
   ASSERT_EQ(0, rmdir_cache_result);
   TearDownAndroidData(android_data_, true);
+  dalvik_cache_.clear();
 
   // icu4c has a fixed 10-element array "gCommonICUDataArray".
   // If we run > 10 tests, we fill that array and u_setCommonData fails.
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 7727b2d..6beb606 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -131,7 +131,7 @@
 
 class BitmapSetSlowPathVisitor {
  public:
-  void operator()(const mirror::Object* obj) const {
+  void operator()(const mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
     // Marking a large object, make sure its aligned as a sanity check.
     if (!IsAligned<kPageSize>(obj)) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 01dff19..2e5b599 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1547,7 +1547,6 @@
 }
 
 void Heap::DumpSpaces(std::ostream& stream) const {
-  ScopedObjectAccess soa(Thread::Current());
   for (const auto& space : continuous_spaces_) {
     accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
     accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap();
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 889069d..e0a53a0 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -651,8 +651,8 @@
     }
   }
 
-  std::string DumpSpaces() const WARN_UNUSED;
-  void DumpSpaces(std::ostream& stream) const;
+  void DumpSpaces(std::ostream& stream) const SHARED_REQUIRES(Locks::mutator_lock_);
+  std::string DumpSpaces() const SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Dump object should only be used by the signal handler.
   void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index a4e5587..9ecd391 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1535,50 +1535,31 @@
   //              images[0] is          f/c/d/e.art
   // ----------------------------------------------
   //              images[1] is          g/h/i/j.art  -> /a/b/h/i/j.art
-
-  // Derive pattern.
-  std::vector<std::string> left;
-  Split(input_image_file_name, '/', &left);
-  std::vector<std::string> right;
-  Split(images[0], '/', &right);
-
-  size_t common = 1;
-  while (common < left.size() && common < right.size()) {
-    if (left[left.size() - common - 1] != right[right.size() - common - 1]) {
-      break;
-    }
-    common++;
+  const std::string& first_image = images[0];
+  // Length of common suffix.
+  size_t common = 0;
+  while (common < input_image_file_name.size() &&
+         common < first_image.size() &&
+         *(input_image_file_name.end() - common - 1) == *(first_image.end() - common - 1)) {
+    ++common;
   }
-
-  std::vector<std::string> prefix_vector(left.begin(), left.end() - common);
-  std::string common_prefix = Join(prefix_vector, '/');
-  if (!common_prefix.empty() && common_prefix[0] != '/' && input_image_file_name[0] == '/') {
-    common_prefix = "/" + common_prefix;
-  }
+  // We want to replace the prefix of the input image with the prefix of the boot class path.
+  // This handles the case where the image file contains @ separators.
+  // Example image_file_name is oats/system@framework@boot.art
+  // images[0] is .../arm/boot.art
+  // means that the image name prefix will be oats/system@framework@
+  // so that the other images are openable.
+  const size_t old_prefix_length = first_image.size() - common;
+  const std::string new_prefix = input_image_file_name.substr(
+      0,
+      input_image_file_name.size() - common);
 
   // Apply pattern to images[1] .. images[n].
   for (size_t i = 1; i < images.size(); ++i) {
-    std::string image = images[i];
-
-    size_t rslash = std::string::npos;
-    for (size_t j = 0; j < common; ++j) {
-      if (rslash != std::string::npos) {
-        rslash--;
-      }
-
-      rslash = image.rfind('/', rslash);
-      if (rslash == std::string::npos) {
-        rslash = 0;
-      }
-      if (rslash == 0) {
-        break;
-      }
-    }
-    std::string image_part = image.substr(rslash);
-
-    std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") +
-        image_part;
-    image_file_names->push_back(new_image);
+    const std::string& image = images[i];
+    CHECK_GT(image.length(), old_prefix_length);
+    std::string suffix = image.substr(old_prefix_length);
+    image_file_names->push_back(new_prefix + suffix);
   }
 }
 
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index e70fe21..010f677 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -27,6 +27,7 @@
 #include "base/stl_util.h"
 #include "image.h"
 #include "os.h"
+#include "scoped_thread_state_change.h"
 #include "space-inl.h"
 #include "thread-inl.h"
 
@@ -190,6 +191,7 @@
   MutexLock mu(self, lock_);
   auto it = large_objects_.find(ptr);
   if (UNLIKELY(it == large_objects_.end())) {
+    ScopedObjectAccess soa(self);
     Runtime::Current()->GetHeap()->DumpSpaces(LOG(INTERNAL_FATAL));
     LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live";
   }
diff --git a/runtime/interpreter/mterp/mips/binop.S b/runtime/interpreter/mterp/mips/binop.S
index ce09da45..66627e2 100644
--- a/runtime/interpreter/mterp/mips/binop.S
+++ b/runtime/interpreter/mterp/mips/binop.S
@@ -7,8 +7,8 @@
      *
      * If "chkzero" is set to 1, we perform a divide-by-zero check on
      * vCC (a1).  Useful for integer division and modulus.  Note that we
-     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
-     * handles it correctly.
+     * *don't* check for (INT_MIN / -1) here, because the CPU handles it
+     * correctly.
      *
      * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
      *      xor-int, shl-int, shr-int, ushr-int
diff --git a/runtime/interpreter/mterp/mips64/bincmp.S b/runtime/interpreter/mterp/mips64/bincmp.S
index d39c900..aa5e74b 100644
--- a/runtime/interpreter/mterp/mips64/bincmp.S
+++ b/runtime/interpreter/mterp/mips64/bincmp.S
@@ -6,27 +6,27 @@
      * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
      */
     /* if-cmp vA, vB, +CCCC */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended CCCC
+    .extern MterpProfileBranch
     ext     a2, rINST, 8, 4             # a2 <- A
     ext     a3, rINST, 12, 4            # a3 <- B
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended CCCC)
     GET_VREG a0, a2                     # a0 <- vA
     GET_VREG a1, a3                     # a1 <- vB
-
     b${condition}c a0, a1, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + CCCC * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # CCCC * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S
index 1a2e22b..14d5fe0 100644
--- a/runtime/interpreter/mterp/mips64/footer.S
+++ b/runtime/interpreter/mterp/mips64/footer.S
@@ -49,6 +49,7 @@
  *
  */
     .extern MterpHandleException
+    .extern MterpShouldSwitchInterpreters
 MterpException:
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -59,8 +60,11 @@
     REFRESH_IBASE
     daddu   rPC, a0, CODEITEM_INSNS_OFFSET
     dlsa    rPC, a1, rPC, 1                         # generate new dex_pc_ptr
-    sd      rPC, OFF_FP_DEX_PC_PTR(rFP)
+    /* Do we need to switch interpreters? */
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     /* resume execution at catch block */
+    EXPORT_PC
     FETCH_INST
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
@@ -81,10 +85,24 @@
     EXPORT_PC
     move    a0, rSELF
     jal     MterpSuspendCheck                       # (self)
+    bnezc   v0, MterpFallback                       # Something in the environment changed, switch interpreters
     GET_INST_OPCODE v0                              # extract opcode from rINST
     GOTO_OPCODE v0                                  # jump to next instruction
 
 /*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST                               # rINST contains offset
+    jal     MterpLogOSR
+#endif
+    li      v0, 1                                   # Signal normal return
+    b       MterpDone
+
+/*
  * Bail out to reference interpreter.
  */
     .extern MterpLogFallback
diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S
index 4c3ca9e..dd0fbe0 100644
--- a/runtime/interpreter/mterp/mips64/header.S
+++ b/runtime/interpreter/mterp/mips64/header.S
@@ -82,14 +82,7 @@
 #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
 #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
 
-/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
+#define MTERP_PROFILE_BRANCHES 1
 #define MTERP_LOGGING 0
 
 /*
diff --git a/runtime/interpreter/mterp/mips64/invoke.S b/runtime/interpreter/mterp/mips64/invoke.S
index 4ae4fb1..be647b6 100644
--- a/runtime/interpreter/mterp/mips64/invoke.S
+++ b/runtime/interpreter/mterp/mips64/invoke.S
@@ -5,6 +5,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern $helper
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -13,5 +14,7 @@
     jal     $helper
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
diff --git a/runtime/interpreter/mterp/mips64/op_goto.S b/runtime/interpreter/mterp/mips64/op_goto.S
index f2df3e4..7c7d0ec 100644
--- a/runtime/interpreter/mterp/mips64/op_goto.S
+++ b/runtime/interpreter/mterp/mips64/op_goto.S
@@ -5,19 +5,21 @@
      * double to get a byte offset.
      */
     /* goto +AA */
-    srl     a0, rINST, 8
-    seb     a0, a0                      # a0 <- sign-extended AA
-    dlsa    rPC, a0, rPC, 1             # rPC <- rPC + AA * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a0, 1f                      # AA * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a0, MterpCheckSuspendAndContinue
+    .extern MterpProfileBranch
+    srl     rINST, rINST, 8
+    seb     rINST, rINST                # rINST <- offset (sign-extended AA)
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/op_goto_16.S b/runtime/interpreter/mterp/mips64/op_goto_16.S
index cbf8cf2..566e3a7 100644
--- a/runtime/interpreter/mterp/mips64/op_goto_16.S
+++ b/runtime/interpreter/mterp/mips64/op_goto_16.S
@@ -5,18 +5,20 @@
      * double to get a byte offset.
      */
     /* goto/16 +AAAA */
-    lh      a0, 2(rPC)                  # a0 <- sign-extended AAAA
-    dlsa    rPC, a0, rPC, 1             # rPC <- rPC + AAAA * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a0, 1f                      # AA * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a0, MterpCheckSuspendAndContinue
+    .extern MterpProfileBranch
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended AAAA)
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/op_goto_32.S b/runtime/interpreter/mterp/mips64/op_goto_32.S
index 4a1feac..b260083 100644
--- a/runtime/interpreter/mterp/mips64/op_goto_32.S
+++ b/runtime/interpreter/mterp/mips64/op_goto_32.S
@@ -8,20 +8,22 @@
      * our "backward branch" test must be "<=0" instead of "<0".
      */
     /* goto/32 +AAAAAAAA */
-    lh      a0, 2(rPC)                  # a0 <- aaaa (low)
+    .extern MterpProfileBranch
+    lh      rINST, 2(rPC)               # rINST <- aaaa (low)
     lh      a1, 4(rPC)                  # a1 <- AAAA (high)
-    ins     a0, a1, 16, 16              # a0 = sign-extended AAAAaaaa
-    dlsa    rPC, a0, rPC, 1             # rPC <- rPC + AAAAAAAA * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgtz    a0, 1f                      # AA * 2 > 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    blez    a0, MterpCheckSuspendAndContinue
+    ins     rINST, a1, 16, 16           # rINST <- offset (sign-extended AAAAaaaa)
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    blez    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/op_packed_switch.S b/runtime/interpreter/mterp/mips64/op_packed_switch.S
index cdbdf75..2c6eb2f 100644
--- a/runtime/interpreter/mterp/mips64/op_packed_switch.S
+++ b/runtime/interpreter/mterp/mips64/op_packed_switch.S
@@ -10,6 +10,7 @@
      */
     /* op vAA, +BBBBBBBB */
     .extern $func
+    .extern MterpProfileBranch
     lh      a0, 2(rPC)                  # a0 <- bbbb (lo)
     lh      a1, 4(rPC)                  # a1 <- BBBB (hi)
     srl     a3, rINST, 8                # a3 <- AA
@@ -17,15 +18,19 @@
     GET_VREG a1, a3                     # a1 <- vAA
     dlsa    a0, a0, rPC, 1              # a0 <- PC + BBBBbbbb*2
     jal     $func                       # v0 <- code-unit branch offset
-    dlsa    rPC, v0, rPC, 1             # rPC <- rPC + offset * 2
-    FETCH_INST                          # load rINST
-#if MTERP_SUSPEND
-    bgtz    v0, 1f                      # offset * 2 > 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    blez    v0, MterpCheckSuspendAndContinue
+    move    rINST, v0
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    blez    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/zcmp.S b/runtime/interpreter/mterp/mips64/zcmp.S
index d7ad894..0e0477f 100644
--- a/runtime/interpreter/mterp/mips64/zcmp.S
+++ b/runtime/interpreter/mterp/mips64/zcmp.S
@@ -6,25 +6,25 @@
      * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
      */
     /* if-cmp vAA, +BBBB */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended BBBB
+    .extern MterpProfileBranch
     srl     a2, rINST, 8                # a2 <- AA
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended BBBB)
     GET_VREG a0, a2                     # a0 <- vAA
-
     b${condition}zc a0, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + BBBB * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # BBBB * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index ca727f4..10b19c5 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -147,16 +147,7 @@
     SHARED_REQUIRES(Locks::mutator_lock_) {
   const instrumentation::Instrumentation* const instrumentation =
       Runtime::Current()->GetInstrumentation();
-  bool unhandled_instrumentation;
-  // TODO: enable for other targets after more extensive testing.
-  if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) ||
-      (kRuntimeISA == kX86_64) || (kRuntimeISA == kX86) ||
-      (kRuntimeISA == kMips)) {
-    unhandled_instrumentation = instrumentation->NonJitProfilingActive();
-  } else {
-    unhandled_instrumentation = instrumentation->IsActive();
-  }
-  return unhandled_instrumentation || Dbg::IsDebuggerActive();
+  return instrumentation->NonJitProfilingActive() || Dbg::IsDebuggerActive();
 }
 
 
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 7cef823..a17252b 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -89,14 +89,7 @@
 #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
 #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
 
-/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
+#define MTERP_PROFILE_BRANCHES 1
 #define MTERP_LOGGING 0
 
 /*
@@ -1107,20 +1100,22 @@
      * double to get a byte offset.
      */
     /* goto +AA */
-    srl     a0, rINST, 8
-    seb     a0, a0                      # a0 <- sign-extended AA
-    dlsa    rPC, a0, rPC, 1             # rPC <- rPC + AA * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a0, 1f                      # AA * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a0, MterpCheckSuspendAndContinue
+    .extern MterpProfileBranch
+    srl     rINST, rINST, 8
+    seb     rINST, rINST                # rINST <- offset (sign-extended AA)
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1135,19 +1130,21 @@
      * double to get a byte offset.
      */
     /* goto/16 +AAAA */
-    lh      a0, 2(rPC)                  # a0 <- sign-extended AAAA
-    dlsa    rPC, a0, rPC, 1             # rPC <- rPC + AAAA * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a0, 1f                      # AA * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a0, MterpCheckSuspendAndContinue
+    .extern MterpProfileBranch
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended AAAA)
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1165,21 +1162,23 @@
      * our "backward branch" test must be "<=0" instead of "<0".
      */
     /* goto/32 +AAAAAAAA */
-    lh      a0, 2(rPC)                  # a0 <- aaaa (low)
+    .extern MterpProfileBranch
+    lh      rINST, 2(rPC)               # rINST <- aaaa (low)
     lh      a1, 4(rPC)                  # a1 <- AAAA (high)
-    ins     a0, a1, 16, 16              # a0 = sign-extended AAAAaaaa
-    dlsa    rPC, a0, rPC, 1             # rPC <- rPC + AAAAAAAA * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgtz    a0, 1f                      # AA * 2 > 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    blez    a0, MterpCheckSuspendAndContinue
+    ins     rINST, a1, 16, 16           # rINST <- offset (sign-extended AAAAaaaa)
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    blez    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1198,6 +1197,7 @@
      */
     /* op vAA, +BBBBBBBB */
     .extern MterpDoPackedSwitch
+    .extern MterpProfileBranch
     lh      a0, 2(rPC)                  # a0 <- bbbb (lo)
     lh      a1, 4(rPC)                  # a1 <- BBBB (hi)
     srl     a3, rINST, 8                # a3 <- AA
@@ -1205,16 +1205,20 @@
     GET_VREG a1, a3                     # a1 <- vAA
     dlsa    a0, a0, rPC, 1              # a0 <- PC + BBBBbbbb*2
     jal     MterpDoPackedSwitch                       # v0 <- code-unit branch offset
-    dlsa    rPC, v0, rPC, 1             # rPC <- rPC + offset * 2
-    FETCH_INST                          # load rINST
-#if MTERP_SUSPEND
-    bgtz    v0, 1f                      # offset * 2 > 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    blez    v0, MterpCheckSuspendAndContinue
+    move    rINST, v0
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    blez    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1234,6 +1238,7 @@
      */
     /* op vAA, +BBBBBBBB */
     .extern MterpDoSparseSwitch
+    .extern MterpProfileBranch
     lh      a0, 2(rPC)                  # a0 <- bbbb (lo)
     lh      a1, 4(rPC)                  # a1 <- BBBB (hi)
     srl     a3, rINST, 8                # a3 <- AA
@@ -1241,16 +1246,20 @@
     GET_VREG a1, a3                     # a1 <- vAA
     dlsa    a0, a0, rPC, 1              # a0 <- PC + BBBBbbbb*2
     jal     MterpDoSparseSwitch                       # v0 <- code-unit branch offset
-    dlsa    rPC, v0, rPC, 1             # rPC <- rPC + offset * 2
-    FETCH_INST                          # load rINST
-#if MTERP_SUSPEND
-    bgtz    v0, 1f                      # offset * 2 > 0 => no suspend check
-    REFRESH_IBASE
-1:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    blez    v0, MterpCheckSuspendAndContinue
+    move    rINST, v0
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    blez    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1438,28 +1447,28 @@
      * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
      */
     /* if-cmp vA, vB, +CCCC */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended CCCC
+    .extern MterpProfileBranch
     ext     a2, rINST, 8, 4             # a2 <- A
     ext     a3, rINST, 12, 4            # a3 <- B
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended CCCC)
     GET_VREG a0, a2                     # a0 <- vA
     GET_VREG a1, a3                     # a1 <- vB
-
     beqc a0, a1, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + CCCC * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # CCCC * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1477,28 +1486,28 @@
      * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
      */
     /* if-cmp vA, vB, +CCCC */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended CCCC
+    .extern MterpProfileBranch
     ext     a2, rINST, 8, 4             # a2 <- A
     ext     a3, rINST, 12, 4            # a3 <- B
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended CCCC)
     GET_VREG a0, a2                     # a0 <- vA
     GET_VREG a1, a3                     # a1 <- vB
-
     bnec a0, a1, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + CCCC * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # CCCC * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1516,28 +1525,28 @@
      * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
      */
     /* if-cmp vA, vB, +CCCC */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended CCCC
+    .extern MterpProfileBranch
     ext     a2, rINST, 8, 4             # a2 <- A
     ext     a3, rINST, 12, 4            # a3 <- B
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended CCCC)
     GET_VREG a0, a2                     # a0 <- vA
     GET_VREG a1, a3                     # a1 <- vB
-
     bltc a0, a1, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + CCCC * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # CCCC * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1555,28 +1564,28 @@
      * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
      */
     /* if-cmp vA, vB, +CCCC */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended CCCC
+    .extern MterpProfileBranch
     ext     a2, rINST, 8, 4             # a2 <- A
     ext     a3, rINST, 12, 4            # a3 <- B
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended CCCC)
     GET_VREG a0, a2                     # a0 <- vA
     GET_VREG a1, a3                     # a1 <- vB
-
     bgec a0, a1, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + CCCC * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # CCCC * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1594,28 +1603,28 @@
      * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
      */
     /* if-cmp vA, vB, +CCCC */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended CCCC
+    .extern MterpProfileBranch
     ext     a2, rINST, 8, 4             # a2 <- A
     ext     a3, rINST, 12, 4            # a3 <- B
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended CCCC)
     GET_VREG a0, a2                     # a0 <- vA
     GET_VREG a1, a3                     # a1 <- vB
-
     bgtc a0, a1, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + CCCC * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # CCCC * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1633,28 +1642,28 @@
      * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
      */
     /* if-cmp vA, vB, +CCCC */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended CCCC
+    .extern MterpProfileBranch
     ext     a2, rINST, 8, 4             # a2 <- A
     ext     a3, rINST, 12, 4            # a3 <- B
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended CCCC)
     GET_VREG a0, a2                     # a0 <- vA
     GET_VREG a1, a3                     # a1 <- vB
-
     blec a0, a1, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + CCCC * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # CCCC * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1672,26 +1681,26 @@
      * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
      */
     /* if-cmp vAA, +BBBB */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended BBBB
+    .extern MterpProfileBranch
     srl     a2, rINST, 8                # a2 <- AA
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended BBBB)
     GET_VREG a0, a2                     # a0 <- vAA
-
     beqzc a0, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + BBBB * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # BBBB * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1709,26 +1718,26 @@
      * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
      */
     /* if-cmp vAA, +BBBB */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended BBBB
+    .extern MterpProfileBranch
     srl     a2, rINST, 8                # a2 <- AA
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended BBBB)
     GET_VREG a0, a2                     # a0 <- vAA
-
     bnezc a0, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + BBBB * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # BBBB * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1746,26 +1755,26 @@
      * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
      */
     /* if-cmp vAA, +BBBB */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended BBBB
+    .extern MterpProfileBranch
     srl     a2, rINST, 8                # a2 <- AA
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended BBBB)
     GET_VREG a0, a2                     # a0 <- vAA
-
     bltzc a0, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + BBBB * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # BBBB * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1783,26 +1792,26 @@
      * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
      */
     /* if-cmp vAA, +BBBB */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended BBBB
+    .extern MterpProfileBranch
     srl     a2, rINST, 8                # a2 <- AA
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended BBBB)
     GET_VREG a0, a2                     # a0 <- vAA
-
     bgezc a0, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + BBBB * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # BBBB * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1820,26 +1829,26 @@
      * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
      */
     /* if-cmp vAA, +BBBB */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended BBBB
+    .extern MterpProfileBranch
     srl     a2, rINST, 8                # a2 <- AA
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended BBBB)
     GET_VREG a0, a2                     # a0 <- vAA
-
     bgtzc a0, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + BBBB * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # BBBB * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -1857,26 +1866,26 @@
      * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
      */
     /* if-cmp vAA, +BBBB */
-    lh      a4, 2(rPC)                  # a4 <- sign-extended BBBB
+    .extern MterpProfileBranch
     srl     a2, rINST, 8                # a2 <- AA
+    lh      rINST, 2(rPC)               # rINST <- offset (sign-extended BBBB)
     GET_VREG a0, a2                     # a0 <- vAA
-
     blezc a0, 1f
-    li      a4, 2                       # offset if branch not taken
+    li      rINST, 2                    # offset if branch not taken
 1:
-
-    dlsa    rPC, a4, rPC, 1             # rPC <- rPC + BBBB * 2
-    FETCH_INST                          # load rINST
-
-#if MTERP_SUSPEND
-    bgez    a4, 2f                      # BBBB * 2 >= 0 => no suspend check
-    REFRESH_IBASE
-2:
-#else
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
-    bltz    a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+    EXPORT_PC
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST
+    jal     MterpProfileBranch          # (self, shadow_frame, offset)
+    bnezc   v0, MterpOnStackReplacement # Note: offset must be in rINST
 #endif
-
+    dlsa    rPC, rINST, rPC, 1          # rPC <- rPC + offset * 2
+    lw      ra, THREAD_FLAGS_OFFSET(rSELF)  # Preload flags for MterpCheckSuspendAndContinue
+    move    a0, rINST                   # a0 <- offset
+    FETCH_INST                          # load rINST
+    bltz    a0, MterpCheckSuspendAndContinue  # suspend check if backwards branch
     GET_INST_OPCODE v0                  # extract opcode from rINST
     GOTO_OPCODE v0                      # jump to next instruction
 
@@ -3166,6 +3175,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeVirtual
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3174,6 +3184,8 @@
     jal     MterpInvokeVirtual
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3196,6 +3208,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeSuper
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3204,6 +3217,8 @@
     jal     MterpInvokeSuper
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3226,6 +3241,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeDirect
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3234,6 +3250,8 @@
     jal     MterpInvokeDirect
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3249,6 +3267,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeStatic
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3257,6 +3276,8 @@
     jal     MterpInvokeStatic
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3272,6 +3293,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeInterface
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3280,6 +3302,8 @@
     jal     MterpInvokeInterface
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3316,6 +3340,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeVirtualRange
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3324,6 +3349,8 @@
     jal     MterpInvokeVirtualRange
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3339,6 +3366,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeSuperRange
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3347,6 +3375,8 @@
     jal     MterpInvokeSuperRange
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3362,6 +3392,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeDirectRange
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3370,6 +3401,8 @@
     jal     MterpInvokeDirectRange
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3385,6 +3418,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeStaticRange
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3393,6 +3427,8 @@
     jal     MterpInvokeStaticRange
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -3408,6 +3444,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeInterfaceRange
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -3416,6 +3453,8 @@
     jal     MterpInvokeInterfaceRange
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -6962,6 +7001,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeVirtualQuick
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -6970,6 +7010,8 @@
     jal     MterpInvokeVirtualQuick
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -6985,6 +7027,7 @@
     /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
     /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
     .extern MterpInvokeVirtualQuickRange
+    .extern MterpShouldSwitchInterpreters
     EXPORT_PC
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -6993,6 +7036,8 @@
     jal     MterpInvokeVirtualQuickRange
     beqzc   v0, MterpException
     FETCH_ADVANCE_INST 3
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
 
@@ -12256,6 +12301,7 @@
  *
  */
     .extern MterpHandleException
+    .extern MterpShouldSwitchInterpreters
 MterpException:
     move    a0, rSELF
     daddu   a1, rFP, OFF_FP_SHADOWFRAME
@@ -12266,8 +12312,11 @@
     REFRESH_IBASE
     daddu   rPC, a0, CODEITEM_INSNS_OFFSET
     dlsa    rPC, a1, rPC, 1                         # generate new dex_pc_ptr
-    sd      rPC, OFF_FP_DEX_PC_PTR(rFP)
+    /* Do we need to switch interpreters? */
+    jal     MterpShouldSwitchInterpreters
+    bnezc   v0, MterpFallback
     /* resume execution at catch block */
+    EXPORT_PC
     FETCH_INST
     GET_INST_OPCODE v0
     GOTO_OPCODE v0
@@ -12288,10 +12337,24 @@
     EXPORT_PC
     move    a0, rSELF
     jal     MterpSuspendCheck                       # (self)
+    bnezc   v0, MterpFallback                       # Something in the environment changed, switch interpreters
     GET_INST_OPCODE v0                              # extract opcode from rINST
     GOTO_OPCODE v0                                  # jump to next instruction
 
 /*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+    move    a0, rSELF
+    daddu   a1, rFP, OFF_FP_SHADOWFRAME
+    move    a2, rINST                               # rINST contains offset
+    jal     MterpLogOSR
+#endif
+    li      v0, 1                                   # Signal normal return
+    b       MterpDone
+
+/*
  * Bail out to reference interpreter.
  */
     .extern MterpLogFallback
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index a3b99e3..7dbd89c 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -188,9 +188,11 @@
 }
 
 void Jit::StartProfileSaver(const std::string& filename,
-                            const std::vector<std::string>& code_paths) {
+                            const std::vector<std::string>& code_paths,
+                            const std::string& foreign_dex_profile_path,
+                            const std::string& app_dir) {
   if (save_profiling_info_) {
-    ProfileSaver::Start(filename, code_cache_.get(), code_paths);
+    ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir);
   }
 }
 
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 3f54192..ee416d8 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -70,7 +70,17 @@
     return instrumentation_cache_.get();
   }
 
-  void StartProfileSaver(const std::string& filename, const std::vector<std::string>& code_paths);
+  // Starts the profile saver if the config options allow profile recording.
+  // The profile will be stored in the specified `filename` and will contain
+  // information collected from the given `code_paths` (a set of dex locations).
+  // The `foreign_dex_profile_path` is the path where the saver will put the
+  // profile markers for loaded dex files which are not owned by the application.
+  // The `app_dir` is the application directory and is used to decide which
+  // dex files belong to the application.
+  void StartProfileSaver(const std::string& filename,
+                         const std::vector<std::string>& code_paths,
+                         const std::string& foreign_dex_profile_path,
+                         const std::string& app_dir);
   void StopProfileSaver();
 
   void DumpForSigQuit(std::ostream& os) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 4f87e5b..050bb68 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -169,12 +169,16 @@
   return false;
 }
 
-class ScopedCodeCacheWrite {
+class ScopedCodeCacheWrite : ScopedTrace {
  public:
-  explicit ScopedCodeCacheWrite(MemMap* code_map) : code_map_(code_map) {
+  explicit ScopedCodeCacheWrite(MemMap* code_map)
+      : ScopedTrace("ScopedCodeCacheWrite"),
+        code_map_(code_map) {
+    ScopedTrace trace("mprotect all");
     CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtAll);
   }
   ~ScopedCodeCacheWrite() {
+    ScopedTrace trace("mprotect code");
     CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtCode);
   }
  private:
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index bd58157..6fe17db 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -16,6 +16,10 @@
 
 #include "profile_saver.h"
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include "art_method-inl.h"
 #include "base/systrace.h"
 #include "scoped_thread_state_change.h"
@@ -43,14 +47,31 @@
 
 ProfileSaver::ProfileSaver(const std::string& output_filename,
                            jit::JitCodeCache* jit_code_cache,
-                           const std::vector<std::string>& code_paths)
+                           const std::vector<std::string>& code_paths,
+                           const std::string& foreign_dex_profile_path,
+                           const std::string& app_data_dir)
     : jit_code_cache_(jit_code_cache),
+      foreign_dex_profile_path_(foreign_dex_profile_path),
       code_cache_last_update_time_ns_(0),
       shutting_down_(false),
       first_profile_(true),
       wait_lock_("ProfileSaver wait lock"),
       period_condition_("ProfileSaver period condition", wait_lock_) {
   AddTrackedLocations(output_filename, code_paths);
+  app_data_dir_ = "";
+  if (!app_data_dir.empty()) {
+    // The application directory is used to determine which dex files are owned by app.
+    // Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we
+    // don't have control over how the dex files are actually loaded (symlink or canonical path),
+    // store it's canonical form to be sure we use the same base when comparing.
+    UniqueCPtr<const char[]> app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr));
+    if (app_data_dir_real_path != nullptr) {
+      app_data_dir_.assign(app_data_dir_real_path.get());
+    } else {
+      LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_
+          << ". The app dir will not be used to determine which dex files belong to the app";
+    }
+  }
 }
 
 void ProfileSaver::Run() {
@@ -164,7 +185,9 @@
 
 void ProfileSaver::Start(const std::string& output_filename,
                          jit::JitCodeCache* jit_code_cache,
-                         const std::vector<std::string>& code_paths) {
+                         const std::vector<std::string>& code_paths,
+                         const std::string& foreign_dex_profile_path,
+                         const std::string& app_data_dir) {
   DCHECK(Runtime::Current()->UseJit());
   DCHECK(!output_filename.empty());
   DCHECK(jit_code_cache != nullptr);
@@ -183,7 +206,11 @@
   VLOG(profiler) << "Starting profile saver using output file: " << output_filename
       << ". Tracking: " << Join(code_paths, ':');
 
-  instance_ = new ProfileSaver(output_filename, jit_code_cache, code_paths);
+  instance_ = new ProfileSaver(output_filename,
+                               jit_code_cache,
+                               code_paths,
+                               foreign_dex_profile_path,
+                               app_data_dir);
 
   // Create a new thread which does the saving.
   CHECK_PTHREAD_CALL(
@@ -250,4 +277,97 @@
   }
 }
 
+void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
+  std::set<std::string> app_code_paths;
+  std::string foreign_dex_profile_path;
+  std::string app_data_dir;
+  {
+    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+    DCHECK(instance_ != nullptr);
+    // Make a copy so that we don't hold the lock while doing I/O.
+    for (const auto& it : instance_->tracked_dex_base_locations_) {
+      app_code_paths.insert(it.second.begin(), it.second.end());
+    }
+    foreign_dex_profile_path = instance_->foreign_dex_profile_path_;
+    app_data_dir = instance_->app_data_dir_;
+  }
+
+  MaybeRecordDexUseInternal(dex_location,
+                            app_code_paths,
+                            foreign_dex_profile_path,
+                            app_data_dir);
+}
+
+void ProfileSaver::MaybeRecordDexUseInternal(
+      const std::string& dex_location,
+      const std::set<std::string>& app_code_paths,
+      const std::string& foreign_dex_profile_path,
+      const std::string& app_data_dir) {
+  if (dex_location.empty()) {
+    LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
+    return;
+  }
+  if (foreign_dex_profile_path.empty()) {
+    LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
+    return;
+  }
+
+  UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
+  if (dex_location_real_path == nullptr) {
+    PLOG(WARNING) << "Could not get realpath for " << dex_location;
+  }
+  std::string dex_location_real_path_str((dex_location_real_path == nullptr)
+    ? dex_location.c_str()
+    : dex_location_real_path.get());
+
+  if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) {
+    // The dex location is under the application folder. Nothing to record.
+    return;
+  }
+
+  if (app_code_paths.find(dex_location) != app_code_paths.end()) {
+    // The dex location belongs to the application code paths. Nothing to record.
+    return;
+  }
+  // Do another round of checks with the real paths.
+  // Note that we could cache all the real locations in the saver (since it's an expensive
+  // operation). However we expect that app_code_paths is small (usually 1 element), and
+  // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise
+  // to save some bytes of memory usage.
+  for (const auto& app_code_location : app_code_paths) {
+    UniqueCPtr<const char[]> real_app_code_location(realpath(app_code_location.c_str(), nullptr));
+    if (real_app_code_location == nullptr) {
+      PLOG(WARNING) << "Could not get realpath for " << app_code_location;
+    }
+    std::string real_app_code_location_str((real_app_code_location == nullptr)
+        ? app_code_location.c_str()
+        : real_app_code_location.get());
+    if (real_app_code_location_str == dex_location_real_path_str) {
+      // The dex location belongs to the application code paths. Nothing to record.
+      return;
+    }
+  }
+
+  // For foreign dex files we record a flag on disk. PackageManager will (potentially) take this
+  // into account when deciding how to optimize the loaded dex file.
+  // The expected flag name is the canonical path of the apk where '/' is substituted to '@'.
+  // (it needs to be kept in sync with
+  // frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java)
+  std::replace(dex_location_real_path_str.begin(), dex_location_real_path_str.end(), '/', '@');
+  std::string flag_path = foreign_dex_profile_path + "/" + dex_location_real_path_str;
+  // No need to give any sort of access to flag_path. The system has enough permissions
+  // to test for its existence.
+  int fd = TEMP_FAILURE_RETRY(open(flag_path.c_str(), O_CREAT | O_EXCL, 0));
+  if (fd != -1) {
+    if (close(fd) != 0) {
+      PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
+    }
+  } else {
+    if (errno != EEXIST) {
+      // Another app could have already created the file.
+      PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
+    }
+  }
+}
+
 }   // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 21017c1..e7eab95 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -30,7 +30,9 @@
   // If the saver is already running it adds (output_filename, code_paths) to its tracked locations.
   static void Start(const std::string& output_filename,
                     jit::JitCodeCache* jit_code_cache,
-                    const std::vector<std::string>& code_paths)
+                    const std::vector<std::string>& code_paths,
+                    const std::string& foreign_dex_profile_path,
+                    const std::string& app_data_dir)
       REQUIRES(!Locks::profiler_lock_, !wait_lock_);
 
   // Stops the profile saver thread.
@@ -42,10 +44,14 @@
   // Returns true if the profile saver is started.
   static bool IsStarted() REQUIRES(!Locks::profiler_lock_);
 
+  static void NotifyDexUse(const std::string& dex_location);
+
  private:
   ProfileSaver(const std::string& output_filename,
                jit::JitCodeCache* jit_code_cache,
-               const std::vector<std::string>& code_paths);
+               const std::vector<std::string>& code_paths,
+               const std::string& foreign_dex_profile_path,
+               const std::string& app_data_dir);
 
   // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
   static void* RunProfileSaverThread(void* arg)
@@ -64,6 +70,12 @@
                            const std::vector<std::string>& code_paths)
       REQUIRES(Locks::profiler_lock_);
 
+  static void MaybeRecordDexUseInternal(
+      const std::string& dex_location,
+      const std::set<std::string>& tracked_locations,
+      const std::string& foreign_dex_profile_path,
+      const std::string& app_data_dir);
+
   // The only instance of the saver.
   static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
   // Profile saver thread.
@@ -72,6 +84,8 @@
   jit::JitCodeCache* jit_code_cache_;
   SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_
       GUARDED_BY(Locks::profiler_lock_);
+  std::string foreign_dex_profile_path_;
+  std::string app_data_dir_;
   uint64_t code_cache_last_update_time_ns_;
   bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
   bool first_profile_ = true;
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 11156c6..421641c 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -590,7 +590,19 @@
 }
 
 bool MemMap::Sync() {
-  return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0;
+  bool result;
+  if (redzone_size_ != 0) {
+    // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing
+    // it to msync() as it only accepts page-aligned base address, and exclude the higher-end
+    // noaccess protection from the msync range. b/27552451.
+    uint8_t* base_begin = reinterpret_cast<uint8_t*>(base_begin_);
+    MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin);
+    result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0;
+    MEMORY_TOOL_MAKE_NOACCESS(base_begin, begin_ - base_begin);
+  } else {
+    result = msync(BaseBegin(), BaseSize(), MS_SYNC) == 0;
+  }
+  return result;
 }
 
 bool MemMap::Protect(int prot) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 103a8b7..19584ed 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -29,7 +29,6 @@
 #include "dex_cache.h"
 #include "dex_file.h"
 #include "gc/heap-inl.h"
-#include "jit/profiling_info.h"
 #include "iftable.h"
 #include "object_array-inl.h"
 #include "read_barrier-inl.h"
@@ -940,12 +939,6 @@
   }
   for (ArtMethod& method : GetMethods(pointer_size)) {
     method.VisitRoots(visitor, pointer_size);
-    if (method.GetDeclaringClassUnchecked() != nullptr && !method.IsNative()) {
-      ProfilingInfo* profiling_info = method.GetProfilingInfo(pointer_size);
-      if (profiling_info != nullptr) {
-        profiling_info->VisitRoots(visitor);
-      }
-    }
   }
 }
 
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 6643ac2..f1e0fa7 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -347,15 +347,14 @@
 
 static jint GetDexOptNeeded(JNIEnv* env,
                             const char* filename,
-                            const char* pkgname,
                             const char* instruction_set,
-                            const jboolean defer) {
+                            const int target_compilation_type_mask) {
   if ((filename == nullptr) || !OS::FileExists(filename)) {
     LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
     ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
     const char* message = (filename == nullptr) ? "<empty file name>" : filename;
     env->ThrowNew(fnfe.get(), message);
-    return OatFileAssistant::kNoDexOptNeeded;
+    return -1;
   }
 
   const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
@@ -363,73 +362,52 @@
     ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
     std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set));
     env->ThrowNew(iae.get(), message.c_str());
-    return 0;
+    return -1;
   }
 
   // TODO: Verify the dex location is well formed, and throw an IOException if
   // not?
-
-  OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname);
+  OatFileAssistant oat_file_assistant(filename, target_compilation_type_mask,
+      target_instruction_set, false);
 
   // Always treat elements of the bootclasspath as up-to-date.
   if (oat_file_assistant.IsInBootClassPath()) {
     return OatFileAssistant::kNoDexOptNeeded;
   }
 
-  // TODO: Checking the profile should probably be done in the GetStatus()
-  // function. We have it here because GetStatus() should not be copying
-  // profile files. But who should be copying profile files?
-  if (oat_file_assistant.OdexFileIsOutOfDate()) {
-    // Needs recompile if profile has changed significantly.
-    if (Runtime::Current()->GetProfilerOptions().IsEnabled()) {
-      if (oat_file_assistant.IsProfileChangeSignificant()) {
-        if (!defer) {
-          oat_file_assistant.CopyProfileFile();
-        }
-        return OatFileAssistant::kDex2OatNeeded;
-      } else if (oat_file_assistant.ProfileExists()
-          && !oat_file_assistant.OldProfileExists()) {
-        if (!defer) {
-          oat_file_assistant.CopyProfileFile();
-        }
-      }
-    }
-  }
-
   return oat_file_assistant.GetDexOptNeeded();
 }
 
 static jint DexFile_getDexOptNeeded(JNIEnv* env,
                                     jclass,
                                     jstring javaFilename,
-                                    jstring javaPkgname,
                                     jstring javaInstructionSet,
-                                    jboolean defer) {
+                                    jint javaTargetCompilationTypeMask) {
   ScopedUtfChars filename(env, javaFilename);
   if (env->ExceptionCheck()) {
-    return 0;
+    return -1;
   }
 
-  NullableScopedUtfChars pkgname(env, javaPkgname);
-
   ScopedUtfChars instruction_set(env, javaInstructionSet);
   if (env->ExceptionCheck()) {
-    return 0;
+    return -1;
   }
 
   return GetDexOptNeeded(env,
                          filename.c_str(),
-                         pkgname.c_str(),
                          instruction_set.c_str(),
-                         defer);
+                         javaTargetCompilationTypeMask);
 }
 
-// public API, null pkgname
+// public API
 static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
   const char* instruction_set = GetInstructionSetString(kRuntimeISA);
   ScopedUtfChars filename(env, javaFilename);
-  jint status = GetDexOptNeeded(env, filename.c_str(), nullptr /* pkgname */,
-                                instruction_set, false /* defer */);
+  jint status = GetDexOptNeeded(
+      env,
+      filename.c_str(),
+      instruction_set,
+      OatFileAssistant::kFullCompilation | OatFileAssistant::kProfileGuideCompilation);
   return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -445,7 +423,7 @@
   NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
   NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
   NATIVE_METHOD(DexFile, getDexOptNeeded,
-                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
+                "(Ljava/lang/String;Ljava/lang/String;I)I"),
   NATIVE_METHOD(DexFile, openDexFileNative,
                 "(Ljava/lang/String;"
                 "Ljava/lang/String;"
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index da4a891..f6b2f21 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -566,8 +566,9 @@
 static void VMRuntime_registerAppInfo(JNIEnv* env,
                                       jclass clazz ATTRIBUTE_UNUSED,
                                       jstring profile_file,
-                                      jstring app_dir ATTRIBUTE_UNUSED,  // TODO: remove argument
-                                      jobjectArray code_paths) {
+                                      jstring app_dir,
+                                      jobjectArray code_paths,
+                                      jstring foreign_dex_profile_path) {
   std::vector<std::string> code_paths_vec;
   int code_paths_length = env->GetArrayLength(code_paths);
   for (int i = 0; i < code_paths_length; i++) {
@@ -581,7 +582,22 @@
   std::string profile_file_str(raw_profile_file);
   env->ReleaseStringUTFChars(profile_file, raw_profile_file);
 
-  Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str);
+  std::string foreign_dex_profile_path_str = "";
+  if (foreign_dex_profile_path != nullptr) {
+    const char* raw_foreign_dex_profile_path =
+        env->GetStringUTFChars(foreign_dex_profile_path, nullptr);
+    foreign_dex_profile_path_str.assign(raw_foreign_dex_profile_path);
+    env->ReleaseStringUTFChars(foreign_dex_profile_path, raw_foreign_dex_profile_path);
+  }
+
+  const char* raw_app_dir = env->GetStringUTFChars(app_dir, nullptr);
+  std::string app_dir_str(raw_app_dir);
+  env->ReleaseStringUTFChars(app_dir, raw_app_dir);
+
+  Runtime::Current()->RegisterAppInfo(code_paths_vec,
+                                      profile_file_str,
+                                      foreign_dex_profile_path_str,
+                                      app_dir_str);
 }
 
 static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) {
@@ -638,7 +654,7 @@
   NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "!()Z"),
   NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"),
   NATIVE_METHOD(VMRuntime, registerAppInfo,
-                "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V"),
+                "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V"),
   NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"),
   NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"),
 };
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 6ffd476..858849f 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
+#include <atomic>
 
 namespace art {
 
@@ -473,6 +474,18 @@
   obj->SetField64<false>(MemberOffset(offset), conv.converted);
 }
 
+static void Unsafe_loadFence(JNIEnv*, jobject) {
+  std::atomic_thread_fence(std::memory_order_acquire);
+}
+
+static void Unsafe_storeFence(JNIEnv*, jobject) {
+  std::atomic_thread_fence(std::memory_order_release);
+}
+
+static void Unsafe_fullFence(JNIEnv*, jobject) {
+  std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"),
   NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"),
@@ -532,6 +545,11 @@
   OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ),
   OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF),
   OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD),
+
+  // CAS
+  NATIVE_METHOD(Unsafe, loadFence, "!()V"),
+  NATIVE_METHOD(Unsafe, storeFence, "!()V"),
+  NATIVE_METHOD(Unsafe, fullFence, "!()V"),
 };
 
 void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 4948558..2ac1052 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -29,6 +29,8 @@
 constexpr uint8_t OatHeader::kOatVersion[4];
 constexpr const char OatHeader::kTrueValue[];
 constexpr const char OatHeader::kFalseValue[];
+constexpr const char OatHeader::kExtractOnlyValue[];
+constexpr const char OatHeader::kProfileGuideCompiledValue[];
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -467,12 +469,24 @@
 }
 
 bool OatHeader::IsExtractOnly() const {
-  return IsKeyEnabled(OatHeader::kExtractOnlyKey);
+  return KeyHasValue(kCompilationType,
+                     kExtractOnlyValue,
+                     sizeof(kExtractOnlyValue));
+}
+
+bool OatHeader::IsProfileGuideCompiled() const {
+  return KeyHasValue(kCompilationType,
+                     kProfileGuideCompiledValue,
+                     sizeof(kProfileGuideCompiledValue));
+}
+
+bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const {
+  const char* key_value = GetStoreValueByKey(key);
+  return (key_value != nullptr && strncmp(key_value, value, value_size) == 0);
 }
 
 bool OatHeader::IsKeyEnabled(const char* key) const {
-  const char* key_value = GetStoreValueByKey(key);
-  return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0);
+  return KeyHasValue(key, kTrueValue, sizeof(kTrueValue));
 }
 
 void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
diff --git a/runtime/oat.h b/runtime/oat.h
index fde386f..0660e19 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -38,12 +38,15 @@
   static constexpr const char* kDex2OatHostKey = "dex2oat-host";
   static constexpr const char* kPicKey = "pic";
   static constexpr const char* kDebuggableKey = "debuggable";
-  static constexpr const char* kExtractOnlyKey = "extract-only";
+  static constexpr const char* kCompilationType = "compilation-type";
   static constexpr const char* kClassPathKey = "classpath";
   static constexpr const char* kBootClassPath = "bootclasspath";
 
   static constexpr const char kTrueValue[] = "true";
   static constexpr const char kFalseValue[] = "false";
+  static constexpr const char kExtractOnlyValue[] = "extract-only";
+  static constexpr const char kProfileGuideCompiledValue[] = "profile-guide";
+
 
   static OatHeader* Create(InstructionSet instruction_set,
                            const InstructionSetFeatures* instruction_set_features,
@@ -108,8 +111,11 @@
   bool IsPic() const;
   bool IsDebuggable() const;
   bool IsExtractOnly() const;
+  bool IsProfileGuideCompiled() const;
 
  private:
+  bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
+
   OatHeader(InstructionSet instruction_set,
             const InstructionSetFeatures* instruction_set_features,
             uint32_t dex_file_count,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c389547..7155c79 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -169,7 +169,10 @@
     return false;
   }
   if (requested_base != nullptr && begin_ != requested_base) {
-    PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+    // Host can fail this check. Do not dump there to avoid polluting the output.
+    if (kIsTargetBuild) {
+      PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+    }
     *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "
         "oatdata=%p != expected=%p. See process maps in the log.",
         begin_, requested_base);
@@ -1232,6 +1235,10 @@
   return GetOatHeader().IsExtractOnly();
 }
 
+bool OatFile::IsProfileGuideCompiled() const {
+  return GetOatHeader().IsProfileGuideCompiled();
+}
+
 static constexpr char kDexClassPathEncodingSeparator = '*';
 
 std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index fb91a8c..1084253 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -93,6 +93,8 @@
 
   bool IsExtractOnly() const;
 
+  bool IsProfileGuideCompiled() const;
+
   const std::string& GetLocation() const {
     return location_;
   }
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 262c932..90712c6 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -36,7 +36,6 @@
 #include "image.h"
 #include "oat.h"
 #include "os.h"
-#include "profiler.h"
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
 #include "ScopedFd.h"
@@ -45,28 +44,19 @@
 namespace art {
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
+                                   const int target_compilation_type_mask,
                                    const InstructionSet isa,
                                    bool load_executable)
-    : OatFileAssistant(dex_location, nullptr, isa, load_executable, nullptr) { }
+    : OatFileAssistant(dex_location, nullptr, target_compilation_type_mask, isa, load_executable)
+{ }
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const char* oat_location,
+                                   const int target_compilation_type_mask,
                                    const InstructionSet isa,
                                    bool load_executable)
-    : OatFileAssistant(dex_location, oat_location, isa, load_executable, nullptr) { }
-
-OatFileAssistant::OatFileAssistant(const char* dex_location,
-                                   const InstructionSet isa,
-                                   bool load_executable,
-                                   const char* package_name)
-    : OatFileAssistant(dex_location, nullptr, isa, load_executable, package_name) { }
-
-OatFileAssistant::OatFileAssistant(const char* dex_location,
-                                   const char* oat_location,
-                                   const InstructionSet isa,
-                                   bool load_executable,
-                                   const char* package_name)
-    : isa_(isa), package_name_(package_name), load_executable_(load_executable) {
+    : target_compilation_type_mask_(target_compilation_type_mask), isa_(isa),
+      load_executable_(load_executable) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
   dex_location_.assign(dex_location);
 
@@ -83,18 +73,6 @@
     cached_oat_file_name_attempted_ = true;
     cached_oat_file_name_found_ = true;
   }
-
-  // If there is no package name given, we will not be able to find any
-  // profiles associated with this dex location. Preemptively mark that to
-  // be the case, rather than trying to find and load the profiles later.
-  // Similarly, if profiling is disabled.
-  if (package_name == nullptr
-      || !Runtime::Current()->GetProfilerOptions().IsEnabled()) {
-    profile_load_attempted_ = true;
-    profile_load_succeeded_ = false;
-    old_profile_load_attempted_ = true;
-    old_profile_load_succeeded_ = false;
-  }
 }
 
 OatFileAssistant::~OatFileAssistant() {
@@ -138,10 +116,23 @@
   return true;
 }
 
-OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
-  // TODO: If the profiling code is ever restored, it's worth considering
-  // whether we should check to see if the profile is out of date here.
+// Returns the compilation mode of the given oat file.
+static OatFileAssistant::CompilationType GetCompilationType(const OatFile& oat_file) {
+    if (oat_file.IsExtractOnly()) {
+      return OatFileAssistant::kExtractOnly;
+    }
+    if (oat_file.IsProfileGuideCompiled()) {
+      return OatFileAssistant::kProfileGuideCompilation;
+    }
+    // Assume that if the oat files is not extract-only or profile-guide compiled
+    // then it must be fully compiled.
+    // NB: this does not necessary mean that the oat file is actually fully compiled. It
+    // might have been compiled in a different way (e.g. interpret-only) which does
+    // not record a type in the header.
+    return OatFileAssistant::kFullCompilation;
+}
 
+OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
   if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
     return kNoDexOptNeeded;
   }
@@ -419,6 +410,11 @@
 }
 
 bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
+  // Verify the file satisfies the desired compilation type.
+  if ((target_compilation_type_mask_ & GetCompilationType(file)) == 0) {
+    return true;
+  }
+
   // Verify the dex checksum.
   // Note: GetOatDexFile will return null if the dex checksum doesn't match
   // what we provide, which verifies the primary dex checksum for us.
@@ -541,104 +537,6 @@
   return true;
 }
 
-bool OatFileAssistant::ProfileExists() {
-  return GetProfile() != nullptr;
-}
-
-bool OatFileAssistant::OldProfileExists() {
-  return GetOldProfile() != nullptr;
-}
-
-// TODO: The IsProfileChangeSignificant implementation was copied from likely
-// bit-rotted code.
-bool OatFileAssistant::IsProfileChangeSignificant() {
-  ProfileFile* profile = GetProfile();
-  if (profile == nullptr) {
-    return false;
-  }
-
-  ProfileFile* old_profile = GetOldProfile();
-  if (old_profile == nullptr) {
-    return false;
-  }
-
-  // TODO: The following code to compare two profile files should live with
-  // the rest of the profiler code, not the oat file assistant code.
-
-  // A change in profile is considered significant if X% (change_thr property)
-  // of the top K% (compile_thr property) samples has changed.
-  const ProfilerOptions& options = Runtime::Current()->GetProfilerOptions();
-  const double top_k_threshold = options.GetTopKThreshold();
-  const double change_threshold = options.GetTopKChangeThreshold();
-  std::set<std::string> top_k, old_top_k;
-  profile->GetTopKSamples(top_k, top_k_threshold);
-  old_profile->GetTopKSamples(old_top_k, top_k_threshold);
-  std::set<std::string> diff;
-  std::set_difference(top_k.begin(), top_k.end(), old_top_k.begin(),
-      old_top_k.end(), std::inserter(diff, diff.end()));
-
-  // TODO: consider using the usedPercentage instead of the plain diff count.
-  double change_percent = 100.0 * static_cast<double>(diff.size())
-                                / static_cast<double>(top_k.size());
-  std::set<std::string>::iterator end = diff.end();
-  for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
-    VLOG(oat) << "Profile new in topK: " << *it;
-  }
-
-  if (change_percent > change_threshold) {
-      VLOG(oat) << "Oat File Assistant: Profile for " << dex_location_
-        << "has changed significantly: (top "
-        << top_k_threshold << "% samples changed in proportion of "
-        << change_percent << "%)";
-      return true;
-  }
-  return false;
-}
-
-// TODO: The CopyProfileFile implementation was copied from likely bit-rotted
-// code.
-void OatFileAssistant::CopyProfileFile() {
-  if (!ProfileExists()) {
-    return;
-  }
-
-  std::string profile_name = ProfileFileName();
-  std::string old_profile_name = OldProfileFileName();
-
-  ScopedFd src(open(old_profile_name.c_str(), O_RDONLY));
-  if (src.get() == -1) {
-    PLOG(WARNING) << "Failed to open profile file " << old_profile_name
-      << ". My uid:gid is " << getuid() << ":" << getgid();
-    return;
-  }
-
-  struct stat stat_src;
-  if (fstat(src.get(), &stat_src) == -1) {
-    PLOG(WARNING) << "Failed to get stats for profile file  " << old_profile_name
-      << ". My uid:gid is " << getuid() << ":" << getgid();
-    return;
-  }
-
-  // Create the copy with rw------- (only accessible by system)
-  ScopedFd dst(open(profile_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0600));
-  if (dst.get()  == -1) {
-    PLOG(WARNING) << "Failed to create/write prev profile file " << profile_name
-      << ".  My uid:gid is " << getuid() << ":" << getgid();
-    return;
-  }
-
-#ifdef __linux__
-  if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) {
-#else
-  off_t len;
-  if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) {
-#endif
-    PLOG(WARNING) << "Failed to copy profile file " << old_profile_name
-      << " to " << profile_name << ". My uid:gid is " << getuid()
-      << ":" << getgid();
-  }
-}
-
 bool OatFileAssistant::RelocateOatFile(const std::string* input_file,
                                        std::string* error_msg) {
   CHECK(error_msg != nullptr);
@@ -694,6 +592,15 @@
 bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
   CHECK(error_msg != nullptr);
 
+  // TODO: Currently we only know how to make a fully-compiled oat file.
+  // Perhaps we should support generating other kinds of oat files?
+  if ((target_compilation_type_mask_ & kFullCompilation) == 0) {
+    *error_msg = "Generation of oat file for dex location " + dex_location_
+      + " not attempted because full compilation was not specified"
+      + " as an acceptable target compilation type.";
+    return false;
+  }
+
   Runtime* runtime = Runtime::Current();
   if (!runtime->IsDex2OatEnabled()) {
     *error_msg = "Generation of oat file for dex location " + dex_location_
@@ -861,21 +768,6 @@
   return result;
 }
 
-std::string OatFileAssistant::ProfileFileName() {
-  if (package_name_ != nullptr) {
-    return DalvikCacheDirectory() + std::string("profiles/") + package_name_;
-  }
-  return "";
-}
-
-std::string OatFileAssistant::OldProfileFileName() {
-  std::string profile_name = ProfileFileName();
-  if (profile_name.empty()) {
-    return "";
-  }
-  return profile_name + "@old";
-}
-
 std::string OatFileAssistant::ImageLocation() {
   Runtime* runtime = Runtime::Current();
   const std::vector<gc::space::ImageSpace*>& image_spaces =
@@ -1007,34 +899,6 @@
   return image_info_load_succeeded_ ? &cached_image_info_ : nullptr;
 }
 
-ProfileFile* OatFileAssistant::GetProfile() {
-  if (!profile_load_attempted_) {
-    CHECK(package_name_ != nullptr)
-      << "pakage_name_ is nullptr: "
-      << "profile_load_attempted_ should have been true";
-    profile_load_attempted_ = true;
-    std::string profile_name = ProfileFileName();
-    if (!profile_name.empty()) {
-      profile_load_succeeded_ = cached_profile_.LoadFile(profile_name);
-    }
-  }
-  return profile_load_succeeded_ ? &cached_profile_ : nullptr;
-}
-
-ProfileFile* OatFileAssistant::GetOldProfile() {
-  if (!old_profile_load_attempted_) {
-    CHECK(package_name_ != nullptr)
-      << "pakage_name_ is nullptr: "
-      << "old_profile_load_attempted_ should have been true";
-    old_profile_load_attempted_ = true;
-    std::string old_profile_name = OldProfileFileName();
-    if (!old_profile_name.empty()) {
-      old_profile_load_succeeded_ = cached_old_profile_.LoadFile(old_profile_name);
-    }
-  }
-  return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr;
-}
-
 gc::space::ImageSpace* OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
   DCHECK(oat_file != nullptr);
   std::string art_file = ArtFileName(oat_file);
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 7b45bca..893aea2 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -44,9 +44,6 @@
 // The oat file assistant is intended to be used with dex locations not on the
 // boot class path. See the IsInBootClassPath method for a way to check if the
 // dex location is in the boot class path.
-//
-// TODO: All the profiling related code is old and untested. It should either
-// be restored and tested, or removed.
 class OatFileAssistant {
  public:
   enum DexOptNeeded {
@@ -73,8 +70,8 @@
 
   enum OatStatus {
     // kOatOutOfDate - An oat file is said to be out of date if the file does
-    // not exist, or is out of date with respect to the dex file or boot
-    // image.
+    // not exist, is out of date with respect to the dex file or boot image,
+    // or does not meet the target compilation type.
     kOatOutOfDate,
 
     // kOatNeedsRelocation - An oat file is said to need relocation if the
@@ -88,6 +85,20 @@
     kOatUpToDate,
   };
 
+  // Represents the different compilation types of oat files that OatFileAssitant
+  // and external GetDexOptNeeded callers care about.
+  // Note: these should be able to be used as part of a mask.
+  enum CompilationType {
+    // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_FULL = 1
+    kFullCompilation = 1,
+
+    // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_PROFILE_GUIDE = 2
+    kProfileGuideCompilation = 2,
+
+    // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_EXTRACT_ONLY = 4
+    kExtractOnly = 4,
+  };
+
   // Constructs an OatFileAssistant object to assist the oat file
   // corresponding to the given dex location with the target instruction set.
   //
@@ -99,31 +110,28 @@
   // Note: Currently the dex_location must have an extension.
   // TODO: Relax this restriction?
   //
+  // The target compilation type specifies a set of CompilationTypes that
+  // should be considered up to date. An oat file compiled in a way not
+  // included in the set is considered out of date. For example, to consider
+  // otherwise up-to-date fully compiled and profile-guide compiled oat
+  // files as up to date, but to consider extract-only files as out of date,
+  // specify: (kFullCompilation | kProfileGuideCompilation).
+  //
   // The isa should be either the 32 bit or 64 bit variant for the current
   // device. For example, on an arm device, use arm or arm64. An oat file can
   // be loaded executable only if the ISA matches the current runtime.
-  OatFileAssistant(const char* dex_location, const InstructionSet isa,
+  OatFileAssistant(const char* dex_location,
+                   int target_compilation_type_mask,
+                   const InstructionSet isa,
                    bool load_executable);
 
   // Constructs an OatFileAssistant, providing an explicit target oat_location
   // to use instead of the standard oat location.
-  OatFileAssistant(const char* dex_location, const char* oat_location,
-                   const InstructionSet isa, bool load_executable);
-
-  // Constructs an OatFileAssistant, providing an additional package_name used
-  // solely for the purpose of locating profile files.
-  //
-  // TODO: Why is the name of the profile file based on the package name and
-  // not the dex location? If there is no technical reason the dex_location
-  // can't be used, we should prefer that instead.
-  OatFileAssistant(const char* dex_location, const InstructionSet isa,
-                   bool load_executable, const char* package_name);
-
-  // Constructs an OatFileAssistant with user specified oat location and a
-  // package name.
-  OatFileAssistant(const char* dex_location, const char* oat_location,
-                   const InstructionSet isa, bool load_executable,
-                   const char* package_name);
+  OatFileAssistant(const char* dex_location,
+                   const char* oat_location,
+                   int target_compilation_type_mask,
+                   const InstructionSet isa,
+                   bool load_executable);
 
   ~OatFileAssistant();
 
@@ -233,28 +241,6 @@
   bool GivenOatFileNeedsRelocation(const OatFile& file);
   bool GivenOatFileIsUpToDate(const OatFile& file);
 
-  // Returns true if there is an accessible profile associated with the dex
-  // location.
-  // This returns false if profiling is disabled.
-  bool ProfileExists();
-
-  // The old profile is a file containing a previous snapshot of profiling
-  // information associated with the dex file code. This is used to track how
-  // the profiling information has changed over time.
-  //
-  // Returns true if there is an accessible old profile associated with the
-  // dex location.
-  // This returns false if profiling is disabled.
-  bool OldProfileExists();
-
-  // Returns true if there has been a significant change between the old
-  // profile and the current profile.
-  // This returns false if profiling is disabled.
-  bool IsProfileChangeSignificant();
-
-  // Copy the current profile to the old profile location.
-  void CopyProfileFile();
-
   // Generates the oat file by relocation from the named input file.
   // This does not check the current status before attempting to relocate the
   // oat file.
@@ -309,16 +295,6 @@
   // Returns an empty string if we can't get the dalvik cache directory path.
   std::string DalvikCacheDirectory();
 
-  // Constructs the filename for the profile file.
-  // Returns an empty string if we do not have the necessary information to
-  // construct the filename.
-  std::string ProfileFileName();
-
-  // Constructs the filename for the old profile file.
-  // Returns an empty string if we do not have the necessary information to
-  // construct the filename.
-  std::string OldProfileFileName();
-
   // Returns the current image location.
   // Returns an empty string if the image location could not be retrieved.
   //
@@ -364,35 +340,18 @@
   // The caller shouldn't clean up or free the returned pointer.
   const ImageInfo* GetImageInfo();
 
-  // Returns the loaded profile.
-  // Loads the profile if needed. Returns null if the profile failed
-  // to load.
-  // The caller shouldn't clean up or free the returned pointer.
-  ProfileFile* GetProfile();
-
-  // Returns the loaded old profile.
-  // Loads the old profile if needed. Returns null if the old profile
-  // failed to load.
-  // The caller shouldn't clean up or free the returned pointer.
-  ProfileFile* GetOldProfile();
-
   // To implement Lock(), we lock a dummy file where the oat file would go
   // (adding ".flock" to the target file name) and retain the lock for the
   // remaining lifetime of the OatFileAssistant object.
   ScopedFlock flock_;
 
   std::string dex_location_;
+  const int target_compilation_type_mask_;
 
   // In a properly constructed OatFileAssistant object, isa_ should be either
   // the 32 or 64 bit variant for the current device.
   const InstructionSet isa_ = kNone;
 
-  // The package name, used solely to find the profile file.
-  // This may be null in a properly constructed object. In this case,
-  // profile_load_attempted_ and old_profile_load_attempted_ will be true, and
-  // profile_load_succeeded_ and old_profile_load_succeeded_ will be false.
-  const char* package_name_ = nullptr;
-
   // Whether we will attempt to load oat files executable.
   bool load_executable_ = false;
 
@@ -451,18 +410,6 @@
   bool image_info_load_succeeded_ = false;
   ImageInfo cached_image_info_;
 
-  // Cached value of the profile file.
-  // Use the GetProfile method rather than accessing these directly.
-  bool profile_load_attempted_ = false;
-  bool profile_load_succeeded_ = false;
-  ProfileFile cached_profile_;
-
-  // Cached value of the profile file.
-  // Use the GetOldProfile method rather than accessing these directly.
-  bool old_profile_load_attempted_ = false;
-  bool old_profile_load_succeeded_ = false;
-  ProfileFile cached_old_profile_;
-
   // For debugging only.
   // If this flag is set, the oat or odex file has been released to the user
   // of the OatFileAssistant object and the OatFileAssistant object is in a
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 83d4457..4541468 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -260,7 +260,7 @@
   }
 
   void GenerateExtractOnlyOdexForTest(const std::string& dex_location,
-                                          const std::string& odex_location) {
+                                      const std::string& odex_location) {
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--oat-file=" + odex_location);
@@ -277,7 +277,26 @@
     EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u);
     EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u);
     EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0);
-}
+  }
+
+  void GenerateProfileGuideOdexForTest(const std::string& dex_location,
+                                       const std::string& odex_location) {
+    std::vector<std::string> args;
+    args.push_back("--dex-file=" + dex_location);
+    args.push_back("--oat-file=" + odex_location);
+    ScratchFile profile_file;
+    args.push_back("--profile-file=" + profile_file.GetFilename());
+    std::string error_msg;
+    ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+    // Verify the odex file was generated as expected.
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(
+        odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
+        false, dex_location.c_str(), &error_msg));
+    printf("error %s", error_msg.c_str());
+    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+    EXPECT_TRUE(odex_file->IsProfileGuideCompiled());
+  }
 
  private:
   // Reserve memory around where the image will be loaded so other memory
@@ -344,7 +363,8 @@
 // Generate an oat file for the purposes of test, as opposed to testing
 // generation of oat files.
 static void GenerateOatForTest(const char* dex_location) {
-  OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location,
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
 
   std::string error_msg;
   ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg;
@@ -356,7 +376,8 @@
   std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
   Copy(GetDexSrc1(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
 
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -379,7 +400,8 @@
 TEST_F(OatFileAssistantTest, NoDexNoOat) {
   std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
@@ -400,7 +422,8 @@
   Copy(GetDexSrc1(), dex_location);
   GenerateOatForTest(dex_location.c_str());
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -422,7 +445,8 @@
   Copy(GetMultiDexSrc1(), dex_location);
   GenerateOatForTest(dex_location.c_str());
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
@@ -448,7 +472,8 @@
   // is out of date.
   Copy(GetMultiDexSrc2(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
@@ -475,6 +500,7 @@
   // Verify we can load both dex files.
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       oat_location.c_str(),
+                                      OatFileAssistant::kFullCompilation,
                                       kRuntimeISA, true);
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -495,7 +521,8 @@
   GenerateOatForTest(dex_location.c_str());
   Copy(GetDexSrc2(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -508,32 +535,6 @@
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
-// Case: We have a DEX file and an extract-only ODEX file out of date relative
-//       to the DEX file.
-// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) {
-  std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar";
-  std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex";
-
-  // We create a dex, generate an oat for it, then overwrite the dex with a
-  // different dex to make the oat out of date.
-  Copy(GetDexSrc1(), dex_location);
-  GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str());
-  Copy(GetDexSrc2(), dex_location);
-
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
-}
-
 // Case: We have a DEX file and an ODEX file, but no OAT file.
 // Expect: The status is kPatchOatNeeded.
 TEST_F(OatFileAssistantTest, DexOdexNoOat) {
@@ -545,7 +546,8 @@
   GenerateOdexForTest(dex_location, odex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
 
   EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -578,7 +580,8 @@
   Copy(GetStrippedDexSrc1(), dex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -633,7 +636,8 @@
   Copy(GetStrippedDexSrc1(), dex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -681,7 +685,8 @@
   Copy(GetStrippedDexSrc1(), dex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -724,7 +729,7 @@
   GenerateOdexForTest(dex_location, oat_location);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      oat_location.c_str(), kRuntimeISA, true);
+      oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -782,7 +787,7 @@
 
   // Verify things don't go bad.
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      oat_location.c_str(), kRuntimeISA, true);
+      oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -816,7 +821,8 @@
   GeneratePicOdexForTest(dex_location, odex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -841,7 +847,9 @@
   GenerateExtractOnlyOdexForTest(dex_location, odex_location);
 
   // Verify the status.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
+      kRuntimeISA, false);
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -864,7 +872,8 @@
   GenerateOatForTest(dex_location.c_str());
 
   // Load the oat using an oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -883,7 +892,8 @@
   GenerateOatForTest(dex_location.c_str());
 
   // Load the oat using an oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -903,7 +913,8 @@
   Copy(GetDexSrc1(), dex_location);
 
   OatFileAssistant oat_file_assistant(
-      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+      dex_location.c_str(), oat_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
   std::string error_msg;
   ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
 
@@ -917,7 +928,8 @@
   EXPECT_TRUE(OS::FileExists(oat_location.c_str()));
 
   // Verify it didn't create an oat in the default location.
-  OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant ofm(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
   EXPECT_FALSE(ofm.OatFileExists());
 }
 
@@ -933,7 +945,8 @@
   Copy(GetDexSrc1(), dex_location);
 
   OatFileAssistant oat_file_assistant(
-      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+      dex_location.c_str(), oat_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
   std::string error_msg;
   ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
 
@@ -948,7 +961,8 @@
   std::string oat_location = GetScratchDir() + "/GenNoDex.oat";
 
   OatFileAssistant oat_file_assistant(
-      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+      dex_location.c_str(), oat_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
   std::string error_msg;
   ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg));
 }
@@ -996,7 +1010,8 @@
   Copy(GetDexSrc1(), abs_dex_location);
 
   std::string dex_location = MakePathRelative(abs_dex_location);
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1013,7 +1028,8 @@
 TEST_F(OatFileAssistantTest, ShortDexLocation) {
   std::string dex_location = "/xx";
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1037,7 +1053,8 @@
   std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
   Copy(GetDexSrc1(), dex_location);
 
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
 
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
 
@@ -1134,7 +1151,8 @@
   GenerateOdexForTest(dex_location, odex_location);
 
   // Load the oat using an executable oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1156,7 +1174,8 @@
   GenerateOdexForTest(dex_location, odex_location);
 
   // Load the oat using an executable oat file assistant.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, true);
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1184,6 +1203,45 @@
         "/foo/bar/baz_noext", kArm, &odex_file, &error_msg));
 }
 
+// Case: We have a DEX file, extract-only ODEX, and fully compiled OAT.
+// Expect: The status depends on the target compilation type mask.
+TEST_F(OatFileAssistantTest, TargetCompilationType) {
+  std::string dex_location = GetScratchDir() + "/TargetCompilationType.jar";
+  std::string odex_location = GetOdexDir() + "/TargetCompilationType.odex";
+  Copy(GetDexSrc1(), dex_location);
+  GenerateExtractOnlyOdexForTest(dex_location, odex_location);
+  GenerateOatForTest(dex_location.c_str());
+
+  OatFileAssistant ofa_full(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_full.GetDexOptNeeded());
+  EXPECT_FALSE(ofa_full.IsInBootClassPath());
+  EXPECT_TRUE(ofa_full.OdexFileIsOutOfDate());
+  EXPECT_TRUE(ofa_full.OatFileIsUpToDate());
+
+  OatFileAssistant ofa_extract(dex_location.c_str(),
+      OatFileAssistant::kExtractOnly, kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract.GetDexOptNeeded());
+  EXPECT_FALSE(ofa_extract.IsInBootClassPath());
+  EXPECT_TRUE(ofa_extract.OdexFileIsUpToDate());
+  EXPECT_TRUE(ofa_extract.OatFileIsOutOfDate());
+
+  OatFileAssistant ofa_profile(dex_location.c_str(),
+      OatFileAssistant::kProfileGuideCompilation, kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, ofa_profile.GetDexOptNeeded());
+  EXPECT_FALSE(ofa_profile.IsInBootClassPath());
+  EXPECT_TRUE(ofa_profile.OdexFileIsOutOfDate());
+  EXPECT_TRUE(ofa_profile.OatFileIsOutOfDate());
+
+  OatFileAssistant ofa_extract_full(dex_location.c_str(),
+      OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
+      kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract_full.GetDexOptNeeded());
+  EXPECT_FALSE(ofa_extract_full.IsInBootClassPath());
+  EXPECT_TRUE(ofa_extract_full.OdexFileIsUpToDate());
+  EXPECT_TRUE(ofa_extract_full.OatFileIsUpToDate());
+}
+
 // Verify the dexopt status values from dalvik.system.DexFile
 // match the OatFileAssistant::DexOptStatus values.
 TEST_F(OatFileAssistantTest, DexOptStatusValues) {
@@ -1218,13 +1276,31 @@
   ASSERT_FALSE(self_patchoat_needed == nullptr);
   EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
   EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
+
+    ArtField* compilation_type_full = mirror::Class::FindStaticField(
+      soa.Self(), dexfile, "COMPILATION_TYPE_FULL", "I");
+  ASSERT_FALSE(compilation_type_full == nullptr);
+  EXPECT_EQ(compilation_type_full->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+  EXPECT_EQ(OatFileAssistant::kFullCompilation, compilation_type_full->GetInt(dexfile.Get()));
+
+  ArtField* compilation_type_profile_guide = mirror::Class::FindStaticField(
+      soa.Self(), dexfile, "COMPILATION_TYPE_PROFILE_GUIDE", "I");
+  ASSERT_FALSE(compilation_type_profile_guide == nullptr);
+  EXPECT_EQ(compilation_type_profile_guide->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+  EXPECT_EQ(OatFileAssistant::kProfileGuideCompilation,
+            compilation_type_profile_guide->GetInt(dexfile.Get()));
+
+  ArtField* compilation_type_extract_only = mirror::Class::FindStaticField(
+      soa.Self(), dexfile, "COMPILATION_TYPE_EXTRACT_ONLY", "I");
+  ASSERT_FALSE(compilation_type_extract_only == nullptr);
+  EXPECT_EQ(compilation_type_extract_only->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+  EXPECT_EQ(OatFileAssistant::kExtractOnly, compilation_type_extract_only->GetInt(dexfile.Get()));
 }
 
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
 //  * Test using secondary isa
-//  * Test with profiling info?
 //  * Test for status of oat while oat is being generated (how?)
 //  * Test case where 32 and 64 bit boot class paths differ,
 //      and we ask IsInBootClassPath for a class in exactly one of the 32 or
@@ -1233,5 +1309,7 @@
 //    - Dex is stripped, don't have odex.
 //    - Oat file corrupted after status check, before reload unexecutable
 //    because it's unrelocated and no dex2oat
+//  * Test unrelocated specific target compilation type can be relocated to
+//    make it up to date.
 
 }  // namespace art
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 9ae179f..e57125b 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -307,8 +307,13 @@
   Thread* const self = Thread::Current();
   Locks::mutator_lock_->AssertNotHeld(self);
   Runtime* const runtime = Runtime::Current();
+
+  int target_compilation_type_mask = OatFileAssistant::kFullCompilation
+    | OatFileAssistant::kProfileGuideCompilation
+    | OatFileAssistant::kExtractOnly;
   OatFileAssistant oat_file_assistant(dex_location,
                                       oat_location,
+                                      target_compilation_type_mask,
                                       kRuntimeISA,
                                       !runtime->IsAotCompiler());
 
@@ -443,6 +448,10 @@
           + std::string(dex_location));
     }
   }
+
+  // TODO(calin): Consider optimizing this knowing that is useless to record the
+  // use of fully compiled apks.
+  Runtime::Current()->NotifyDexLoaded(dex_location);
   return dex_files;
 }
 
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 9b10f2e..c7ccee2 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -744,9 +744,12 @@
     return false;
   }
   DCHECK_GE(field->GetOffset().Int32Value(), 0);
+  // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451.
+  uint32_t field_offset = field->GetOffset().Uint32Value();
+  bool is_volatile = field->IsVolatile();
   result->field_idx = field_idx;
-  result->field_offset = field->GetOffset().Int32Value();
-  result->is_volatile = field->IsVolatile();
+  result->field_offset = field_offset;
+  result->is_volatile = is_volatile ? 1u : 0u;
   return true;
 }
 
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 7e84b40..0e12d73 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -101,6 +101,17 @@
   kIntrinsicCas,
   kIntrinsicUnsafeGet,
   kIntrinsicUnsafePut,
+
+  // 1.8.
+  kIntrinsicUnsafeGetAndAddInt,
+  kIntrinsicUnsafeGetAndAddLong,
+  kIntrinsicUnsafeGetAndSetInt,
+  kIntrinsicUnsafeGetAndSetLong,
+  kIntrinsicUnsafeGetAndSetObject,
+  kIntrinsicUnsafeLoadFence,
+  kIntrinsicUnsafeStoreFence,
+  kIntrinsicUnsafeFullFence,
+
   kIntrinsicSystemArrayCopyCharArray,
   kIntrinsicSystemArrayCopy,
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index bbb79af..e95f2c5 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -119,6 +119,7 @@
 #include "os.h"
 #include "parsed_options.h"
 #include "profiler.h"
+#include "jit/profile_saver.h"
 #include "quick/quick_method_frame_info.h"
 #include "reflection.h"
 #include "runtime_options.h"
@@ -1700,7 +1701,9 @@
 }
 
 void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
-                              const std::string& profile_output_filename) {
+                              const std::string& profile_output_filename,
+                              const std::string& foreign_dex_profile_path,
+                              const std::string& app_dir) {
   if (jit_.get() == nullptr) {
     // We are not JITing. Nothing to do.
     return;
@@ -1723,7 +1726,18 @@
   }
 
   profile_output_filename_ = profile_output_filename;
-  jit_->StartProfileSaver(profile_output_filename, code_paths);
+  jit_->StartProfileSaver(profile_output_filename,
+                          code_paths,
+                          foreign_dex_profile_path,
+                          app_dir);
+}
+
+void Runtime::NotifyDexLoaded(const std::string& dex_location) {
+  VLOG(profiler) << "Notify dex loaded: " << dex_location;
+  // We know that if the ProfileSaver is started then we can record profile information.
+  if (ProfileSaver::IsStarted()) {
+    ProfileSaver::NotifyDexUse(dex_location);
+  }
 }
 
 // Transaction support.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 83e77d2..8e99f80 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -467,7 +467,10 @@
   }
 
   void RegisterAppInfo(const std::vector<std::string>& code_paths,
-                       const std::string& profile_output_filename);
+                       const std::string& profile_output_filename,
+                       const std::string& foreign_dex_profile_path,
+                       const std::string& app_dir);
+  void NotifyDexLoaded(const std::string& dex_location);
 
   // Transaction support.
   bool IsActiveTransaction() const {
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 8237b06..bc963c5 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -36,6 +36,7 @@
 
 static constexpr bool kDumpHeapObjectOnSigsevg = false;
 static constexpr bool kUseSigRTTimeout = true;
+static constexpr bool kDumpNativeStackOnTimeout = true;
 
 struct Backtrace {
  public:
@@ -350,7 +351,9 @@
   if (runtime != nullptr) {
     if (IsTimeoutSignal(signal_number)) {
       // Special timeout signal. Try to dump all threads.
-      runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL));
+      // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts
+      //       are of value here.
+      runtime->GetThreadList()->Dump(LOG(INTERNAL_FATAL), kDumpNativeStackOnTimeout);
     }
     gc::Heap* heap = runtime->GetHeap();
     LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index cf515b6..4c81d4f 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -171,9 +171,9 @@
   closedir(d);
 }
 
-// Dump checkpoint timeout in milliseconds. Larger amount on the host, as dumping will invoke
-// addr2line when available.
-static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000;
+// Dump checkpoint timeout in milliseconds. Larger amount on the target, since the device could be
+// overloaded with ANR dumps.
+static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000;
 
 // A closure used by Thread::Dump.
 class DumpCheckpoint FINAL : public Closure {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 30f613c..b171b75 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -17,6 +17,7 @@
 #include "reg_type_cache-inl.h"
 
 #include "base/arena_bit_vector.h"
+#include "base/bit_vector-inl.h"
 #include "base/casts.h"
 #include "base/scoped_arena_allocator.h"
 #include "base/stl_util.h"
@@ -351,9 +352,11 @@
     types.Copy(&left_merge->GetUnresolvedTypes());
     left_resolved = &left_merge->GetResolvedPart();
   } else if (left.IsUnresolvedTypes()) {
+    types.ClearAllBits();
     types.SetBit(left.GetId());
     left_resolved = &Zero();
   } else {
+    types.ClearAllBits();
     left_resolved = &left;
   }
 
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index 22ac7e4..42a74f8 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -30,23 +30,14 @@
 namespace art {
 namespace verifier {
 
-class BaseRegTypeTest : public CommonRuntimeTest {
- public:
-  void PostRuntimeCreate() OVERRIDE {
-    stack.reset(new ArenaStack(Runtime::Current()->GetArenaPool()));
-    allocator.reset(new ScopedArenaAllocator(stack.get()));
-  }
-
-  std::unique_ptr<ArenaStack> stack;
-  std::unique_ptr<ScopedArenaAllocator> allocator;
-};
-
-class RegTypeTest : public BaseRegTypeTest {};
+class RegTypeTest : public CommonRuntimeTest {};
 
 TEST_F(RegTypeTest, ConstLoHi) {
   // Tests creating primitive types types.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   const RegType& ref_type_const_0 = cache.FromCat1Const(10, true);
   const RegType& ref_type_const_1 = cache.FromCat1Const(10, true);
   const RegType& ref_type_const_2 = cache.FromCat1Const(30, true);
@@ -67,8 +58,10 @@
 }
 
 TEST_F(RegTypeTest, Pairs) {
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   int64_t val = static_cast<int32_t>(1234);
   const RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true);
   const RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
@@ -91,8 +84,10 @@
 }
 
 TEST_F(RegTypeTest, Primitives) {
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
 
   const RegType& bool_reg_type = cache.Boolean();
   EXPECT_FALSE(bool_reg_type.IsUndefined());
@@ -359,13 +354,15 @@
   EXPECT_TRUE(double_reg_type.HasClass());
 }
 
-class RegTypeReferenceTest : public BaseRegTypeTest {};
+class RegTypeReferenceTest : public CommonRuntimeTest {};
 
 TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) {
   // Tests matching precisions. A reference type that was created precise doesn't
   // match the one that is imprecise.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   const RegType& imprecise_obj = cache.JavaLangObject(false);
   const RegType& precise_obj = cache.JavaLangObject(true);
   const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
@@ -379,8 +376,10 @@
 TEST_F(RegTypeReferenceTest, UnresolvedType) {
   // Tests creating unresolved types. Miss for the first time asking the cache and
   // a hit second time.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
   EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes());
@@ -395,8 +394,10 @@
 
 TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) {
   // Tests creating types uninitialized types from unresolved types.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
   const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
@@ -417,8 +418,10 @@
 
 TEST_F(RegTypeReferenceTest, Dump) {
   // Tests types for proper Dump messages.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true);
   const RegType& resolved_ref = cache.JavaLangString();
@@ -442,8 +445,10 @@
   // Add a class to the cache then look for the same class and make sure it is  a
   // Hit the second time. Then check for the same effect when using
   // The JavaLangObject method instead of FromDescriptor. String class is final.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   const RegType& ref_type = cache.JavaLangString();
   const RegType& ref_type_2 = cache.JavaLangString();
   const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true);
@@ -462,8 +467,10 @@
   // Add a class to the cache then look for the same class and make sure it is  a
   // Hit the second time. Then I am checking for the same effect when using
   // The JavaLangObject method instead of FromDescriptor. Object Class in not final.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache(true, *allocator);
+  RegTypeCache cache(true, allocator);
   const RegType& ref_type = cache.JavaLangObject(true);
   const RegType& ref_type_2 = cache.JavaLangObject(true);
   const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
@@ -476,7 +483,9 @@
   // Tests merging logic
   // String and object , LUB is object.
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache_new(true, *allocator);
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
+  RegTypeCache cache_new(true, allocator);
   const RegType& string = cache_new.JavaLangString();
   const RegType& Object = cache_new.JavaLangObject(true);
   EXPECT_TRUE(string.Merge(Object, &cache_new).IsJavaLangObject());
@@ -498,8 +507,10 @@
 
 TEST_F(RegTypeTest, MergingFloat) {
   // Testing merging logic with float and float constants.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache_new(true, *allocator);
+  RegTypeCache cache_new(true, allocator);
 
   constexpr int32_t kTestConstantValue = 10;
   const RegType& float_type = cache_new.Float();
@@ -529,8 +540,10 @@
 
 TEST_F(RegTypeTest, MergingLong) {
   // Testing merging logic with long and long constants.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache_new(true, *allocator);
+  RegTypeCache cache_new(true, allocator);
 
   constexpr int32_t kTestConstantValue = 10;
   const RegType& long_lo_type = cache_new.LongLo();
@@ -583,8 +596,10 @@
 
 TEST_F(RegTypeTest, MergingDouble) {
   // Testing merging logic with double and double constants.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache_new(true, *allocator);
+  RegTypeCache cache_new(true, allocator);
 
   constexpr int32_t kTestConstantValue = 10;
   const RegType& double_lo_type = cache_new.DoubleLo();
@@ -637,8 +652,10 @@
 
 TEST_F(RegTypeTest, ConstPrecision) {
   // Tests creating primitive types types.
+  ArenaStack stack(Runtime::Current()->GetArenaPool());
+  ScopedArenaAllocator allocator(&stack);
   ScopedObjectAccess soa(Thread::Current());
-  RegTypeCache cache_new(true, *allocator);
+  RegTypeCache cache_new(true, allocator);
   const RegType& imprecise_const = cache_new.FromCat1Const(10, false);
   const RegType& precise_const = cache_new.FromCat1Const(10, true);
 
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index a9a7a05..b2f905e 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -40,7 +40,7 @@
   }
 
   private static Unsafe getUnsafe() throws Exception {
-    Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+    Class<?> unsafeClass = Unsafe.class;
     Field f = unsafeClass.getDeclaredField("theUnsafe");
     f.setAccessible(true);
     return (Unsafe) f.get(null);
diff --git a/test/004-checker-UnsafeTest18/expected.txt b/test/004-checker-UnsafeTest18/expected.txt
new file mode 100644
index 0000000..651da72
--- /dev/null
+++ b/test/004-checker-UnsafeTest18/expected.txt
@@ -0,0 +1,2 @@
+starting
+passed
diff --git a/test/004-checker-UnsafeTest18/info.txt b/test/004-checker-UnsafeTest18/info.txt
new file mode 100644
index 0000000..0fca5eb
--- /dev/null
+++ b/test/004-checker-UnsafeTest18/info.txt
@@ -0,0 +1 @@
+Test support for 1.8 sun.misc.Unsafe.
diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java
new file mode 100644
index 0000000..bb020b9
--- /dev/null
+++ b/test/004-checker-UnsafeTest18/src/Main.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+
+import sun.misc.Unsafe;
+
+/**
+ * Checker test on the 1.8 unsafe operations. Note, this is by no means an
+ * exhaustive unit test for these CAS (compare-and-swap) and fence operations.
+ * Instead, this test ensures the methods are recognized as intrinsic and behave
+ * as expected.
+ */
+public class Main {
+
+  private static final Unsafe unsafe = getUnsafe();
+
+  private static Thread[] sThreads = new Thread[10];
+
+  //
+  // Fields accessed by setters and adders.
+  //
+
+  public int i = 0;
+  public long l = 0;
+  public Object o = null;
+
+  //
+  // Setters.
+  //
+
+  /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetInt
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int set32(Object o, long offset, int newValue) {
+    return unsafe.getAndSetInt(o, offset, newValue);
+  }
+
+  /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetLong
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static long set64(Object o, long offset, long newValue) {
+    return unsafe.getAndSetLong(o, offset, newValue);
+  }
+
+  /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:l\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetObject
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static Object setObj(Object o, long offset, Object newValue) {
+    return unsafe.getAndSetObject(o, offset, newValue);
+  }
+
+  //
+  // Adders.
+  //
+
+  /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddInt
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int add32(Object o, long offset, int delta) {
+    return unsafe.getAndAddInt(o, offset, delta);
+  }
+
+  /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddLong
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static long add64(Object o, long offset, long delta) {
+    return unsafe.getAndAddLong(o, offset, delta);
+  }
+
+  //
+  // Fences (native).
+  //
+
+  /// CHECK-START: void Main.load() intrinsics_recognition (after)
+  /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence
+  //
+  /// CHECK-START: void Main.load() instruction_simplifier (after)
+  /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence
+  //
+  /// CHECK-START: void Main.load() instruction_simplifier (after)
+  /// CHECK-DAG: MemoryBarrier kind:LoadAny
+  private static void load() {
+    unsafe.loadFence();
+  }
+
+  /// CHECK-START: void Main.store() intrinsics_recognition (after)
+  /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence
+  //
+  /// CHECK-START: void Main.store() instruction_simplifier (after)
+  /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence
+  //
+  /// CHECK-START: void Main.store() instruction_simplifier (after)
+  /// CHECK-DAG: MemoryBarrier kind:AnyStore
+  private static void store() {
+    unsafe.storeFence();
+  }
+
+  /// CHECK-START: void Main.full() intrinsics_recognition (after)
+  /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence
+  //
+  /// CHECK-START: void Main.full() instruction_simplifier (after)
+  /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence
+  //
+  /// CHECK-START: void Main.full() instruction_simplifier (after)
+  /// CHECK-DAG: MemoryBarrier kind:AnyAny
+  private static void full() {
+    unsafe.fullFence();
+  }
+
+  //
+  // Thread fork/join.
+  //
+
+  private static void fork(Runnable r) {
+    for (int i = 0; i < 10; i++) {
+      sThreads[i] = new Thread(r);
+      sThreads[i].start();
+    }
+  }
+
+  private static void join() {
+    try {
+      for (int i = 0; i < 10; i++) {
+        sThreads[i].join();
+      }
+    } catch (InterruptedException e) {
+      throw new Error("Failed join: " + e);
+    }
+  }
+
+  //
+  // Driver.
+  //
+
+  public static void main(String[] args) {
+    System.out.println("starting");
+
+    final Main m = new Main();
+
+    // Get the offsets.
+
+    final long intOffset, longOffset, objOffset;
+    try {
+      Field intField = Main.class.getDeclaredField("i");
+      Field longField = Main.class.getDeclaredField("l");
+      Field objField = Main.class.getDeclaredField("o");
+
+      intOffset = unsafe.objectFieldOffset(intField);
+      longOffset = unsafe.objectFieldOffset(longField);
+      objOffset = unsafe.objectFieldOffset(objField);
+
+    } catch (NoSuchFieldException e) {
+      throw new Error("No offset: " + e);
+    }
+
+    // Some sanity within same thread.
+
+    set32(m, intOffset, 3);
+    expectEquals32(3, m.i);
+
+    set64(m, longOffset, 7L);
+    expectEquals64(7L, m.l);
+
+    setObj(m, objOffset, m);
+    expectEqualsObj(m, m.o);
+
+    add32(m, intOffset, 11);
+    expectEquals32(14, m.i);
+
+    add64(m, longOffset, 13L);
+    expectEquals64(20L, m.l);
+
+    // Some sanity on setters within different threads.
+
+    fork(new Runnable() {
+      public void run() {
+        for (int i = 0; i < 10; i++)
+          set32(m, intOffset, i);
+      }
+    });
+    join();
+    expectEquals32(9, m.i);  // one thread's last value wins
+
+    fork(new Runnable() {
+      public void run() {
+        for (int i = 0; i < 10; i++)
+          set64(m, longOffset, (long) (100 + i));
+      }
+    });
+    join();
+    expectEquals64(109L, m.l);  // one thread's last value wins
+
+    fork(new Runnable() {
+      public void run() {
+        for (int i = 0; i < 10; i++)
+          setObj(m, objOffset, sThreads[i]);
+      }
+    });
+    join();
+    expectEqualsObj(sThreads[9], m.o);  // one thread's last value wins
+
+    // Some sanity on adders within different threads.
+
+    fork(new Runnable() {
+      public void run() {
+        for (int i = 0; i < 10; i++)
+          add32(m, intOffset, i + 1);
+      }
+    });
+    join();
+    expectEquals32(559, m.i);  // all values accounted for
+
+    fork(new Runnable() {
+      public void run() {
+        for (int i = 0; i < 10; i++)
+          add64(m, longOffset, (long) (i + 1));
+      }
+    });
+    join();
+    expectEquals64(659L, m.l);  // all values accounted for
+
+    // TODO: the fences
+
+    System.out.println("passed");
+  }
+
+  // Use reflection to implement "Unsafe.getUnsafe()";
+  private static Unsafe getUnsafe() {
+    try {
+      Class<?> unsafeClass = Unsafe.class;
+      Field f = unsafeClass.getDeclaredField("theUnsafe");
+      f.setAccessible(true);
+      return (Unsafe) f.get(null);
+    } catch (Exception e) {
+      throw new Error("Cannot get Unsafe instance");
+    }
+  }
+
+  private static void expectEquals32(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEquals64(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEqualsObj(Object expected, Object result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/145-alloc-tracking-stress/src/Main.java b/test/145-alloc-tracking-stress/src/Main.java
index 752fdd9..418690a 100644
--- a/test/145-alloc-tracking-stress/src/Main.java
+++ b/test/145-alloc-tracking-stress/src/Main.java
@@ -1,5 +1,4 @@
 /*
-
  * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/test/462-checker-inlining-across-dex-files/multidex.jpp b/test/462-checker-inlining-across-dex-files/multidex.jpp
new file mode 100644
index 0000000..ae55456
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/multidex.jpp
@@ -0,0 +1,8 @@
+Main:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Main
+
+AAA:
+  @@com.android.jack.annotations.ForceInMainDex
+  class AAA
+
diff --git a/test/556-invoke-super/multidex.jpp b/test/556-invoke-super/multidex.jpp
new file mode 100644
index 0000000..fe01801
--- /dev/null
+++ b/test/556-invoke-super/multidex.jpp
@@ -0,0 +1,4 @@
+Main:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Main*
+
diff --git a/test/569-checker-pattern-replacement/multidex.jpp b/test/569-checker-pattern-replacement/multidex.jpp
new file mode 100644
index 0000000..cfc8ad1
--- /dev/null
+++ b/test/569-checker-pattern-replacement/multidex.jpp
@@ -0,0 +1,8 @@
+Main:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Main
+
+BaseInMainDex:
+  @@com.android.jack.annotations.ForceInMainDex
+  class BaseInMainDex
+
diff --git a/test/577-profile-foreign-dex/expected.txt b/test/577-profile-foreign-dex/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/577-profile-foreign-dex/expected.txt
diff --git a/test/577-profile-foreign-dex/info.txt b/test/577-profile-foreign-dex/info.txt
new file mode 100644
index 0000000..090db3f
--- /dev/null
+++ b/test/577-profile-foreign-dex/info.txt
@@ -0,0 +1 @@
+Check that we record the use of foreign dex files when profiles are enabled.
diff --git a/test/577-profile-foreign-dex/run b/test/577-profile-foreign-dex/run
new file mode 100644
index 0000000..ad57d14
--- /dev/null
+++ b/test/577-profile-foreign-dex/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} \
+  --runtime-option -Xjitsaveprofilinginfo \
+  --runtime-option -Xusejit:true \
+  "${@}"
diff --git a/test/577-profile-foreign-dex/src-ex/OtherDex.java b/test/577-profile-foreign-dex/src-ex/OtherDex.java
new file mode 100644
index 0000000..cba73b3
--- /dev/null
+++ b/test/577-profile-foreign-dex/src-ex/OtherDex.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+public class OtherDex {
+}
diff --git a/test/577-profile-foreign-dex/src/Main.java b/test/577-profile-foreign-dex/src/Main.java
new file mode 100644
index 0000000..0cd85b5
--- /dev/null
+++ b/test/577-profile-foreign-dex/src/Main.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+public class Main {
+
+  private static final String PROFILE_NAME = "primary.prof";
+  private static final String APP_DIR_PREFIX = "app_dir_";
+  private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex";
+  private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+  private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+  public static void main(String[] args) throws Exception {
+    File tmpFile = null;
+    File appDir = null;
+    File profileFile = null;
+    File foreignDexProfileDir = null;
+
+    try {
+      // Create the necessary files layout.
+      tmpFile = createTempFile();
+      appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName());
+      appDir.mkdir();
+      foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR);
+      foreignDexProfileDir.mkdir();
+      profileFile = createTempFile();
+
+      String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+
+      // Register the app with the runtime
+      VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(),
+             new String[] { codePath }, foreignDexProfileDir.getPath());
+
+      testMarkerForForeignDex(foreignDexProfileDir);
+      testMarkerForCodePath(foreignDexProfileDir);
+      testMarkerForApplicationDexFile(foreignDexProfileDir, appDir);
+    } finally {
+      if (tmpFile != null) {
+        tmpFile.delete();
+      }
+      if (profileFile != null) {
+        profileFile.delete();
+      }
+      if (foreignDexProfileDir != null) {
+        foreignDexProfileDir.delete();
+      }
+      if (appDir != null) {
+        appDir.delete();
+      }
+    }
+  }
+
+  // Verify we actually create a marker on disk for foreign dex files.
+  private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception {
+    String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar";
+    loadDexFile(foreignDex);
+    checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true);
+  }
+
+  // Verify we do not create a marker on disk for dex files path of the code path.
+  private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception {
+    String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+    loadDexFile(codePath);
+    checkMarker(foreignDexProfileDir, codePath, /* exists */ false);
+  }
+
+  private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir)
+      throws Exception {
+    // Copy the -ex jar to the application directory and load it from there.
+    // This will record duplicate class conflicts but we don't care for this use case.
+    File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar");
+    File appDex = new File(appDir, "appDex.jar");
+    try {
+      copyFile(foreignDex, appDex);
+
+      loadDexFile(appDex.getAbsolutePath());
+      checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false);
+    } finally {
+      if (appDex != null) {
+        appDex.delete();
+      }
+    }
+  }
+
+  private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) {
+    File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@'));
+    boolean result_ok = exists ? marker.exists() : !marker.exists();
+    if (!result_ok) {
+      throw new RuntimeException("Marker test failed for:" + marker.getPath());
+    }
+  }
+
+  private static void loadDexFile(String dexFile) throws Exception {
+    Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+    if (pathClassLoader == null) {
+        throw new RuntimeException("Couldn't find path class loader class");
+    }
+    Constructor constructor =
+        pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+    constructor.newInstance(
+            dexFile, ClassLoader.getSystemClassLoader());
+  }
+
+  private static class VMRuntime {
+    private static final Method registerAppInfoMethod;
+    static {
+      try {
+        Class c = Class.forName("dalvik.system.VMRuntime");
+        registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+            String.class, String.class, String[].class, String.class);
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    public static void registerAppInfo(String pkgName, String appDir,
+        String[] codePath, String foreignDexProfileDir) throws Exception {
+      registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir);
+    }
+  }
+
+  private static void copyFile(File fromFile, File toFile) throws Exception {
+    FileInputStream in = new FileInputStream(fromFile);
+    FileOutputStream out = new FileOutputStream(toFile);
+    try {
+      byte[] buffer = new byte[4096];
+      int bytesRead;
+      while ((bytesRead = in.read(buffer)) >= 0) {
+          out.write(buffer, 0, bytesRead);
+      }
+    } finally {
+      out.flush();
+      try {
+          out.getFD().sync();
+      } catch (IOException e) {
+      }
+      out.close();
+      in.close();
+    }
+  }
+
+  private static File createTempFile() throws Exception {
+    try {
+      return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+    } catch (IOException e) {
+      System.setProperty("java.io.tmpdir", "/data/local/tmp");
+      try {
+        return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+      } catch (IOException e2) {
+        System.setProperty("java.io.tmpdir", "/sdcard");
+        return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+      }
+    }
+  }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c4f0171..7036bdc 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -567,7 +567,9 @@
   537-checker-arraycopy
 
 # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).
-TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
+# 145: Test sometimes times out in read barrier configuration (b/27467554).
+TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \
+  145-alloc-tracking-stress
 
 ifeq ($(ART_USE_READ_BARRIER),true)
   ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
diff --git a/test/etc/default-build b/test/etc/default-build
index 6e855ec..5f78496 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -116,28 +116,33 @@
   SKIP_DX_MERGER="true"
 fi
 
-if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
-  # Jack does not support this configuration unless we specify how to partition the DEX file
-  # with a .jpp file.
-  USE_JACK="false"
-fi
-
 if [ ${USE_JACK} = "true" ]; then
   # Jack toolchain
   if [ "${HAS_SRC}" = "true" ]; then
-    ${JACK} ${JACK_ARGS} --output-jack src.jack src
-    imported_jack_files="--import src.jack"
+    if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
+      # Compile src and src-multidex in the same .jack file. We will apply multidex partitioning
+      # when creating the output .dex file.
+      ${JACK} ${JACK_ARGS} --output-jack src.jack src src src-multidex
+      jack_extra_args="${jack_extra_args} -D jack.dex.output.policy=minimal-multidex"
+      jack_extra_args="${jack_extra_args} -D jack.preprocessor=true"
+      jack_extra_args="${jack_extra_args} -D jack.preprocessor.file=multidex.jpp"
+    else
+      ${JACK} ${JACK_ARGS} --output-jack src.jack src
+    fi
+    jack_extra_args="${jack_extra_args} --import src.jack"
   fi
 
   if [ "${HAS_SRC2}" = "true" ]; then
     ${JACK} ${JACK_ARGS} --output-jack src2.jack src2
-    imported_jack_files="--import src2.jack ${imported_jack_files}"
+    # In case of duplicate classes, we want to take into account the classes from src2. Therefore
+    # we apply the 'keep-first' policy and import src2.jack file *before* the src.jack file.
+    jack_extra_args="${jack_extra_args} -D jack.import.type.policy=keep-first"
+    jack_extra_args="--import src2.jack ${jack_extra_args}"
   fi
 
-  # Compile jack files into a DEX file. We set jack.import.type.policy=keep-first to consider
-  # class definitions from src2 first.
+  # Compile jack files into a DEX file.
   if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
-    ${JACK} ${JACK_ARGS} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex .
+    ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex .
   fi
 else
   # Legacy toolchain with javac+dx
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index fab4599..46100ae 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -270,10 +270,5 @@
   description: "Only work with --mode=activity",
   result: EXEC_FAILED,
   names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ]
-},
-{
-  description: "Temporary suppressing while test is fixed",
-  result: EXEC_FAILED,
-  names: [ "org.apache.harmony.tests.java.util.ArrayDequeTest#test_forEachRemaining_iterator" ]
 }
 ]
diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt
index 19a61dc..95f0c2d 100644
--- a/tools/libcore_failures_concurrent_collector.txt
+++ b/tools/libcore_failures_concurrent_collector.txt
@@ -16,20 +16,5 @@
   names: ["jsr166.LinkedTransferQueueTest#testTransfer2",
           "jsr166.LinkedTransferQueueTest#testWaitingConsumer"],
   bug: 25883050
-},
-{
-  description: "libcore.java.lang.OldSystemTest#test_gc failure on armv8-concurrent-collector.",
-  result: EXEC_FAILED,
-  names: ["libcore.java.lang.OldSystemTest#test_gc"],
-  bug: 26155567
-},
-{
-  description: "TimeoutException on hammerhead-concurrent-collector",
-  result: EXEC_FAILED,
-  modes: [device],
-  names: ["libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
-          "libcore.java.text.SimpleDateFormatTest#testLocales",
-          "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries"],
-  bug: 26711853
 }
 ]
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 9e085b5..1e9c763 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -46,5 +46,13 @@
 adb logcat -p
 
 echo -e "${green}Kill stalled dalvikvm processes${nc}"
-processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
-for i in $processes; do adb shell kill -9 $i; done
+# 'ps' on M can sometimes hang.
+timeout 2s adb shell "ps"
+if [ $? = 124 ]; then
+  echo -e "${green}Rebooting device to fix 'ps'${nc}"
+  adb reboot
+  adb wait-for-device root
+else
+  processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
+  for i in $processes; do adb shell kill -9 $i; done
+fi