Merge "Use dex cache from compilation unit in HInstructionBuilder."
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 2a62643..d446539 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -169,11 +169,13 @@
   BuildLoop();
 
   // Populate the loop with instructions: set/get array with different types.
+  // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
+  // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
   HInstruction* get_array = new (&allocator_) HArrayGet(
-      parameter_, int_constant_, Primitive::kPrimInt, 0);
+      parameter_, int_constant_, Primitive::kPrimByte, 0);
   loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
   HInstruction* set_array = new (&allocator_) HArraySet(
-      parameter_, int_constant_, float_constant_, Primitive::kPrimFloat, 0);
+      parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0);
   loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
 
   EXPECT_EQ(get_array->GetBlock(), loop_body_);
@@ -187,11 +189,13 @@
   BuildLoop();
 
   // Populate the loop with instructions: set/get array with same types.
+  // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
+  // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
   HInstruction* get_array = new (&allocator_) HArrayGet(
-      parameter_, int_constant_, Primitive::kPrimFloat, 0);
+      parameter_, int_constant_, Primitive::kPrimByte, 0);
   loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
   HInstruction* set_array = new (&allocator_) HArraySet(
-      parameter_, get_array, float_constant_, Primitive::kPrimFloat, 0);
+      parameter_, get_array, float_constant_, Primitive::kPrimByte, 0);
   loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
 
   EXPECT_EQ(get_array->GetBlock(), loop_body_);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6f3e536..dc5a8fa 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1551,21 +1551,21 @@
   static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) {
     return is_volatile
         ? AllWritesAndReads()
-        : SideEffects(TypeFlag(type, kFieldWriteOffset));
+        : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset));
   }
 
   static SideEffects ArrayWriteOfType(Primitive::Type type) {
-    return SideEffects(TypeFlag(type, kArrayWriteOffset));
+    return SideEffects(TypeFlagWithAlias(type, kArrayWriteOffset));
   }
 
   static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) {
     return is_volatile
         ? AllWritesAndReads()
-        : SideEffects(TypeFlag(type, kFieldReadOffset));
+        : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset));
   }
 
   static SideEffects ArrayReadOfType(Primitive::Type type) {
-    return SideEffects(TypeFlag(type, kArrayReadOffset));
+    return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset));
   }
 
   static SideEffects CanTriggerGC() {
@@ -1692,6 +1692,23 @@
   static constexpr uint64_t kAllReads =
       ((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset;
 
+  // Work around the fact that HIR aliases I/F and J/D.
+  // TODO: remove this interceptor once HIR types are clean
+  static uint64_t TypeFlagWithAlias(Primitive::Type type, int offset) {
+    switch (type) {
+      case Primitive::kPrimInt:
+      case Primitive::kPrimFloat:
+        return TypeFlag(Primitive::kPrimInt, offset) |
+               TypeFlag(Primitive::kPrimFloat, offset);
+      case Primitive::kPrimLong:
+      case Primitive::kPrimDouble:
+        return TypeFlag(Primitive::kPrimLong, offset) |
+               TypeFlag(Primitive::kPrimDouble, offset);
+      default:
+        return TypeFlag(type, offset);
+    }
+  }
+
   // Translates type to bit flag.
   static uint64_t TypeFlag(Primitive::Type type, int offset) {
     CHECK_NE(type, Primitive::kPrimVoid);
@@ -5179,8 +5196,10 @@
             uint32_t dex_pc,
             SideEffects additional_side_effects = SideEffects::None())
       : HTemplateInstruction(
-          SideEffectsForArchRuntimeCalls(value->GetType()).Union(additional_side_effects),
-          dex_pc) {
+            SideEffects::ArrayWriteOfType(expected_component_type).Union(
+                SideEffectsForArchRuntimeCalls(value->GetType())).Union(
+                    additional_side_effects),
+            dex_pc) {
     SetPackedField<ExpectedComponentTypeField>(expected_component_type);
     SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot);
     SetPackedFlag<kFlagValueCanBeNull>(true);
@@ -5188,8 +5207,6 @@
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
     SetRawInputAt(2, value);
-    // We can now call component type logic to set correct type-based side effects.
-    AddSideEffects(SideEffects::ArrayWriteOfType(GetComponentType()));
   }
 
   bool NeedsEnvironment() const OVERRIDE {
diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc
index b01bc1c..9bbc354 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -148,19 +148,19 @@
   EXPECT_FALSE(any_write.MayDependOn(volatile_read));
 }
 
-TEST(SideEffectsTest, SameWidthTypesNoAlias) {
+TEST(SideEffectsTest, SameWidthTypes) {
   // Type I/F.
-  testNoWriteAndReadDependence(
+  testWriteAndReadDependence(
       SideEffects::FieldWriteOfType(Primitive::kPrimInt, /* is_volatile */ false),
       SideEffects::FieldReadOfType(Primitive::kPrimFloat, /* is_volatile */ false));
-  testNoWriteAndReadDependence(
+  testWriteAndReadDependence(
       SideEffects::ArrayWriteOfType(Primitive::kPrimInt),
       SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
   // Type L/D.
-  testNoWriteAndReadDependence(
+  testWriteAndReadDependence(
       SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false),
       SideEffects::FieldReadOfType(Primitive::kPrimDouble, /* is_volatile */ false));
-  testNoWriteAndReadDependence(
+  testWriteAndReadDependence(
       SideEffects::ArrayWriteOfType(Primitive::kPrimLong),
       SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
 }
@@ -216,32 +216,14 @@
       "||||||L|",
       SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str());
   EXPECT_STREQ(
-      "||DFJISCBZL|DFJISCBZL||DFJISCBZL|DFJISCBZL|",
-      SideEffects::FieldWriteOfType(Primitive::kPrimNot, true).ToString().c_str());
-  EXPECT_STREQ(
       "|||||Z||",
       SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str());
   EXPECT_STREQ(
-      "|||||C||",
-      SideEffects::ArrayWriteOfType(Primitive::kPrimChar).ToString().c_str());
-  EXPECT_STREQ(
-      "|||||S||",
-      SideEffects::ArrayWriteOfType(Primitive::kPrimShort).ToString().c_str());
-  EXPECT_STREQ(
       "|||B||||",
       SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str());
   EXPECT_STREQ(
-      "||D|||||",
+      "||DJ|||||",  // note: DJ alias
       SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str());
-  EXPECT_STREQ(
-      "||J|||||",
-      SideEffects::ArrayReadOfType(Primitive::kPrimLong).ToString().c_str());
-  EXPECT_STREQ(
-      "||F|||||",
-      SideEffects::ArrayReadOfType(Primitive::kPrimFloat).ToString().c_str());
-  EXPECT_STREQ(
-      "||I|||||",
-      SideEffects::ArrayReadOfType(Primitive::kPrimInt).ToString().c_str());
   SideEffects s = SideEffects::None();
   s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, /* is_volatile */ false));
   s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false));
@@ -249,7 +231,9 @@
   s = s.Union(SideEffects::FieldReadOfType(Primitive::kPrimInt, /* is_volatile */ false));
   s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
   s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
-  EXPECT_STREQ("||DF|I||S|JC|", s.ToString().c_str());
+  EXPECT_STREQ(
+      "||DFJI|FI||S|DJC|",   // note: DJ/FI alias.
+      s.ToString().c_str());
 }
 
 }  // namespace art
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index cbd0c40..214222d 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -51,11 +51,13 @@
   explicit ImgDiagDumper(std::ostream* os,
                          const ImageHeader& image_header,
                          const std::string& image_location,
-                         pid_t image_diff_pid)
+                         pid_t image_diff_pid,
+                         pid_t zygote_diff_pid)
       : os_(os),
         image_header_(image_header),
         image_location_(image_location),
-        image_diff_pid_(image_diff_pid) {}
+        image_diff_pid_(image_diff_pid),
+        zygote_diff_pid_(zygote_diff_pid) {}
 
   bool Dump() SHARED_REQUIRES(Locks::mutator_lock_) {
     std::ostream& os = *os_;
@@ -68,7 +70,7 @@
     bool ret = true;
     if (image_diff_pid_ >= 0) {
       os << "IMAGE DIFF PID (" << image_diff_pid_ << "): ";
-      ret = DumpImageDiff(image_diff_pid_);
+      ret = DumpImageDiff(image_diff_pid_, zygote_diff_pid_);
       os << "\n\n";
     } else {
       os << "IMAGE DIFF PID: disabled\n\n";
@@ -95,7 +97,8 @@
     return str.substr(idx + 1);
   }
 
-  bool DumpImageDiff(pid_t image_diff_pid) SHARED_REQUIRES(Locks::mutator_lock_) {
+  bool DumpImageDiff(pid_t image_diff_pid, pid_t zygote_diff_pid)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
     std::ostream& os = *os_;
 
     {
@@ -138,7 +141,7 @@
     }
 
     // Future idea: diff against zygote so we can ignore the shared dirty pages.
-    return DumpImageDiffMap(image_diff_pid, boot_map);
+    return DumpImageDiffMap(image_diff_pid, zygote_diff_pid, boot_map);
   }
 
   static std::string PrettyFieldValue(ArtField* field, mirror::Object* obj)
@@ -212,8 +215,74 @@
     std::vector<mirror::Object*> dirty_objects;
   };
 
+  void DiffObjectContents(mirror::Object* obj,
+                          uint8_t* remote_bytes,
+                          std::ostream& os) SHARED_REQUIRES(Locks::mutator_lock_) {
+    const char* tabs = "    ";
+    // Attempt to find fields for all dirty bytes.
+    mirror::Class* klass = obj->GetClass();
+    if (obj->IsClass()) {
+      os << tabs << "Class " << PrettyClass(obj->AsClass()) << " " << obj << "\n";
+    } else {
+      os << tabs << "Instance of " << PrettyClass(klass) << " " << obj << "\n";
+    }
+
+    std::unordered_set<ArtField*> dirty_instance_fields;
+    std::unordered_set<ArtField*> dirty_static_fields;
+    const uint8_t* obj_bytes = reinterpret_cast<const uint8_t*>(obj);
+    mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(remote_bytes);
+    for (size_t i = 0, count = obj->SizeOf(); i < count; ++i) {
+      if (obj_bytes[i] != remote_bytes[i]) {
+        ArtField* field = ArtField::FindInstanceFieldWithOffset</*exact*/false>(klass, i);
+        if (field != nullptr) {
+          dirty_instance_fields.insert(field);
+        } else if (obj->IsClass()) {
+          field = ArtField::FindStaticFieldWithOffset</*exact*/false>(obj->AsClass(), i);
+          if (field != nullptr) {
+            dirty_static_fields.insert(field);
+          }
+        }
+        if (field == nullptr) {
+          if (klass->IsArrayClass()) {
+            mirror::Class* component_type = klass->GetComponentType();
+            Primitive::Type primitive_type = component_type->GetPrimitiveType();
+            size_t component_size = Primitive::ComponentSize(primitive_type);
+            size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value();
+            if (i >= data_offset) {
+              os << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n";
+              // Skip to next element to prevent spam.
+              i += component_size - 1;
+              continue;
+            }
+          }
+          os << tabs << "No field for byte offset " << i << "\n";
+        }
+      }
+    }
+    // Dump different fields. TODO: Dump field contents.
+    if (!dirty_instance_fields.empty()) {
+      os << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n";
+      for (ArtField* field : dirty_instance_fields) {
+        os << tabs << PrettyField(field)
+           << " original=" << PrettyFieldValue(field, obj)
+           << " remote=" << PrettyFieldValue(field, remote_obj) << "\n";
+      }
+    }
+    if (!dirty_static_fields.empty()) {
+      os << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n";
+      for (ArtField* field : dirty_static_fields) {
+        os << tabs << PrettyField(field)
+           << " original=" << PrettyFieldValue(field, obj)
+           << " remote=" << PrettyFieldValue(field, remote_obj) << "\n";
+      }
+    }
+    os << "\n";
+  }
+
   // Look at /proc/$pid/mem and only diff the things from there
-  bool DumpImageDiffMap(pid_t image_diff_pid, const backtrace_map_t& boot_map)
+  bool DumpImageDiffMap(pid_t image_diff_pid,
+                        pid_t zygote_diff_pid,
+                        const backtrace_map_t& boot_map)
     SHARED_REQUIRES(Locks::mutator_lock_) {
     std::ostream& os = *os_;
     const size_t pointer_size = InstructionSetPointerSize(
@@ -272,6 +341,20 @@
       return false;
     }
 
+    std::vector<uint8_t> zygote_contents;
+    std::unique_ptr<File> zygote_map_file;
+    if (zygote_diff_pid != -1) {
+      std::string zygote_file_name =
+          StringPrintf("/proc/%ld/mem", static_cast<long>(zygote_diff_pid));  // NOLINT [runtime/int]
+      zygote_map_file.reset(OS::OpenFileForReading(zygote_file_name.c_str()));
+      // The boot map should be at the same address.
+      zygote_contents.resize(boot_map_size);
+      if (!zygote_map_file->PreadFully(&zygote_contents[0], boot_map_size, boot_map.start)) {
+        LOG(WARNING) << "Could not fully read zygote file " << zygote_file_name;
+        zygote_contents.clear();
+      }
+    }
+
     std::string page_map_file_name = StringPrintf(
         "/proc/%ld/pagemap", static_cast<long>(image_diff_pid));  // NOLINT [runtime/int]
     auto page_map_file = std::unique_ptr<File>(OS::OpenFileForReading(page_map_file_name.c_str()));
@@ -416,8 +499,11 @@
     // Look up local classes by their descriptor
     std::map<std::string, mirror::Class*> local_class_map;
 
-    // Use set to have sorted output.
-    std::set<mirror::Object*> dirty_objects;
+    // Objects that are dirty against the image (possibly shared or private dirty).
+    std::set<mirror::Object*> image_dirty_objects;
+
+    // Objects that are dirty against the zygote (probably private dirty).
+    std::set<mirror::Object*> zygote_dirty_objects;
 
     size_t dirty_object_bytes = 0;
     const uint8_t* begin_image_ptr = image_begin_unaligned;
@@ -454,17 +540,29 @@
 
       mirror::Class* klass = obj->GetClass();
 
-      bool different_object = false;
-
       // Check against the other object and see if they are different
       ptrdiff_t offset = current - begin_image_ptr;
       const uint8_t* current_remote = &remote_contents[offset];
       mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(
           const_cast<uint8_t*>(current_remote));
-      if (memcmp(current, current_remote, obj->SizeOf()) != 0) {
+
+      bool different_image_object = memcmp(current, current_remote, obj->SizeOf()) != 0;
+      if (different_image_object) {
+        bool different_zygote_object = false;
+        if (!zygote_contents.empty()) {
+          const uint8_t* zygote_ptr = &zygote_contents[offset];
+          different_zygote_object = memcmp(current, zygote_ptr, obj->SizeOf()) != 0;
+        }
+        if (different_zygote_object) {
+          // Different from zygote.
+          zygote_dirty_objects.insert(obj);
+        } else {
+          // Just different from iamge.
+          image_dirty_objects.insert(obj);
+        }
+
         different_objects++;
         dirty_object_bytes += obj->SizeOf();
-        dirty_objects.insert(obj);
 
         ++class_data[klass].dirty_object_count;
 
@@ -477,16 +575,13 @@
         }
         class_data[klass].dirty_object_byte_count += dirty_byte_count_per_object;
         class_data[klass].dirty_object_size_in_bytes += obj->SizeOf();
-
-        different_object = true;
-
         class_data[klass].dirty_objects.push_back(remote_obj);
       } else {
         ++class_data[klass].clean_object_count;
       }
 
       std::string descriptor = GetClassDescriptor(klass);
-      if (different_object) {
+      if (different_image_object) {
         if (klass->IsClassClass()) {
           // this is a "Class"
           mirror::Class* obj_as_class  = reinterpret_cast<mirror::Class*>(remote_obj);
@@ -558,69 +653,23 @@
     auto clean_object_class_values = SortByValueDesc<mirror::Class*, int, ClassData>(
         class_data, [](const ClassData& d) { return d.clean_object_count; });
 
-    os << "\n" << "  Dirty objects: " << dirty_objects.size() << "\n";
-    for (mirror::Object* obj : dirty_objects) {
-      const char* tabs = "    ";
-      // Attempt to find fields for all dirty bytes.
-      mirror::Class* klass = obj->GetClass();
-      if (obj->IsClass()) {
-        os << tabs << "Class " << PrettyClass(obj->AsClass()) << " " << obj << "\n";
-      } else {
-        os << tabs << "Instance of " << PrettyClass(klass) << " " << obj << "\n";
+    if (!zygote_dirty_objects.empty()) {
+      os << "\n" << "  Dirty objects compared to zygote (probably private dirty): "
+         << zygote_dirty_objects.size() << "\n";
+      for (mirror::Object* obj : zygote_dirty_objects) {
+        const uint8_t* obj_bytes = reinterpret_cast<const uint8_t*>(obj);
+        ptrdiff_t offset = obj_bytes - begin_image_ptr;
+        uint8_t* remote_bytes = &zygote_contents[offset];
+        DiffObjectContents(obj, remote_bytes, os);
       }
-
-      std::unordered_set<ArtField*> dirty_instance_fields;
-      std::unordered_set<ArtField*> dirty_static_fields;
+    }
+    os << "\n" << "  Dirty objects compared to image (private or shared dirty): "
+       << image_dirty_objects.size() << "\n";
+    for (mirror::Object* obj : image_dirty_objects) {
       const uint8_t* obj_bytes = reinterpret_cast<const uint8_t*>(obj);
       ptrdiff_t offset = obj_bytes - begin_image_ptr;
       uint8_t* remote_bytes = &remote_contents[offset];
-      mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(remote_bytes);
-      for (size_t i = 0, count = obj->SizeOf(); i < count; ++i) {
-        if (obj_bytes[i] != remote_bytes[i]) {
-          ArtField* field = ArtField::FindInstanceFieldWithOffset</*exact*/false>(klass, i);
-          if (field != nullptr) {
-            dirty_instance_fields.insert(field);
-          } else if (obj->IsClass()) {
-            field = ArtField::FindStaticFieldWithOffset</*exact*/false>(obj->AsClass(), i);
-            if (field != nullptr) {
-              dirty_static_fields.insert(field);
-            }
-          }
-          if (field == nullptr) {
-            if (klass->IsArrayClass()) {
-              mirror::Class* component_type = klass->GetComponentType();
-              Primitive::Type primitive_type = component_type->GetPrimitiveType();
-              size_t component_size = Primitive::ComponentSize(primitive_type);
-              size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value();
-              if (i >= data_offset) {
-                os << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n";
-                // Skip to next element to prevent spam.
-                i += component_size - 1;
-                continue;
-              }
-            }
-            os << tabs << "No field for byte offset " << i << "\n";
-          }
-        }
-      }
-      // Dump different fields. TODO: Dump field contents.
-      if (!dirty_instance_fields.empty()) {
-        os << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n";
-        for (ArtField* field : dirty_instance_fields) {
-          os << tabs << PrettyField(field)
-             << " original=" << PrettyFieldValue(field, obj)
-             << " remote=" << PrettyFieldValue(field, remote_obj) << "\n";
-        }
-      }
-      if (!dirty_static_fields.empty()) {
-        os << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n";
-        for (ArtField* field : dirty_static_fields) {
-          os << tabs << PrettyField(field)
-             << " original=" << PrettyFieldValue(field, obj)
-             << " remote=" << PrettyFieldValue(field, remote_obj) << "\n";
-        }
-      }
-      os << "\n";
+      DiffObjectContents(obj, remote_bytes, os);
     }
 
     os << "\n" << "  Dirty object count by class:\n";
@@ -959,11 +1008,15 @@
   const ImageHeader& image_header_;
   const std::string image_location_;
   pid_t image_diff_pid_;  // Dump image diff against boot.art if pid is non-negative
+  pid_t zygote_diff_pid_;  // Dump image diff against zygote boot.art if pid is non-negative
 
   DISALLOW_COPY_AND_ASSIGN(ImgDiagDumper);
 };
 
-static int DumpImage(Runtime* runtime, std::ostream* os, pid_t image_diff_pid) {
+static int DumpImage(Runtime* runtime,
+                     std::ostream* os,
+                     pid_t image_diff_pid,
+                     pid_t zygote_diff_pid) {
   ScopedObjectAccess soa(Thread::Current());
   gc::Heap* heap = runtime->GetHeap();
   std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
@@ -975,8 +1028,11 @@
       return EXIT_FAILURE;
     }
 
-    ImgDiagDumper img_diag_dumper(
-        os, image_header, image_space->GetImageLocation(), image_diff_pid);
+    ImgDiagDumper img_diag_dumper(os,
+                                  image_header,
+                                  image_space->GetImageLocation(),
+                                  image_diff_pid,
+                                  zygote_diff_pid);
     if (!img_diag_dumper.Dump()) {
       return EXIT_FAILURE;
     }
@@ -1004,6 +1060,13 @@
         *error_msg = "Image diff pid out of range";
         return kParseError;
       }
+    } else if (option.starts_with("--zygote-diff-pid=")) {
+      const char* zygote_diff_pid = option.substr(strlen("--zygote-diff-pid=")).data();
+
+      if (!ParseInt(zygote_diff_pid, &zygote_diff_pid_)) {
+        *error_msg = "Zygote diff pid out of range";
+        return kParseError;
+      }
     } else {
       return kParseUnknownArgument;
     }
@@ -1053,6 +1116,9 @@
     usage +=  // Optional.
         "  --image-diff-pid=<pid>: provide the PID of a process whose boot.art you want to diff.\n"
         "      Example: --image-diff-pid=$(pid zygote)\n"
+        "  --zygote-diff-pid=<pid>: provide the PID of the zygote whose boot.art you want to diff "
+        "against.\n"
+        "      Example: --zygote-diff-pid=$(pid zygote)\n"
         "\n";
 
     return usage;
@@ -1060,6 +1126,7 @@
 
  public:
   pid_t image_diff_pid_ = -1;
+  pid_t zygote_diff_pid_ = -1;
 };
 
 struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
@@ -1068,7 +1135,8 @@
 
     return DumpImage(runtime,
                      args_->os_,
-                     args_->image_diff_pid_) == EXIT_SUCCESS;
+                     args_->image_diff_pid_,
+                     args_->zygote_diff_pid_) == EXIT_SUCCESS;
   }
 };
 
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index dc101e5..9f771ba 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -36,6 +36,8 @@
 static const char* kImgDiagBootImage = "--boot-image";
 static const char* kImgDiagBinaryName = "imgdiag";
 
+static const char* kImgDiagZygoteDiffPid = "--zygote-diff-pid";
+
 // from kernel <include/linux/threads.h>
 #define PID_MAX_LIMIT (4*1024*1024)  // Upper bound. Most kernel configs will have smaller max pid.
 
@@ -90,17 +92,25 @@
 
     // Run imgdiag --image-diff-pid=$image_diff_pid and wait until it's done with a 0 exit code.
     std::string diff_pid_args;
+    std::string zygote_diff_pid_args;
     {
       std::stringstream diff_pid_args_ss;
       diff_pid_args_ss << kImgDiagDiffPid << "=" << image_diff_pid;
       diff_pid_args = diff_pid_args_ss.str();
     }
-    std::string boot_image_args;
     {
-      boot_image_args = boot_image_args + kImgDiagBootImage + "=" + boot_image;
+      std::stringstream zygote_pid_args_ss;
+      zygote_pid_args_ss << kImgDiagZygoteDiffPid << "=" << image_diff_pid;
+      zygote_diff_pid_args = zygote_pid_args_ss.str();
     }
+    std::string boot_image_args = std::string(kImgDiagBootImage) + "=" + boot_image;
 
-    std::vector<std::string> exec_argv = { file_path, diff_pid_args, boot_image_args };
+    std::vector<std::string> exec_argv = {
+        file_path,
+        diff_pid_args,
+        zygote_diff_pid_args,
+        boot_image_args
+    };
 
     return ::art::Exec(exec_argv, error_msg);
   }
diff --git a/runtime/Android.mk b/runtime/Android.mk
index c859079..aa12c83 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -106,7 +106,6 @@
   jit/debugger_interface.cc \
   jit/jit.cc \
   jit/jit_code_cache.cc \
-  jit/jit_instrumentation.cc \
   jit/offline_profiling_info.cc \
   jit/profiling_info.cc \
   jit/profile_saver.cc  \
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index d27d2f6..21725d3 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -20,7 +20,7 @@
 #if defined(__cplusplus)
 #include "art_method.h"
 #include "gc/allocator/rosalloc.h"
-#include "jit/jit_instrumentation.h"
+#include "jit/jit.h"
 #include "lock_word.h"
 #include "mirror/class.h"
 #include "mirror/string.h"
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 3a28422..ce7f62a 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -57,6 +57,7 @@
 // TODO: move all of the macro functionality into the DexCache class.
 class DexFile {
  public:
+  static const uint32_t kDefaultMethodsVersion = 37;
   static const uint8_t kDexMagic[];
   static constexpr size_t kNumDexVersions = 2;
   static constexpr size_t kDexVersionLen = 4;
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 681c5f9..3df4e98 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2465,7 +2465,7 @@
                                 GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
                                 field_access_flags,
                                 PrettyJavaAccessFlags(field_access_flags).c_str());
-      if (header_->GetVersion() >= 37) {
+      if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
         return false;
       } else {
         // Allow in older versions, but warn.
@@ -2480,7 +2480,7 @@
                                 GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
                                 field_access_flags,
                                 PrettyJavaAccessFlags(field_access_flags).c_str());
-      if (header_->GetVersion() >= 37) {
+      if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
         return false;
       } else {
         // Allow in older versions, but warn.
@@ -2628,12 +2628,16 @@
 
   // Interfaces are special.
   if ((class_access_flags & kAccInterface) != 0) {
-    // Non-static interface methods must be public.
-    if ((method_access_flags & (kAccPublic | kAccStatic)) == 0) {
+    // Non-static interface methods must be public or private.
+    uint32_t desired_flags = (kAccPublic | kAccStatic);
+    if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+      desired_flags |= kAccPrivate;
+    }
+    if ((method_access_flags & desired_flags) == 0) {
       *error_msg = StringPrintf("Interface virtual method %" PRIu32 "(%s) is not public",
           method_index,
           GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
-      if (header_->GetVersion() >= 37) {
+      if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
         return false;
       } else {
         // Allow in older versions, but warn.
@@ -2686,7 +2690,7 @@
         *error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract",
             method_index,
             GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
-        if (header_->GetVersion() >= 37) {
+        if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
           return false;
         } else {
           // Allow in older versions, but warn.
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index baf4afe..a432782 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -285,16 +285,19 @@
     }
 
     jit::Jit* jit = Runtime::Current()->GetJit();
-    if (jit != nullptr && jit->CanInvokeCompiledCode(method)) {
-      JValue result;
+    if (jit != nullptr) {
+      jit->MethodEntered(self, shadow_frame.GetMethod());
+      if (jit->CanInvokeCompiledCode(method)) {
+        JValue result;
 
-      // Pop the shadow frame before calling into compiled code.
-      self->PopShadowFrame();
-      ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
-      // Push the shadow frame back as the caller will expect it.
-      self->PushShadowFrame(&shadow_frame);
+        // Pop the shadow frame before calling into compiled code.
+        self->PopShadowFrame();
+        ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
+        // Push the shadow frame back as the caller will expect it.
+        self->PushShadowFrame(&shadow_frame);
 
-      return result;
+        return result;
+      }
     }
   }
 
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 19d971e..fb98175 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -34,6 +34,7 @@
 #include "dex_instruction-inl.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "handle_scope-inl.h"
+#include "jit/jit.h"
 #include "lambda/art_lambda_method.h"
 #include "lambda/box_table.h"
 #include "lambda/closure.h"
@@ -628,6 +629,15 @@
     result->SetJ(0);
     return false;
   } else {
+    jit::Jit* jit = Runtime::Current()->GetJit();
+    if (jit != nullptr) {
+      if (type == kVirtual || type == kInterface) {
+        jit->InvokeVirtualOrInterface(
+            self, receiver, sf_method, shadow_frame.GetDexPC(), called_method);
+      }
+      jit->AddSamples(self, sf_method, 1);
+    }
+    // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
     if (type == kVirtual || type == kInterface) {
       instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
       if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
@@ -667,7 +677,14 @@
     result->SetJ(0);
     return false;
   } else {
+    jit::Jit* jit = Runtime::Current()->GetJit();
+    if (jit != nullptr) {
+      jit->InvokeVirtualOrInterface(
+          self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
+      jit->AddSamples(self, shadow_frame.GetMethod(), 1);
+    }
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+    // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
     if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
       instrumentation->InvokeVirtualOrInterface(
           self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index ce698fb..c95af6f 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -22,7 +22,6 @@
 #include "experimental_flags.h"
 #include "interpreter_common.h"
 #include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
 #include "safe_math.h"
 
 #include <memory>  // std::unique_ptr
@@ -67,7 +66,9 @@
 
 #define BRANCH_INSTRUMENTATION(offset)                                                          \
   do {                                                                                          \
-    instrumentation->Branch(self, method, dex_pc, offset);                                      \
+    if (UNLIKELY(instrumentation->HasBranchListeners())) {                                      \
+      instrumentation->Branch(self, method, dex_pc, offset);                                    \
+    }                                                                                           \
     JValue result;                                                                              \
     if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) {           \
       return result;                                                                            \
@@ -76,8 +77,8 @@
 
 #define HOTNESS_UPDATE()                                                                       \
   do {                                                                                         \
-    if (jit_instrumentation_cache != nullptr) {                                                \
-      jit_instrumentation_cache->AddSamples(self, method, 1);                                  \
+    if (jit != nullptr) {                                                                      \
+      jit->AddSamples(self, method, 1);                                                        \
     }                                                                                          \
   } while (false)
 
@@ -195,10 +196,6 @@
   const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
   ArtMethod* method = shadow_frame.GetMethod();
   jit::Jit* jit = Runtime::Current()->GetJit();
-  jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr;
-  if (jit != nullptr) {
-    jit_instrumentation_cache = jit->GetInstrumentationCache();
-  }
 
   // Jump to first instruction.
   ADVANCE(0);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 442e191..ca1d635 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -18,7 +18,6 @@
 #include "experimental_flags.h"
 #include "interpreter_common.h"
 #include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
 #include "safe_math.h"
 
 #include <memory>  // std::unique_ptr
@@ -74,7 +73,9 @@
 
 #define BRANCH_INSTRUMENTATION(offset)                                                         \
   do {                                                                                         \
-    instrumentation->Branch(self, method, dex_pc, offset);                                     \
+    if (UNLIKELY(instrumentation->HasBranchListeners())) {                                     \
+      instrumentation->Branch(self, method, dex_pc, offset);                                   \
+    }                                                                                          \
     JValue result;                                                                             \
     if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) {          \
       if (interpret_one_instruction) {                                                         \
@@ -87,8 +88,8 @@
 
 #define HOTNESS_UPDATE()                                                                       \
   do {                                                                                         \
-    if (jit_instrumentation_cache != nullptr) {                                                \
-      jit_instrumentation_cache->AddSamples(self, method, 1);                                  \
+    if (jit != nullptr) {                                                                      \
+      jit->AddSamples(self, method, 1);                                                        \
     }                                                                                          \
   } while (false)
 
@@ -115,10 +116,6 @@
   uint16_t inst_data;
   ArtMethod* method = shadow_frame.GetMethod();
   jit::Jit* jit = Runtime::Current()->GetJit();
-  jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr;
-  if (jit != nullptr) {
-    jit_instrumentation_cache = jit->GetInstrumentationCache();
-  }
 
   // TODO: collapse capture-variable+create-lambda into one opcode, then we won't need
   // to keep this live for the scope of the entire function call.
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 32c45fc..f800683 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -20,8 +20,6 @@
 #include "interpreter/interpreter_common.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "mterp.h"
-#include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
 #include "debugger.h"
 
 namespace art {
@@ -652,10 +650,9 @@
   int32_t countdown_value = jit::kJitHotnessDisabled;
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (jit != nullptr) {
-    jit::JitInstrumentationCache* cache = jit->GetInstrumentationCache();
-    int32_t warm_threshold = cache->WarmMethodThreshold();
-    int32_t hot_threshold = cache->HotMethodThreshold();
-    int32_t osr_threshold = cache->OSRMethodThreshold();
+    int32_t warm_threshold = jit->WarmMethodThreshold();
+    int32_t hot_threshold = jit->HotMethodThreshold();
+    int32_t osr_threshold = jit->OSRMethodThreshold();
     if (hotness_count < warm_threshold) {
       countdown_value = warm_threshold - hotness_count;
     } else if (hotness_count < hot_threshold) {
@@ -666,7 +663,7 @@
       countdown_value = jit::kJitCheckForOSR;
     }
     if (jit::Jit::ShouldUsePriorityThreadWeight()) {
-      int32_t priority_thread_weight = cache->PriorityThreadWeight();
+      int32_t priority_thread_weight = jit->PriorityThreadWeight();
       countdown_value = std::min(countdown_value, countdown_value / priority_thread_weight);
     }
   }
@@ -692,7 +689,7 @@
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (jit != nullptr) {
     int16_t count = shadow_frame->GetCachedHotnessCountdown() - shadow_frame->GetHotnessCountdown();
-    jit->GetInstrumentationCache()->AddSamples(self, method, count);
+    jit->AddSamples(self, method, count);
   }
   return MterpSetUpHotnessCountdown(method, shadow_frame);
 }
@@ -705,7 +702,7 @@
   uint32_t dex_pc = shadow_frame->GetDexPC();
   jit::Jit* jit = Runtime::Current()->GetJit();
   if ((jit != nullptr) && (offset <= 0)) {
-    jit->GetInstrumentationCache()->AddSamples(self, method, 1);
+    jit->AddSamples(self, method, 1);
   }
   int16_t countdown_value = MterpSetUpHotnessCountdown(method, shadow_frame);
   if (countdown_value == jit::kJitCheckForOSR) {
@@ -725,7 +722,7 @@
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (offset <= 0) {
     // Keep updating hotness in case a compilation request was dropped.  Eventually it will retry.
-    jit->GetInstrumentationCache()->AddSamples(self, method, 1);
+    jit->AddSamples(self, method, 1);
   }
   // Assumes caller has already determined that an OSR check is appropriate.
   return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 3344346..4ba90c1 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -23,7 +23,6 @@
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "interpreter/interpreter.h"
 #include "jit_code_cache.h"
-#include "jit_instrumentation.h"
 #include "oat_file_manager.h"
 #include "oat_quick_method_header.h"
 #include "offline_profiling_info.h"
@@ -31,12 +30,15 @@
 #include "runtime.h"
 #include "runtime_options.h"
 #include "stack_map.h"
+#include "thread_list.h"
 #include "utils.h"
 
 namespace art {
 namespace jit {
 
 static constexpr bool kEnableOnStackReplacement = true;
+// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
+static constexpr int kJitPoolThreadPthreadPriority = 9;
 
 // JIT compiler
 void* Jit::jit_library_handle_= nullptr;
@@ -146,6 +148,16 @@
       << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
       << ", compile_threshold=" << options->GetCompileThreshold()
       << ", save_profiling_info=" << options->GetSaveProfilingInfo();
+
+
+  jit->hot_method_threshold_ = options->GetCompileThreshold();
+  jit->warm_method_threshold_ = options->GetWarmupThreshold();
+  jit->osr_method_threshold_ = options->GetOsrThreshold();
+
+  jit->CreateThreadPool();
+
+  // Notify native debugger about the classes already loaded before the creation of the jit.
+  jit->DumpTypeInfoForLoadedTypes(Runtime::Current()->GetClassLinker());
   return jit.release();
 }
 
@@ -233,13 +245,31 @@
 }
 
 void Jit::CreateThreadPool() {
-  CHECK(instrumentation_cache_.get() != nullptr);
-  instrumentation_cache_->CreateThreadPool();
+  // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
+  // is not null when we instrument.
+  thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
+  thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
+  thread_pool_->StartWorkers(Thread::Current());
 }
 
 void Jit::DeleteThreadPool() {
-  if (instrumentation_cache_.get() != nullptr) {
-    instrumentation_cache_->DeleteThreadPool(Thread::Current());
+  Thread* self = Thread::Current();
+  DCHECK(Runtime::Current()->IsShuttingDown(self));
+  if (thread_pool_ != nullptr) {
+    ThreadPool* cache = nullptr;
+    {
+      ScopedSuspendAll ssa(__FUNCTION__);
+      // Clear thread_pool_ field while the threads are suspended.
+      // A mutator in the 'AddSamples' method will check against it.
+      cache = thread_pool_.release();
+    }
+    cache->StopWorkers(self);
+    cache->RemoveAllTasks(self);
+    // We could just suspend all threads, but we know those threads
+    // will finish in a short period, so it's not worth adding a suspend logic
+    // here. Besides, this is only done for shutdown.
+    cache->Wait(self, false, false);
+    delete cache;
   }
 }
 
@@ -259,10 +289,7 @@
 }
 
 bool Jit::JitAtFirstUse() {
-  if (instrumentation_cache_ != nullptr) {
-    return instrumentation_cache_->HotMethodThreshold() == 0;
-  }
-  return false;
+  return HotMethodThreshold() == 0;
 }
 
 bool Jit::CanInvokeCompiledCode(ArtMethod* method) {
@@ -285,17 +312,6 @@
   }
 }
 
-void Jit::CreateInstrumentationCache(size_t compile_threshold,
-                                     size_t warmup_threshold,
-                                     size_t osr_threshold,
-                                     uint16_t priority_thread_weight) {
-  instrumentation_cache_.reset(
-      new jit::JitInstrumentationCache(compile_threshold,
-                                       warmup_threshold,
-                                       osr_threshold,
-                                       priority_thread_weight));
-}
-
 void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) {
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (jit != nullptr && jit->generate_debug_info_) {
@@ -480,5 +496,164 @@
   memory_use_.AddValue(bytes);
 }
 
+class JitCompileTask FINAL : public Task {
+ public:
+  enum TaskKind {
+    kAllocateProfile,
+    kCompile,
+    kCompileOsr
+  };
+
+  JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
+    ScopedObjectAccess soa(Thread::Current());
+    // Add a global ref to the class to prevent class unloading until compilation is done.
+    klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass());
+    CHECK(klass_ != nullptr);
+  }
+
+  ~JitCompileTask() {
+    ScopedObjectAccess soa(Thread::Current());
+    soa.Vm()->DeleteGlobalRef(soa.Self(), klass_);
+  }
+
+  void Run(Thread* self) OVERRIDE {
+    ScopedObjectAccess soa(self);
+    if (kind_ == kCompile) {
+      VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
+      if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) {
+        VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
+      }
+    } else if (kind_ == kCompileOsr) {
+      VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_);
+      if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) {
+        VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_);
+      }
+    } else {
+      DCHECK(kind_ == kAllocateProfile);
+      if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
+        VLOG(jit) << "Start profiling " << PrettyMethod(method_);
+      }
+    }
+  }
+
+  void Finalize() OVERRIDE {
+    delete this;
+  }
+
+ private:
+  ArtMethod* const method_;
+  const TaskKind kind_;
+  jobject klass_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
+};
+
+void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) {
+  if (thread_pool_ == nullptr) {
+    // Should only see this when shutting down.
+    DCHECK(Runtime::Current()->IsShuttingDown(self));
+    return;
+  }
+
+  if (method->IsClassInitializer() || method->IsNative()) {
+    // We do not want to compile such methods.
+    return;
+  }
+  DCHECK(thread_pool_ != nullptr);
+  DCHECK_GT(warm_method_threshold_, 0);
+  DCHECK_GT(hot_method_threshold_, warm_method_threshold_);
+  DCHECK_GT(osr_method_threshold_, hot_method_threshold_);
+  DCHECK_GE(priority_thread_weight_, 1);
+  DCHECK_LE(priority_thread_weight_, hot_method_threshold_);
+
+  int32_t starting_count = method->GetCounter();
+  if (Jit::ShouldUsePriorityThreadWeight()) {
+    count *= priority_thread_weight_;
+  }
+  int32_t new_count = starting_count + count;   // int32 here to avoid wrap-around;
+  if (starting_count < warm_method_threshold_) {
+    if (new_count >= warm_method_threshold_) {
+      bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
+      if (success) {
+        VLOG(jit) << "Start profiling " << PrettyMethod(method);
+      }
+
+      if (thread_pool_ == nullptr) {
+        // Calling ProfilingInfo::Create might put us in a suspended state, which could
+        // lead to the thread pool being deleted when we are shutting down.
+        DCHECK(Runtime::Current()->IsShuttingDown(self));
+        return;
+      }
+
+      if (!success) {
+        // We failed allocating. Instead of doing the collection on the Java thread, we push
+        // an allocation to a compiler thread, that will do the collection.
+        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
+      }
+    }
+    // Avoid jumping more than one state at a time.
+    new_count = std::min(new_count, hot_method_threshold_ - 1);
+  } else if (starting_count < hot_method_threshold_) {
+    if (new_count >= hot_method_threshold_) {
+      DCHECK(thread_pool_ != nullptr);
+      thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+    }
+    // Avoid jumping more than one state at a time.
+    new_count = std::min(new_count, osr_method_threshold_ - 1);
+  } else if (starting_count < osr_method_threshold_) {
+    if (new_count >= osr_method_threshold_) {
+      DCHECK(thread_pool_ != nullptr);
+      thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+    }
+  }
+  // Update hotness counter
+  method->SetCounter(new_count);
+}
+
+void Jit::MethodEntered(Thread* thread, ArtMethod* method) {
+  if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) {
+    // The compiler requires a ProfilingInfo object.
+    ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
+    JitCompileTask compile_task(method, JitCompileTask::kCompile);
+    compile_task.Run(thread);
+    return;
+  }
+
+  ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*));
+  // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it
+  // instead of interpreting the method.
+  // We avoid doing this if exit stubs are installed to not mess with the instrumentation.
+  // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
+  if ((profiling_info != nullptr) &&
+      (profiling_info->GetSavedEntryPoint() != nullptr) &&
+      !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
+    method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint());
+  } else {
+    AddSamples(thread, method, 1);
+  }
+}
+
+void Jit::InvokeVirtualOrInterface(Thread* thread,
+                                   mirror::Object* this_object,
+                                   ArtMethod* caller,
+                                   uint32_t dex_pc,
+                                   ArtMethod* callee ATTRIBUTE_UNUSED) {
+  ScopedAssertNoThreadSuspension ants(thread, __FUNCTION__);
+  DCHECK(this_object != nullptr);
+  ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*));
+  if (info != nullptr) {
+    // Since the instrumentation is marked from the declaring class we need to mark the card so
+    // that mod-union tables and card rescanning know about the update.
+    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass());
+    info->AddInvokeInfo(dex_pc, this_object->GetClass());
+  }
+}
+
+void Jit::WaitForCompilationToFinish(Thread* self) {
+  if (thread_pool_ != nullptr) {
+    thread_pool_->Wait(self, false, false);
+  }
+}
+
 }  // namespace jit
 }  // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index e212366..96f9608 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -34,9 +34,11 @@
 namespace jit {
 
 class JitCodeCache;
-class JitInstrumentationCache;
 class JitOptions;
 
+static constexpr int16_t kJitCheckForOSR = -1;
+static constexpr int16_t kJitHotnessDisabled = -2;
+
 class Jit {
  public:
   static constexpr bool kStressMode = kIsDebugBuild;
@@ -46,17 +48,16 @@
   static Jit* Create(JitOptions* options, std::string* error_msg);
   bool CompileMethod(ArtMethod* method, Thread* self, bool osr)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  void CreateInstrumentationCache(size_t compile_threshold,
-                                  size_t warmup_threshold,
-                                  size_t osr_threshold,
-                                  uint16_t priority_thread_weight);
   void CreateThreadPool();
+
   const JitCodeCache* GetCodeCache() const {
     return code_cache_.get();
   }
+
   JitCodeCache* GetCodeCache() {
     return code_cache_.get();
   }
+
   void DeleteThreadPool();
   // Dump interesting info: #methods compiled, code vs data size, compile / verify cumulative
   // loggers.
@@ -68,10 +69,39 @@
       REQUIRES(!lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  JitInstrumentationCache* GetInstrumentationCache() const {
-    return instrumentation_cache_.get();
+  size_t OSRMethodThreshold() const {
+    return osr_method_threshold_;
   }
 
+  size_t HotMethodThreshold() const {
+    return hot_method_threshold_;
+  }
+
+  size_t WarmMethodThreshold() const {
+    return warm_method_threshold_;
+  }
+
+  uint16_t PriorityThreadWeight() const {
+    return priority_thread_weight_;
+  }
+
+  // Wait until there is no more pending compilation tasks.
+  void WaitForCompilationToFinish(Thread* self);
+
+  // Profiling methods.
+  void MethodEntered(Thread* thread, ArtMethod* method)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  void AddSamples(Thread* self, ArtMethod* method, uint16_t samples)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  void InvokeVirtualOrInterface(Thread* thread,
+                                mirror::Object* this_object,
+                                ArtMethod* caller,
+                                uint32_t dex_pc,
+                                ArtMethod* callee)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   // 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).
@@ -137,11 +167,15 @@
   Histogram<uint64_t> memory_use_ GUARDED_BY(lock_);
   Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 
-  std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_;
   std::unique_ptr<jit::JitCodeCache> code_cache_;
 
   bool save_profiling_info_;
   static bool generate_debug_info_;
+  uint16_t hot_method_threshold_;
+  uint16_t warm_method_threshold_;
+  uint16_t osr_method_threshold_;
+  uint16_t priority_thread_weight_;
+  std::unique_ptr<ThreadPool> thread_pool_;
 
   DISALLOW_COPY_AND_ASSIGN(Jit);
 };
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
deleted file mode 100644
index b2c0c20..0000000
--- a/runtime/jit/jit_instrumentation.cc
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jit_instrumentation.h"
-
-#include "art_method-inl.h"
-#include "jit.h"
-#include "jit_code_cache.h"
-#include "scoped_thread_state_change.h"
-#include "thread_list.h"
-
-namespace art {
-namespace jit {
-
-// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
-static constexpr int kJitPoolThreadPthreadPriority = 9;
-
-class JitCompileTask FINAL : public Task {
- public:
-  enum TaskKind {
-    kAllocateProfile,
-    kCompile,
-    kCompileOsr
-  };
-
-  JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
-    ScopedObjectAccess soa(Thread::Current());
-    // Add a global ref to the class to prevent class unloading until compilation is done.
-    klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass());
-    CHECK(klass_ != nullptr);
-  }
-
-  ~JitCompileTask() {
-    ScopedObjectAccess soa(Thread::Current());
-    soa.Vm()->DeleteGlobalRef(soa.Self(), klass_);
-  }
-
-  void Run(Thread* self) OVERRIDE {
-    ScopedObjectAccess soa(self);
-    if (kind_ == kCompile) {
-      VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
-      if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) {
-        VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
-      }
-    } else if (kind_ == kCompileOsr) {
-      VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_);
-      if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) {
-        VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_);
-      }
-    } else {
-      DCHECK(kind_ == kAllocateProfile);
-      if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
-        VLOG(jit) << "Start profiling " << PrettyMethod(method_);
-      }
-    }
-  }
-
-  void Finalize() OVERRIDE {
-    delete this;
-  }
-
- private:
-  ArtMethod* const method_;
-  const TaskKind kind_;
-  jobject klass_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
-};
-
-JitInstrumentationCache::JitInstrumentationCache(uint16_t hot_method_threshold,
-                                                 uint16_t warm_method_threshold,
-                                                 uint16_t osr_method_threshold,
-                                                 uint16_t priority_thread_weight)
-    : hot_method_threshold_(hot_method_threshold),
-      warm_method_threshold_(warm_method_threshold),
-      osr_method_threshold_(osr_method_threshold),
-      priority_thread_weight_(priority_thread_weight),
-      listener_(this) {
-}
-
-void JitInstrumentationCache::CreateThreadPool() {
-  // Create the thread pool before setting the instrumentation, so that
-  // when the threads stopped being suspended, they can use it directly.
-  // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
-  // is not null when we instrument.
-  thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
-  thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
-  thread_pool_->StartWorkers(Thread::Current());
-  {
-    // Add Jit interpreter instrumentation, tells the interpreter when
-    // to notify the jit to compile something.
-    ScopedSuspendAll ssa(__FUNCTION__);
-    Runtime::Current()->GetInstrumentation()->AddListener(
-        &listener_, JitInstrumentationListener::kJitEvents);
-  }
-}
-
-void JitInstrumentationCache::DeleteThreadPool(Thread* self) {
-  DCHECK(Runtime::Current()->IsShuttingDown(self));
-  if (thread_pool_ != nullptr) {
-    // First remove the listener, to avoid having mutators enter
-    // 'AddSamples'.
-    ThreadPool* cache = nullptr;
-    {
-      ScopedSuspendAll ssa(__FUNCTION__);
-      Runtime::Current()->GetInstrumentation()->RemoveListener(
-          &listener_, JitInstrumentationListener::kJitEvents);
-      // Clear thread_pool_ field while the threads are suspended.
-      // A mutator in the 'AddSamples' method will check against it.
-      cache = thread_pool_.release();
-    }
-    cache->StopWorkers(self);
-    cache->RemoveAllTasks(self);
-    // We could just suspend all threads, but we know those threads
-    // will finish in a short period, so it's not worth adding a suspend logic
-    // here. Besides, this is only done for shutdown.
-    cache->Wait(self, false, false);
-    delete cache;
-  }
-}
-
-void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, uint16_t count) {
-  // Since we don't have on-stack replacement, some methods can remain in the interpreter longer
-  // than we want resulting in samples even after the method is compiled.  Also, if the
-  // jit is no longer interested in hotness samples because we're shutting down, just return.
-  if (method->IsClassInitializer() || method->IsNative() || (thread_pool_ == nullptr)) {
-    if (thread_pool_ == nullptr) {
-      // Should only see this when shutting down.
-      DCHECK(Runtime::Current()->IsShuttingDown(self));
-    }
-    return;
-  }
-  DCHECK(thread_pool_ != nullptr);
-  DCHECK_GT(warm_method_threshold_, 0);
-  DCHECK_GT(hot_method_threshold_, warm_method_threshold_);
-  DCHECK_GT(osr_method_threshold_, hot_method_threshold_);
-  DCHECK_GE(priority_thread_weight_, 1);
-  DCHECK_LE(priority_thread_weight_, hot_method_threshold_);
-
-  int32_t starting_count = method->GetCounter();
-  if (Jit::ShouldUsePriorityThreadWeight()) {
-    count *= priority_thread_weight_;
-  }
-  int32_t new_count = starting_count + count;   // int32 here to avoid wrap-around;
-  if (starting_count < warm_method_threshold_) {
-    if (new_count >= warm_method_threshold_) {
-      bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
-      if (success) {
-        VLOG(jit) << "Start profiling " << PrettyMethod(method);
-      }
-
-      if (thread_pool_ == nullptr) {
-        // Calling ProfilingInfo::Create might put us in a suspended state, which could
-        // lead to the thread pool being deleted when we are shutting down.
-        DCHECK(Runtime::Current()->IsShuttingDown(self));
-        return;
-      }
-
-      if (!success) {
-        // We failed allocating. Instead of doing the collection on the Java thread, we push
-        // an allocation to a compiler thread, that will do the collection.
-        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
-      }
-    }
-    // Avoid jumping more than one state at a time.
-    new_count = std::min(new_count, hot_method_threshold_ - 1);
-  } else if (starting_count < hot_method_threshold_) {
-    if (new_count >= hot_method_threshold_) {
-      DCHECK(thread_pool_ != nullptr);
-      thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
-    }
-    // Avoid jumping more than one state at a time.
-    new_count = std::min(new_count, osr_method_threshold_ - 1);
-  } else if (starting_count < osr_method_threshold_) {
-    if (new_count >= osr_method_threshold_) {
-      DCHECK(thread_pool_ != nullptr);
-      thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
-    }
-  }
-  // Update hotness counter
-  method->SetCounter(new_count);
-}
-
-JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* cache)
-    : instrumentation_cache_(cache) {
-  CHECK(instrumentation_cache_ != nullptr);
-}
-
-void JitInstrumentationListener::MethodEntered(Thread* thread,
-                                               mirror::Object* /*this_object*/,
-                                               ArtMethod* method,
-                                               uint32_t /*dex_pc*/) {
-  if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) {
-    // The compiler requires a ProfilingInfo object.
-    ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
-    JitCompileTask compile_task(method, JitCompileTask::kCompile);
-    compile_task.Run(thread);
-    return;
-  }
-
-  ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*));
-  // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it
-  // instead of interpreting the method.
-  // We avoid doing this if exit stubs are installed to not mess with the instrumentation.
-  // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
-  if ((profiling_info != nullptr) &&
-      (profiling_info->GetSavedEntryPoint() != nullptr) &&
-      !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
-    method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint());
-  } else {
-    instrumentation_cache_->AddSamples(thread, method, 1);
-  }
-}
-
-void JitInstrumentationListener::Branch(Thread* thread,
-                                        ArtMethod* method,
-                                        uint32_t dex_pc ATTRIBUTE_UNUSED,
-                                        int32_t dex_pc_offset) {
-  if (dex_pc_offset < 0) {
-    // Increment method hotness if it is a backward branch.
-    instrumentation_cache_->AddSamples(thread, method, 1);
-  }
-}
-
-void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread,
-                                                          mirror::Object* this_object,
-                                                          ArtMethod* caller,
-                                                          uint32_t dex_pc,
-                                                          ArtMethod* callee ATTRIBUTE_UNUSED) {
-  // We make sure we cannot be suspended, as the profiling info can be concurrently deleted.
-  instrumentation_cache_->AddSamples(thread, caller, 1);
-  DCHECK(this_object != nullptr);
-  ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*));
-  if (info != nullptr) {
-    // Since the instrumentation is marked from the declaring class we need to mark the card so
-    // that mod-union tables and card rescanning know about the update.
-    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass());
-    info->AddInvokeInfo(dex_pc, this_object->GetClass());
-  }
-}
-
-void JitInstrumentationCache::WaitForCompilationToFinish(Thread* self) {
-  if (thread_pool_ != nullptr) {
-    thread_pool_->Wait(self, false, false);
-  }
-}
-
-}  // namespace jit
-}  // namespace art
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
deleted file mode 100644
index d0545f8..0000000
--- a/runtime/jit/jit_instrumentation.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
-#define ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
-
-#include <unordered_map>
-
-#include "instrumentation.h"
-
-#include "atomic.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "gc_root.h"
-#include "jni.h"
-#include "object_callbacks.h"
-#include "thread_pool.h"
-
-namespace art {
-namespace mirror {
-  class Object;
-  class Throwable;
-}  // namespace mirror
-class ArtField;
-class ArtMethod;
-union JValue;
-class Thread;
-
-namespace jit {
-static constexpr int16_t kJitCheckForOSR = -1;
-static constexpr int16_t kJitHotnessDisabled = -2;
-
-class JitInstrumentationCache;
-
-class JitInstrumentationListener : public instrumentation::InstrumentationListener {
- public:
-  explicit JitInstrumentationListener(JitInstrumentationCache* cache);
-
-  void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
-                     ArtMethod* method, uint32_t /*dex_pc*/)
-      OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
-
-  void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                    ArtMethod* /*method*/, uint32_t /*dex_pc*/,
-                    const JValue& /*return_value*/)
-      OVERRIDE { }
-  void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                    ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { }
-  void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                 ArtMethod* /*method*/, uint32_t /*dex_pc*/,
-                 ArtField* /*field*/) OVERRIDE { }
-  void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/,
-                    ArtMethod* /*method*/, uint32_t /*dex_pc*/,
-                    ArtField* /*field*/, const JValue& /*field_value*/)
-      OVERRIDE { }
-  void ExceptionCaught(Thread* /*thread*/,
-                       mirror::Throwable* /*exception_object*/) OVERRIDE { }
-
-  void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/,
-                  ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
-
-  void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset)
-      OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
-
-  void InvokeVirtualOrInterface(Thread* thread,
-                                mirror::Object* this_object,
-                                ArtMethod* caller,
-                                uint32_t dex_pc,
-                                ArtMethod* callee)
-      OVERRIDE
-      REQUIRES(Roles::uninterruptible_)
-      SHARED_REQUIRES(Locks::mutator_lock_);
-
-  static constexpr uint32_t kJitEvents =
-      instrumentation::Instrumentation::kMethodEntered |
-      instrumentation::Instrumentation::kInvokeVirtualOrInterface;
-
- private:
-  JitInstrumentationCache* const instrumentation_cache_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationListener);
-};
-
-// Keeps track of which methods are hot.
-class JitInstrumentationCache {
- public:
-  JitInstrumentationCache(uint16_t hot_method_threshold,
-                          uint16_t warm_method_threshold,
-                          uint16_t osr_method_threshold,
-                          uint16_t priority_thread_weight);
-  void AddSamples(Thread* self, ArtMethod* method, uint16_t samples)
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  void CreateThreadPool();
-  void DeleteThreadPool(Thread* self);
-
-  size_t OSRMethodThreshold() const {
-    return osr_method_threshold_;
-  }
-
-  size_t HotMethodThreshold() const {
-    return hot_method_threshold_;
-  }
-
-  size_t WarmMethodThreshold() const {
-    return warm_method_threshold_;
-  }
-
-  size_t PriorityThreadWeight() const {
-    return priority_thread_weight_;
-  }
-
-  // Wait until there is no more pending compilation tasks.
-  void WaitForCompilationToFinish(Thread* self);
-
- private:
-  uint16_t hot_method_threshold_;
-  uint16_t warm_method_threshold_;
-  uint16_t osr_method_threshold_;
-  uint16_t priority_thread_weight_;
-  JitInstrumentationListener listener_;
-  std::unique_ptr<ThreadPool> thread_pool_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache);
-};
-
-}  // namespace jit
-}  // namespace art
-
-#endif  // ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 37bb4c1..2489e45 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1921,16 +1921,7 @@
   }
   std::string error_msg;
   jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
-  if (jit_.get() != nullptr) {
-    jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(),
-                                     jit_options_->GetWarmupThreshold(),
-                                     jit_options_->GetOsrThreshold(),
-                                     jit_options_->GetPriorityThreadWeight());
-    jit_->CreateThreadPool();
-
-    // Notify native debugger about the classes already loaded before the creation of the jit.
-    jit_->DumpTypeInfoForLoadedTypes(GetClassLinker());
-  } else {
+  if (jit_.get() == nullptr) {
     LOG(WARNING) << "Failed to create JIT " << error_msg;
   }
 }
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 83da6b7..d5319fd 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -790,9 +790,16 @@
         } else if (method_access_flags_ & kAccFinal) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods";
           return false;
-        } else if (!(method_access_flags_ & kAccPublic)) {
-          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members";
-          return false;
+        } else {
+          uint32_t access_flag_options = kAccPublic;
+          if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+            access_flag_options |= kAccPrivate;
+          }
+          if (!(method_access_flags_ & access_flag_options)) {
+            Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+                << "interfaces may not have protected or package-private members";
+            return false;
+          }
         }
       }
     }
@@ -3794,9 +3801,12 @@
   // Note: this check must be after the initializer check, as those are required to fail a class,
   //       while this check implies an IncompatibleClassChangeError.
   if (klass->IsInterface()) {
-    // methods called on interfaces should be invoke-interface, invoke-super, or invoke-static.
+    // methods called on interfaces should be invoke-interface, invoke-super, invoke-direct (if
+    // dex file version is 37 or greater), or invoke-static.
     if (method_type != METHOD_INTERFACE &&
         method_type != METHOD_STATIC &&
+        ((dex_file_->GetVersion() < DexFile::kDefaultMethodsVersion) ||
+         method_type != METHOD_DIRECT) &&
         method_type != METHOD_SUPER) {
       Fail(VERIFY_ERROR_CLASS_CHANGE)
           << "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_)
diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc
index d913efe..bbbb0a6 100644
--- a/test/141-class-unload/jni_unload.cc
+++ b/test/141-class-unload/jni_unload.cc
@@ -19,7 +19,6 @@
 #include <iostream>
 
 #include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
 #include "runtime.h"
 #include "thread-inl.h"
 
@@ -29,7 +28,7 @@
 extern "C" JNIEXPORT void JNICALL Java_IntHolder_waitForCompilation(JNIEnv*, jclass) {
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (jit != nullptr) {
-    jit->GetInstrumentationCache()->WaitForCompilationToFinish(Thread::Current());
+    jit->WaitForCompilationToFinish(Thread::Current());
   }
 }
 
diff --git a/test/525-checker-arrays-and-fields/expected.txt b/test/525-checker-arrays-and-fields/expected.txt
index b0aad4d..e69de29 100644
--- a/test/525-checker-arrays-and-fields/expected.txt
+++ b/test/525-checker-arrays-and-fields/expected.txt
@@ -1 +0,0 @@
-passed
diff --git a/test/525-checker-arrays-and-fields/src/Main.java b/test/525-checker-arrays-and-fields/src/Main.java
index b3fbf44..a635a51 100644
--- a/test/525-checker-arrays-and-fields/src/Main.java
+++ b/test/525-checker-arrays-and-fields/src/Main.java
@@ -759,87 +759,12 @@
   }
 
   //
-  // Misc. tests on false cross-over loops with data types (I/F and J/D) that used
-  // to be aliased in an older version of the compiler. This alias has been removed,
-  // however, which enables hoisting the invariant array reference.
-  //
-
-  /// CHECK-START: void Main.FalseCrossOverLoop1() licm (before)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  /// CHECK-START: void Main.FalseCrossOverLoop1() licm (after)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:none
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  private static void FalseCrossOverLoop1() {
-    sArrF[20] = -1;
-    for (int i = 0; i < sArrI.length; i++) {
-      sArrI[i] = (int) sArrF[20] - 2;
-    }
-  }
-
-  /// CHECK-START: void Main.FalseCrossOverLoop2() licm (before)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  /// CHECK-START: void Main.FalseCrossOverLoop2() licm (after)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:none
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  private static void FalseCrossOverLoop2() {
-    sArrI[20] = -2;
-    for (int i = 0; i < sArrF.length; i++) {
-      sArrF[i] = sArrI[20] - 2;
-    }
-  }
-
-  /// CHECK-START: void Main.FalseCrossOverLoop3() licm (before)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  /// CHECK-START: void Main.FalseCrossOverLoop3() licm (after)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:none
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  private static void FalseCrossOverLoop3() {
-    sArrD[20] = -3;
-    for (int i = 0; i < sArrJ.length; i++) {
-      sArrJ[i] = (long) sArrD[20] - 2;
-    }
-  }
-
-  /// CHECK-START: void Main.FalseCrossOverLoop4() licm (before)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  /// CHECK-START: void Main.FalseCrossOverLoop4() licm (after)
-  /// CHECK-DAG: ArraySet loop:none
-  /// CHECK-DAG: ArrayGet loop:none
-  /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
-  private static void FalseCrossOverLoop4() {
-    sArrJ[20] = -4;
-    for (int i = 0; i < sArrD.length; i++) {
-      sArrD[i] = sArrJ[20] - 2;
-    }
-  }
-
-  //
   // Driver and testers.
   //
 
   public static void main(String[] args) {
     DoStaticTests();
     new Main().DoInstanceTests();
-    System.out.println("passed");
   }
 
   private static void DoStaticTests() {
@@ -978,23 +903,6 @@
     for (int i = 0; i < sArrL.length; i++) {
       expectEquals(i <= 20 ? anObject : anotherObject, sArrL[i]);
     }
-    // Misc. tests.
-    FalseCrossOverLoop1();
-    for (int i = 0; i < sArrI.length; i++) {
-      expectEquals(-3, sArrI[i]);
-    }
-    FalseCrossOverLoop2();
-    for (int i = 0; i < sArrF.length; i++) {
-      expectEquals(-4, sArrF[i]);
-    }
-    FalseCrossOverLoop3();
-    for (int i = 0; i < sArrJ.length; i++) {
-      expectEquals(-5, sArrJ[i]);
-    }
-    FalseCrossOverLoop4();
-    for (int i = 0; i < sArrD.length; i++) {
-      expectEquals(-6, sArrD[i]);
-    }
   }
 
   private void DoInstanceTests() {
diff --git a/test/955-lambda-smali/build b/test/955-lambda-smali/build
new file mode 100755
index 0000000..14230c2
--- /dev/null
+++ b/test/955-lambda-smali/build
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@" --experimental default-methods
diff --git a/test/975-iface-private/build b/test/975-iface-private/build
new file mode 100755
index 0000000..14230c2
--- /dev/null
+++ b/test/975-iface-private/build
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@" --experimental default-methods
diff --git a/test/975-iface-private/expected.txt b/test/975-iface-private/expected.txt
new file mode 100644
index 0000000..908a8f2
--- /dev/null
+++ b/test/975-iface-private/expected.txt
@@ -0,0 +1,4 @@
+Saying hi from class
+HELLO!
+Saying hi from interface
+HELLO!
diff --git a/test/975-iface-private/info.txt b/test/975-iface-private/info.txt
new file mode 100644
index 0000000..d5a8d3f
--- /dev/null
+++ b/test/975-iface-private/info.txt
@@ -0,0 +1,5 @@
+Smali-based tests for experimental interface private methods.
+
+This test cannot be run with --jvm.
+
+This test checks that synthetic private methods in interfaces work correctly.
diff --git a/test/975-iface-private/smali/Iface.smali b/test/975-iface-private/smali/Iface.smali
new file mode 100644
index 0000000..a9a44d1
--- /dev/null
+++ b/test/975-iface-private/smali/Iface.smali
@@ -0,0 +1,45 @@
+
+# /*
+#  * Copyright (C) 2015 The Android Open Source Project
+#  *
+#  * Licensed under the Apache License, Version 2.0 (the "License");
+#  * you may not use this file except in compliance with the License.
+#  * You may obtain a copy of the License at
+#  *
+#  *      http://www.apache.org/licenses/LICENSE-2.0
+#  *
+#  * Unless required by applicable law or agreed to in writing, software
+#  * distributed under the License is distributed on an "AS IS" BASIS,
+#  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  * See the License for the specific language governing permissions and
+#  * limitations under the License.
+#  */
+#
+# public interface Iface {
+#   public default void sayHi() {
+#     System.out.println(getHiWords());
+#   }
+#
+#   // Synthetic method
+#   private String getHiWords() {
+#     return "HELLO!";
+#   }
+# }
+
+.class public abstract interface LIface;
+.super Ljava/lang/Object;
+
+.method public sayHi()V
+    .locals 2
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-direct {p0}, LIface;->getHiWords()Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+    return-void
+.end method
+
+.method private synthetic getHiWords()Ljava/lang/String;
+    .locals 1
+    const-string v0, "HELLO!"
+    return-object v0
+.end method
diff --git a/test/975-iface-private/smali/Main.smali b/test/975-iface-private/smali/Main.smali
new file mode 100644
index 0000000..dbde203
--- /dev/null
+++ b/test/975-iface-private/smali/Main.smali
@@ -0,0 +1,71 @@
+# /*
+#  * Copyright (C) 2015 The Android Open Source Project
+#  *
+#  * Licensed under the Apache License, Version 2.0 (the "License");
+#  * you may not use this file except in compliance with the License.
+#  * You may obtain a copy of the License at
+#  *
+#  *      http://www.apache.org/licenses/LICENSE-2.0
+#  *
+#  * Unless required by applicable law or agreed to in writing, software
+#  * distributed under the License is distributed on an "AS IS" BASIS,
+#  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  * See the License for the specific language governing permissions and
+#  * limitations under the License.
+#  */
+#
+# class Main implements Iface {
+#   public static void main(String[] args) {
+#     Main m = new Main();
+#     sayHiMain(m);
+#     sayHiIface(m);
+#   }
+#   public static void sayHiMain(Main m) {
+#     System.out.println("Saying hi from class");
+#     m.sayHi();
+#   }
+#   public static void sayHiIface(Iface m) {
+#     System.out.println("Saying hi from interface");
+#     m.sayHi();
+#   }
+# }
+.class public LMain;
+.super Ljava/lang/Object;
+.implements LIface;
+
+.method public constructor <init>()V
+    .registers 1
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 2
+    new-instance v0, LMain;
+    invoke-direct {v0}, LMain;-><init>()V
+
+    invoke-static {v0}, LMain;->sayHiMain(LMain;)V
+    invoke-static {v0}, LMain;->sayHiIface(LIface;)V
+
+    return-void
+.end method
+
+.method public static sayHiMain(LMain;)V
+    .locals 2
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    const-string v1, "Saying hi from class"
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+    invoke-virtual {p0}, LMain;->sayHi()V
+    return-void
+.end method
+
+.method public static sayHiIface(LIface;)V
+    .locals 2
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    const-string v1, "Saying hi from interface"
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+    invoke-interface {p0}, LIface;->sayHi()V
+    return-void
+.end method
diff --git a/test/etc/default-build b/test/etc/default-build
index 3d84821..962ae38 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -69,10 +69,13 @@
 JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 
+declare -A SMALI_EXPERIMENTAL_ARGS
+SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"
+
 while true; do
   if [ "x$1" = "x--dx-option" ]; then
     shift
-    option="$1"
+    on="$1"
     DX_FLAGS="${DX_FLAGS} $option"
     shift
   elif [ "x$1" = "x--jvm" ]; then
@@ -110,6 +113,7 @@
 # Add args from the experimental mappings.
 for experiment in ${EXPERIMENTAL}; do
   JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}"
+  SMALI_ARGS="${SMALI_ARGS} ${SMALI_EXPERIMENTAL_ARGS[${experiment}]}"
 done
 
 if [ -e classes.dex ]; then
diff --git a/test/run-test b/test/run-test
index 01464cd..013fc63 100755
--- a/test/run-test
+++ b/test/run-test
@@ -46,7 +46,7 @@
 export DEX_LOCATION=/data/run-test/${test_dir}
 export NEED_DEX="true"
 export USE_JACK="true"
-export SMALI_ARGS="--experimental --api-level 23"
+export SMALI_ARGS="--experimental"
 
 # If dx was not set by the environment variable, assume it is in the path.
 if [ -z "$DX" ]; then