Merge "Do not use -DUSE_*MALLOC for host build"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 2c552e6..05cfc42 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -33,7 +33,13 @@
 ART_BUILD_TARGET_DEBUG ?= true
 ART_BUILD_HOST_NDEBUG ?= true
 ART_BUILD_HOST_DEBUG ?= true
-ART_BUILD_HOST_STATIC ?= true
+
+# Enable the static builds only for checkbuilds.
+ifneq (,$(filter checkbuild,$(MAKECMDGOALS)))
+  ART_BUILD_HOST_STATIC ?= true
+else
+  ART_BUILD_HOST_STATIC ?= false
+endif
 
 # Asan does not support static linkage
 ifdef SANITIZE_HOST
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 63ad9cf..4850e6c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -165,6 +165,7 @@
   runtime/base/hex_dump_test.cc \
   runtime/base/histogram_test.cc \
   runtime/base/mutex_test.cc \
+  runtime/base/out_test.cc \
   runtime/base/scoped_flock_test.cc \
   runtime/base/stringprintf_test.cc \
   runtime/base/time_utils_test.cc \
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index c70e12d..0c0c3df 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -64,14 +64,14 @@
     core_compile_options += --compiler-filter=interpret-only
     core_infix := -interpreter
   endif
-  ifeq ($(1),interpreter-access-checks)
+  ifeq ($(1),interp-ac)
     core_compile_options += --compiler-filter=verify-at-runtime --runtime-arg -Xverify:softfail
-    core_infix := -interpreter-access-checks
+    core_infix := -interp-ac
   endif
   ifeq ($(1),default)
     # Default has no infix, no compile options.
   endif
-  ifneq ($(filter-out default interpreter interpreter-access-checks jit optimizing,$(1)),)
+  ifneq ($(filter-out default interpreter interp-ac jit optimizing,$(1)),)
     #Technically this test is not precise, but hopefully good enough.
     $$(error found $(1) expected default, interpreter, interpreter-access-checks, jit or optimizing)
   endif
@@ -147,14 +147,14 @@
 $(eval $(call create-core-oat-host-rule-combination,default,,))
 $(eval $(call create-core-oat-host-rule-combination,optimizing,,))
 $(eval $(call create-core-oat-host-rule-combination,interpreter,,))
-$(eval $(call create-core-oat-host-rule-combination,interpreter-access-checks,,))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,,))
 
 valgrindHOST_CORE_IMG_OUTS :=
 valgrindHOST_CORE_OAT_OUTS :=
 $(eval $(call create-core-oat-host-rule-combination,default,valgrind,32))
 $(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32))
 $(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32))
-$(eval $(call create-core-oat-host-rule-combination,interpreter-access-checks,valgrind,32))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32))
 
 valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS)
 
@@ -184,14 +184,14 @@
     core_compile_options += --compiler-filter=interpret-only
     core_infix := -interpreter
   endif
-  ifeq ($(1),interpreter-access-checks)
+  ifeq ($(1),interp-ac)
     core_compile_options += --compiler-filter=verify-at-runtime --runtime-arg -Xverify:softfail
-    core_infix := -interpreter-access-checks
+    core_infix := -interp-ac
   endif
   ifeq ($(1),default)
     # Default has no infix, no compile options.
   endif
-  ifneq ($(filter-out default interpreter interpreter-access-checks jit optimizing,$(1)),)
+  ifneq ($(filter-out default interpreter interp-ac jit optimizing,$(1)),)
     # Technically this test is not precise, but hopefully good enough.
     $$(error found $(1) expected default, interpreter, interpreter-access-checks, jit or optimizing)
   endif
@@ -272,14 +272,14 @@
 $(eval $(call create-core-oat-target-rule-combination,default,,))
 $(eval $(call create-core-oat-target-rule-combination,optimizing,,))
 $(eval $(call create-core-oat-target-rule-combination,interpreter,,))
-$(eval $(call create-core-oat-target-rule-combination,interpreter-access-checks,,))
+$(eval $(call create-core-oat-target-rule-combination,interp-ac,,))
 
 valgrindTARGET_CORE_IMG_OUTS :=
 valgrindTARGET_CORE_OAT_OUTS :=
 $(eval $(call create-core-oat-target-rule-combination,default,valgrind,32))
 $(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32))
 $(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32))
-$(eval $(call create-core-oat-target-rule-combination,interpreter-access-checks,valgrind,32))
+$(eval $(call create-core-oat-target-rule-combination,interp-ac,valgrind,32))
 
 valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS)
 
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index dd68dd4..16c161e 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -36,7 +36,7 @@
 namespace art {
 
 // Run the tests only on host.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
 
 class QuickCFITest : public CFITest {
  public:
@@ -56,6 +56,8 @@
       CompilerOptions::kDefaultSmallMethodThreshold,
       CompilerOptions::kDefaultTinyMethodThreshold,
       CompilerOptions::kDefaultNumDexMethodsThreshold,
+      CompilerOptions::kDefaultInlineDepthLimit,
+      CompilerOptions::kDefaultInlineMaxCodeUnits,
       false,
       CompilerOptions::kDefaultTopKProfileThreshold,
       false,
@@ -134,6 +136,6 @@
 TEST_ISA(kMips)
 TEST_ISA(kMips64)
 
-#endif  // HAVE_ANDROID_OS
+#endif  // __ANDROID__
 
 }  // namespace art
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index 798e23f..98e9f38 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -39,6 +39,8 @@
         CompilerOptions::kDefaultSmallMethodThreshold,
         CompilerOptions::kDefaultTinyMethodThreshold,
         CompilerOptions::kDefaultNumDexMethodsThreshold,
+        CompilerOptions::kDefaultInlineDepthLimit,
+        CompilerOptions::kDefaultInlineMaxCodeUnits,
         false,
         CompilerOptions::kDefaultTopKProfileThreshold,
         false,
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 05705a2..affa52a 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -690,66 +690,76 @@
   return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end();
 }
 
-static void ResolveExceptionsForMethod(
-    ArtMethod* method_handle, std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
-    SHARED_REQUIRES(Locks::mutator_lock_) {
-  const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
-  if (code_item == nullptr) {
-    return;  // native or abstract method
-  }
-  if (code_item->tries_size_ == 0) {
-    return;  // nothing to process
-  }
-  const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
-  size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
-  for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
-    int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
-    bool has_catch_all = false;
-    if (encoded_catch_handler_size <= 0) {
-      encoded_catch_handler_size = -encoded_catch_handler_size;
-      has_catch_all = true;
+class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
+ public:
+  ResolveCatchBlockExceptionsClassVisitor(
+      std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
+     : exceptions_to_resolve_(exceptions_to_resolve) {}
+
+  void ResolveExceptionsForMethod(ArtMethod* method_handle) SHARED_REQUIRES(Locks::mutator_lock_) {
+    const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
+    if (code_item == nullptr) {
+      return;  // native or abstract method
     }
-    for (int32_t j = 0; j < encoded_catch_handler_size; j++) {
-      uint16_t encoded_catch_handler_handlers_type_idx =
-          DecodeUnsignedLeb128(&encoded_catch_handler_list);
-      // Add to set of types to resolve if not already in the dex cache resolved types
-      if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
-        exceptions_to_resolve.insert(
-            std::pair<uint16_t, const DexFile*>(encoded_catch_handler_handlers_type_idx,
-                                                method_handle->GetDexFile()));
+    if (code_item->tries_size_ == 0) {
+      return;  // nothing to process
+    }
+    const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
+    size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
+    for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
+      int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
+      bool has_catch_all = false;
+      if (encoded_catch_handler_size <= 0) {
+        encoded_catch_handler_size = -encoded_catch_handler_size;
+        has_catch_all = true;
       }
-      // ignore address associated with catch handler
-      DecodeUnsignedLeb128(&encoded_catch_handler_list);
-    }
-    if (has_catch_all) {
-      // ignore catch all address
-      DecodeUnsignedLeb128(&encoded_catch_handler_list);
+      for (int32_t j = 0; j < encoded_catch_handler_size; j++) {
+        uint16_t encoded_catch_handler_handlers_type_idx =
+            DecodeUnsignedLeb128(&encoded_catch_handler_list);
+        // Add to set of types to resolve if not already in the dex cache resolved types
+        if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+          exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
+                                         method_handle->GetDexFile());
+        }
+        // ignore address associated with catch handler
+        DecodeUnsignedLeb128(&encoded_catch_handler_list);
+      }
+      if (has_catch_all) {
+        // ignore catch all address
+        DecodeUnsignedLeb128(&encoded_catch_handler_list);
+      }
     }
   }
-}
 
-static bool ResolveCatchBlockExceptionsClassVisitor(mirror::Class* c, void* arg)
-    SHARED_REQUIRES(Locks::mutator_lock_) {
-  auto* exceptions_to_resolve =
-      reinterpret_cast<std::set<std::pair<uint16_t, const DexFile*>>*>(arg);
-  const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-  for (auto& m : c->GetVirtualMethods(pointer_size)) {
-    ResolveExceptionsForMethod(&m, *exceptions_to_resolve);
+  virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+    for (auto& m : c->GetVirtualMethods(pointer_size)) {
+      ResolveExceptionsForMethod(&m);
+    }
+    for (auto& m : c->GetDirectMethods(pointer_size)) {
+      ResolveExceptionsForMethod(&m);
+    }
+    return true;
   }
-  for (auto& m : c->GetDirectMethods(pointer_size)) {
-    ResolveExceptionsForMethod(&m, *exceptions_to_resolve);
-  }
-  return true;
-}
 
-static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg)
-    SHARED_REQUIRES(Locks::mutator_lock_) {
-  std::unordered_set<std::string>* image_classes =
-      reinterpret_cast<std::unordered_set<std::string>*>(arg);
-  std::string temp;
-  image_classes->insert(klass->GetDescriptor(&temp));
-  return true;
-}
+ private:
+  std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve_;
+};
+
+class RecordImageClassesVisitor : public ClassVisitor {
+ public:
+  explicit RecordImageClassesVisitor(std::unordered_set<std::string>* image_classes)
+      : image_classes_(image_classes) {}
+
+  bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    std::string temp;
+    image_classes_->insert(klass->GetDescriptor(&temp));
+    return true;
+  }
+
+ private:
+  std::unordered_set<std::string>* const image_classes_;
+};
 
 // Make a list of descriptors for classes to include in the image
 void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
@@ -787,8 +797,8 @@
       hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
   do {
     unresolved_exception_types.clear();
-    class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor,
-                               &unresolved_exception_types);
+    ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types);
+    class_linker->VisitClasses(&visitor);
     for (const std::pair<uint16_t, const DexFile*>& exception_type : unresolved_exception_types) {
       uint16_t exception_type_idx = exception_type.first;
       const DexFile* dex_file = exception_type.second;
@@ -811,7 +821,8 @@
   // We walk the roots looking for classes so that we'll pick up the
   // above classes plus any classes them depend on such super
   // classes, interfaces, and the required ClassLinker roots.
-  class_linker->VisitClasses(RecordImageClassesVisitor, image_classes_.get());
+  RecordImageClassesVisitor visitor(image_classes_.get());
+  class_linker->VisitClasses(&visitor);
 
   CHECK_NE(image_classes_->size(), 0U);
 }
@@ -883,8 +894,13 @@
   }
 
   // java.lang.Reference visitor for VisitReferences.
-  void operator()(mirror::Class* /* klass */, mirror::Reference* /* ref */) const {
-  }
+  void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref ATTRIBUTE_UNUSED)
+      const {}
+
+  // Ignore class native roots.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+      const {}
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
 
   void Walk() SHARED_REQUIRES(Locks::mutator_lock_) {
     // Use the initial classes as roots for a search.
@@ -894,6 +910,29 @@
   }
 
  private:
+  class FindImageClassesVisitor : public ClassVisitor {
+   public:
+    explicit FindImageClassesVisitor(ClinitImageUpdate* data) : data_(data) {}
+
+    bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+      std::string temp;
+      const char* name = klass->GetDescriptor(&temp);
+      if (data_->image_class_descriptors_->find(name) != data_->image_class_descriptors_->end()) {
+        data_->image_classes_.push_back(klass);
+      } else {
+        // Check whether it is initialized and has a clinit. They must be kept, too.
+        if (klass->IsInitialized() && klass->FindClassInitializer(
+            Runtime::Current()->GetClassLinker()->GetImagePointerSize()) != nullptr) {
+          data_->image_classes_.push_back(klass);
+        }
+      }
+      return true;
+    }
+
+   private:
+    ClinitImageUpdate* const data_;
+  };
+
   ClinitImageUpdate(std::unordered_set<std::string>* image_class_descriptors, Thread* self,
                     ClassLinker* linker)
       SHARED_REQUIRES(Locks::mutator_lock_) :
@@ -910,25 +949,8 @@
 
     // Find all the already-marked classes.
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-    linker->VisitClasses(FindImageClasses, this);
-  }
-
-  static bool FindImageClasses(mirror::Class* klass, void* arg)
-      SHARED_REQUIRES(Locks::mutator_lock_) {
-    ClinitImageUpdate* data = reinterpret_cast<ClinitImageUpdate*>(arg);
-    std::string temp;
-    const char* name = klass->GetDescriptor(&temp);
-    if (data->image_class_descriptors_->find(name) != data->image_class_descriptors_->end()) {
-      data->image_classes_.push_back(klass);
-    } else {
-      // Check whether it is initialized and has a clinit. They must be kept, too.
-      if (klass->IsInitialized() && klass->FindClassInitializer(
-          Runtime::Current()->GetClassLinker()->GetImagePointerSize()) != nullptr) {
-        data->image_classes_.push_back(klass);
-      }
-    }
-
-    return true;
+    FindImageClassesVisitor visitor(this);
+    linker->VisitClasses(&visitor);
   }
 
   void VisitClinitClassesObject(mirror::Object* object) const
@@ -1726,7 +1748,7 @@
   explicit ResolveClassFieldsAndMethodsVisitor(const ParallelCompilationManager* manager)
       : manager_(manager) {}
 
-  virtual void Visit(size_t class_def_index) OVERRIDE REQUIRES(!Locks::mutator_lock_) {
+  void Visit(size_t class_def_index) OVERRIDE REQUIRES(!Locks::mutator_lock_) {
     ATRACE_CALL();
     Thread* const self = Thread::Current();
     jobject jclass_loader = manager_->GetClassLoader();
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 226e6b7..3f5a1ea 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -27,6 +27,8 @@
       small_method_threshold_(kDefaultSmallMethodThreshold),
       tiny_method_threshold_(kDefaultTinyMethodThreshold),
       num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
+      inline_depth_limit_(kDefaultInlineDepthLimit),
+      inline_max_code_units_(kDefaultInlineMaxCodeUnits),
       include_patch_information_(kDefaultIncludePatchInformation),
       top_k_profile_threshold_(kDefaultTopKProfileThreshold),
       debuggable_(false),
@@ -52,6 +54,8 @@
                                  size_t small_method_threshold,
                                  size_t tiny_method_threshold,
                                  size_t num_dex_methods_threshold,
+                                 size_t inline_depth_limit,
+                                 size_t inline_max_code_units,
                                  bool include_patch_information,
                                  double top_k_profile_threshold,
                                  bool debuggable,
@@ -71,6 +75,8 @@
     small_method_threshold_(small_method_threshold),
     tiny_method_threshold_(tiny_method_threshold),
     num_dex_methods_threshold_(num_dex_methods_threshold),
+    inline_depth_limit_(inline_depth_limit),
+    inline_max_code_units_(inline_max_code_units),
     include_patch_information_(include_patch_information),
     top_k_profile_threshold_(top_k_profile_threshold),
     debuggable_(debuggable),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index fe681e2..17b19dd 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -51,6 +51,8 @@
   static constexpr double kDefaultTopKProfileThreshold = 90.0;
   static const bool kDefaultGenerateDebugInfo = kIsDebugBuild;
   static const bool kDefaultIncludePatchInformation = false;
+  static const size_t kDefaultInlineDepthLimit = 3;
+  static const size_t kDefaultInlineMaxCodeUnits = 18;
 
   CompilerOptions();
   ~CompilerOptions();
@@ -61,6 +63,8 @@
                   size_t small_method_threshold,
                   size_t tiny_method_threshold,
                   size_t num_dex_methods_threshold,
+                  size_t inline_depth_limit,
+                  size_t inline_max_code_units,
                   bool include_patch_information,
                   double top_k_profile_threshold,
                   bool debuggable,
@@ -137,6 +141,14 @@
     return num_dex_methods_threshold_;
   }
 
+  size_t GetInlineDepthLimit() const {
+    return inline_depth_limit_;
+  }
+
+  size_t GetInlineMaxCodeUnits() const {
+    return inline_max_code_units_;
+  }
+
   double GetTopKProfileThreshold() const {
     return top_k_profile_threshold_;
   }
@@ -202,6 +214,8 @@
   const size_t small_method_threshold_;
   const size_t tiny_method_threshold_;
   const size_t num_dex_methods_threshold_;
+  const size_t inline_depth_limit_;
+  const size_t inline_max_code_units_;
   const bool include_patch_information_;
   // When using a profile file only the top K% of the profiled samples will be compiled.
   const double top_k_profile_threshold_;
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index 4d423d0..a07d27c 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -27,7 +27,7 @@
 namespace dwarf {
 
 // Run the tests only on host since we need objdump.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
 
 constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
 
@@ -336,7 +336,7 @@
   CheckObjdumpOutput(is64bit, "-W");
 }
 
-#endif  // HAVE_ANDROID_OS
+#endif  // __ANDROID__
 
 }  // namespace dwarf
 }  // namespace art
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 3ba1415..dda36fa 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -539,16 +539,19 @@
   return true;
 }
 
+class ComputeLazyFieldsForClassesVisitor : public ClassVisitor {
+ public:
+  bool Visit(Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    StackHandleScope<1> hs(Thread::Current());
+    mirror::Class::ComputeName(hs.NewHandle(c));
+    return true;
+  }
+};
+
 void ImageWriter::ComputeLazyFieldsForImageClasses() {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  class_linker->VisitClassesWithoutClassesLock(ComputeLazyFieldsForClassesVisitor, nullptr);
-}
-
-bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* c, void* /*arg*/) {
-  Thread* self = Thread::Current();
-  StackHandleScope<1> hs(self);
-  mirror::Class::ComputeName(hs.NewHandle(c));
-  return true;
+  ComputeLazyFieldsForClassesVisitor visitor;
+  class_linker->VisitClassesWithoutClassesLock(&visitor);
 }
 
 void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED) {
@@ -592,9 +595,20 @@
   return compiler_driver_.IsImageClass(klass->GetDescriptor(&temp));
 }
 
-struct NonImageClasses {
-  ImageWriter* image_writer;
-  std::set<std::string>* non_image_classes;
+class NonImageClassesVisitor : public ClassVisitor {
+ public:
+  explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
+
+  bool Visit(Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!image_writer_->IsImageClass(klass)) {
+      std::string temp;
+      non_image_classes_.insert(klass->GetDescriptor(&temp));
+    }
+    return true;
+  }
+
+  std::set<std::string> non_image_classes_;
+  ImageWriter* const image_writer_;
 };
 
 void ImageWriter::PruneNonImageClasses() {
@@ -606,14 +620,11 @@
   Thread* self = Thread::Current();
 
   // Make a list of classes we would like to prune.
-  std::set<std::string> non_image_classes;
-  NonImageClasses context;
-  context.image_writer = this;
-  context.non_image_classes = &non_image_classes;
-  class_linker->VisitClasses(NonImageClassesVisitor, &context);
+  NonImageClassesVisitor visitor(this);
+  class_linker->VisitClasses(&visitor);
 
   // Remove the undesired classes from the class roots.
-  for (const std::string& it : non_image_classes) {
+  for (const std::string& it : visitor.non_image_classes_) {
     bool result = class_linker->RemoveClass(it.c_str(), nullptr);
     DCHECK(result);
   }
@@ -669,15 +680,6 @@
   class_linker->DropFindArrayClassCache();
 }
 
-bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) {
-  NonImageClasses* context = reinterpret_cast<NonImageClasses*>(arg);
-  if (!context->image_writer->IsImageClass(klass)) {
-    std::string temp;
-    context->non_image_classes->insert(klass->GetDescriptor(&temp));
-  }
-  return true;
-}
-
 void ImageWriter::CheckNonImageClassesRemoved() {
   if (compiler_driver_.GetImageClasses() != nullptr) {
     gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -1189,6 +1191,13 @@
   FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) {
   }
 
+  // Ignore class roots since we don't have a way to map them to the destination. These are handled
+  // with other logic.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+      const {}
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
+
   void operator()(Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     Object* ref = obj->GetFieldObject<Object, kVerifyNone>(offset);
@@ -1200,8 +1209,7 @@
 
   // java.lang.ref.Reference visitor.
   void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(Locks::heap_bitmap_lock_) {
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
     copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
         mirror::Reference::ReferentOffset(), image_writer_->GetImageAddress(ref->GetReferent()));
   }
@@ -1224,8 +1232,7 @@
 
   void operator()(mirror::Class* klass ATTRIBUTE_UNUSED,
                   mirror::Reference* ref ATTRIBUTE_UNUSED) const
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(Locks::heap_bitmap_lock_) {
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
     LOG(FATAL) << "Reference not expected here.";
   }
 };
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 42b1cbf..cabd918 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -217,8 +217,6 @@
   // Preinitializes some otherwise lazy fields (such as Class name) to avoid runtime image dirtying.
   void ComputeLazyFieldsForImageClasses()
       SHARED_REQUIRES(Locks::mutator_lock_);
-  static bool ComputeLazyFieldsForClassesVisitor(mirror::Class* klass, void* arg)
-      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Wire dex cache resolved strings to strings in the image to avoid runtime resolution.
   void ComputeEagerResolvedStrings() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -227,8 +225,6 @@
 
   // Remove unwanted classes from various roots.
   void PruneNonImageClasses() SHARED_REQUIRES(Locks::mutator_lock_);
-  static bool NonImageClassesVisitor(mirror::Class* c, void* arg)
-      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Verify unwanted classes removed.
   void CheckNonImageClassesRemoved() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -376,6 +372,7 @@
   friend class FixupClassVisitor;
   friend class FixupRootVisitor;
   friend class FixupVisitor;
+  friend class NonImageClassesVisitor;
   DISALLOW_COPY_AND_ASSIGN(ImageWriter);
 };
 
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index d70211f..c95bac2 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -71,6 +71,8 @@
       CompilerOptions::kDefaultSmallMethodThreshold,
       CompilerOptions::kDefaultTinyMethodThreshold,
       CompilerOptions::kDefaultNumDexMethodsThreshold,
+      CompilerOptions::kDefaultInlineDepthLimit,
+      CompilerOptions::kDefaultInlineMaxCodeUnits,
       /* include_patch_information */ false,
       CompilerOptions::kDefaultTopKProfileThreshold,
       Runtime::Current()->IsDebuggable(),
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 016f28e..0bfe8a2 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -28,7 +28,7 @@
 namespace art {
 
 // Run the tests only on host.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
 
 class JNICFITest : public CFITest {
  public:
@@ -88,6 +88,6 @@
 TEST_ISA(kMips)
 TEST_ISA(kMips64)
 
-#endif  // HAVE_ANDROID_OS
+#endif  // __ANDROID__
 
 }  // namespace art
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index c98a5f8..88dc29e 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -183,7 +183,7 @@
   EXPECT_EQ(72U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(28U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(112 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+  EXPECT_EQ(113 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
 }
 
 TEST_F(OatTest, OatHeaderIsValid) {
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index cfebb77..e4bc9e6 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -89,6 +89,33 @@
                           block->GetBlockId()));
   }
 
+  // Ensure that the only Return(Void) and Throw jump to Exit. An exiting
+  // TryBoundary may be between a Throw and the Exit if the Throw is in a try.
+  if (block->IsExitBlock()) {
+    for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
+      HBasicBlock* predecessor = block->GetPredecessors().Get(i);
+      if (predecessor->IsSingleTryBoundary()
+          && !predecessor->GetLastInstruction()->AsTryBoundary()->IsEntry()) {
+        HBasicBlock* real_predecessor = predecessor->GetSinglePredecessor();
+        HInstruction* last_instruction = real_predecessor->GetLastInstruction();
+        if (!last_instruction->IsThrow()) {
+          AddError(StringPrintf("Unexpected TryBoundary between %s:%d and Exit.",
+                                last_instruction->DebugName(),
+                                last_instruction->GetId()));
+        }
+      } else {
+        HInstruction* last_instruction = predecessor->GetLastInstruction();
+        if (!last_instruction->IsReturn()
+            && !last_instruction->IsReturnVoid()
+            && !last_instruction->IsThrow()) {
+          AddError(StringPrintf("Unexpected instruction %s:%d jumps into the exit block.",
+                                last_instruction->DebugName(),
+                                last_instruction->GetId()));
+        }
+      }
+    }
+  }
+
   // Visit this block's list of phis.
   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
     HInstruction* current = it.Current();
@@ -328,6 +355,39 @@
 void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
   super_type::VisitBasicBlock(block);
 
+  // Ensure that only catch blocks have exceptional predecessors, and if they do
+  // these are instructions which throw into them.
+  if (block->IsCatchBlock()) {
+    for (size_t i = 0, e = block->GetExceptionalPredecessors().Size(); i < e; ++i) {
+      HInstruction* thrower = block->GetExceptionalPredecessors().Get(i);
+      HBasicBlock* try_block = thrower->GetBlock();
+      if (!thrower->CanThrow()) {
+        AddError(StringPrintf("Exceptional predecessor %s:%d of catch block %d does not throw.",
+                              thrower->DebugName(),
+                              thrower->GetId(),
+                              block->GetBlockId()));
+      } else if (!try_block->IsInTry()) {
+        AddError(StringPrintf("Exceptional predecessor %s:%d of catch block %d "
+                              "is not in a try block.",
+                              thrower->DebugName(),
+                              thrower->GetId(),
+                              block->GetBlockId()));
+      } else if (!try_block->GetTryEntry()->HasExceptionHandler(*block)) {
+        AddError(StringPrintf("Catch block %d is not an exception handler of "
+                              "its exceptional predecessor %s:%d.",
+                              block->GetBlockId(),
+                              thrower->DebugName(),
+                              thrower->GetId()));
+      }
+    }
+  } else {
+    if (!block->GetExceptionalPredecessors().IsEmpty()) {
+      AddError(StringPrintf("Normal block %d has %zu exceptional predecessors.",
+                            block->GetBlockId(),
+                            block->GetExceptionalPredecessors().Size()));
+    }
+  }
+
   // Ensure that catch blocks are not normal successors, and normal blocks are
   // never exceptional successors.
   const size_t num_normal_successors = block->NumberOfNormalSuccessors();
@@ -512,6 +572,7 @@
 
 void SSAChecker::VisitInstruction(HInstruction* instruction) {
   super_type::VisitInstruction(instruction);
+  HBasicBlock* block = instruction->GetBlock();
 
   // Ensure an instruction dominates all its uses.
   for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
@@ -543,6 +604,24 @@
       }
     }
   }
+
+  // Ensure that throwing instructions in try blocks are listed as exceptional
+  // predecessors in their exception handlers.
+  if (instruction->CanThrow() && block->IsInTry()) {
+    for (HExceptionHandlerIterator handler_it(*block->GetTryEntry());
+         !handler_it.Done();
+         handler_it.Advance()) {
+      if (!handler_it.Current()->GetExceptionalPredecessors().Contains(instruction)) {
+        AddError(StringPrintf("Instruction %s:%d is in try block %d and can throw "
+                              "but its exception handler %d does not list it in "
+                              "its exceptional predecessors.",
+                              instruction->DebugName(),
+                              instruction->GetId(),
+                              block->GetBlockId(),
+                              handler_it.Current()->GetBlockId()));
+      }
+    }
+  }
 }
 
 static Primitive::Type PrimitiveKind(Primitive::Type type) {
@@ -590,11 +669,32 @@
   if (phi->IsCatchPhi()) {
     // The number of inputs of a catch phi corresponds to the total number of
     // throwing instructions caught by this catch block.
+    const GrowableArray<HInstruction*>& predecessors =
+        phi->GetBlock()->GetExceptionalPredecessors();
+    if (phi->InputCount() != predecessors.Size()) {
+      AddError(StringPrintf(
+          "Phi %d in catch block %d has %zu inputs, "
+          "but catch block %d has %zu exceptional predecessors.",
+          phi->GetId(), phi->GetBlock()->GetBlockId(), phi->InputCount(),
+          phi->GetBlock()->GetBlockId(), predecessors.Size()));
+    } else {
+      for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+        HInstruction* input = phi->InputAt(i);
+        HInstruction* thrower = predecessors.Get(i);
+        if (!input->StrictlyDominates(thrower)) {
+          AddError(StringPrintf(
+              "Input %d at index %zu of phi %d from catch block %d does not "
+              "dominate the throwing instruction %s:%d.",
+              input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(),
+              thrower->DebugName(), thrower->GetId()));
+        }
+      }
+    }
   } else {
     // Ensure the number of inputs of a non-catch phi is the same as the number
     // of its predecessors.
     const GrowableArray<HBasicBlock*>& predecessors =
-      phi->GetBlock()->GetPredecessors();
+        phi->GetBlock()->GetPredecessors();
     if (phi->InputCount() != predecessors.Size()) {
       AddError(StringPrintf(
           "Phi %d in block %d has %zu inputs, "
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index eca0d93..0f66775 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -25,14 +25,14 @@
  * Create a simple control-flow graph composed of two blocks:
  *
  *   BasicBlock 0, succ: 1
- *     0: Goto 1
+ *     0: ReturnVoid 1
  *   BasicBlock 1, pred: 0
  *     1: Exit
  */
 HGraph* CreateSimpleCFG(ArenaAllocator* allocator) {
   HGraph* graph = CreateGraph(allocator);
   HBasicBlock* entry_block = new (allocator) HBasicBlock(graph);
-  entry_block->AddInstruction(new (allocator) HGoto());
+  entry_block->AddInstruction(new (allocator) HReturnVoid());
   graph->AddBlock(entry_block);
   graph->SetEntryBlock(entry_block);
   HBasicBlock* exit_block = new (allocator) HBasicBlock(graph);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index afea403..069a7a4 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -386,6 +386,7 @@
     StartAttributeStream("recursive") << std::boolalpha
                                       << invoke->IsRecursive()
                                       << std::noboolalpha;
+    StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
   }
 
   void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
@@ -396,6 +397,11 @@
     return strcmp(pass_name_, name) == 0;
   }
 
+  bool IsReferenceTypePropagationPass() {
+    return strstr(pass_name_, ReferenceTypePropagation::kReferenceTypePropagationPassName)
+        != nullptr;
+  }
+
   void PrintInstruction(HInstruction* instruction) {
     output_ << instruction->DebugName();
     if (instruction->InputCount() > 0) {
@@ -459,27 +465,19 @@
       } else {
         StartAttributeStream("loop") << "B" << info->GetHeader()->GetBlockId();
       }
-    } else if (IsPass(ReferenceTypePropagation::kReferenceTypePropagationPassName)
-               && is_after_pass_) {
-      if (instruction->GetType() == Primitive::kPrimNot) {
-        if (instruction->IsLoadClass()) {
-          ReferenceTypeInfo info = instruction->AsLoadClass()->GetLoadedClassRTI();
-          ScopedObjectAccess soa(Thread::Current());
-          if (info.GetTypeHandle().GetReference() != nullptr) {
-            StartAttributeStream("klass") << PrettyClass(info.GetTypeHandle().Get());
-          } else {
-            StartAttributeStream("klass") << "unresolved";
-          }
-        } else {
-          ReferenceTypeInfo info = instruction->GetReferenceTypeInfo();
-          if (info.IsTop()) {
-            StartAttributeStream("klass") << "java.lang.Object";
-          } else {
-            ScopedObjectAccess soa(Thread::Current());
-            StartAttributeStream("klass") << PrettyClass(info.GetTypeHandle().Get());
-          }
-          StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
-        }
+    } else if (IsReferenceTypePropagationPass()
+        && (instruction->GetType() == Primitive::kPrimNot)) {
+      ReferenceTypeInfo info = instruction->IsLoadClass()
+        ? instruction->AsLoadClass()->GetLoadedClassRTI()
+        : instruction->GetReferenceTypeInfo();
+      ScopedObjectAccess soa(Thread::Current());
+      if (info.IsValid()) {
+        StartAttributeStream("klass") << PrettyDescriptor(info.GetTypeHandle().Get());
+        StartAttributeStream("can_be_null")
+            << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
+        StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
+      } else {
+        DCHECK(!is_after_pass_) << "Type info should be valid after reference type propagation";
       }
     }
     if (disasm_info_ != nullptr) {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index c185b58..0106595 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -22,8 +22,10 @@
 #include "constant_folding.h"
 #include "dead_code_elimination.h"
 #include "driver/compiler_driver-inl.h"
+#include "driver/compiler_options.h"
 #include "driver/dex_compilation_unit.h"
 #include "instruction_simplifier.h"
+#include "intrinsics.h"
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache.h"
 #include "nodes.h"
@@ -38,9 +40,6 @@
 
 namespace art {
 
-static constexpr int kMaxInlineCodeUnits = 18;
-static constexpr int kDepthLimit = 3;
-
 void HInliner::Run() {
   if (graph_->IsDebuggable()) {
     // For simplicity, we currently never inline when the graph is debuggable. This avoids
@@ -109,10 +108,8 @@
     receiver = receiver->InputAt(0);
   }
   ReferenceTypeInfo info = receiver->GetReferenceTypeInfo();
-  if (info.IsTop()) {
-    // We have no information on the receiver.
-    return nullptr;
-  } else if (!info.IsExact()) {
+  DCHECK(info.IsValid()) << "Invalid RTI for " << receiver->DebugName();
+  if (!info.IsExact()) {
     // We currently only support inlining with known receivers.
     // TODO: Remove this check, we should be able to inline final methods
     // on unknown receivers.
@@ -221,7 +218,8 @@
     return false;
   }
 
-  if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) {
+  size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits();
+  if (code_item->insns_size_in_code_units_ > inline_max_code_units) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " is too big to inline";
     return false;
@@ -273,11 +271,11 @@
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
   const DexFile& callee_dex_file = *resolved_method->GetDexFile();
   uint32_t method_index = resolved_method->GetDexMethodIndex();
-
+  ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   DexCompilationUnit dex_compilation_unit(
     nullptr,
     caller_compilation_unit_.GetClassLoader(),
-    caller_compilation_unit_.GetClassLinker(),
+    class_linker,
     *resolved_method->GetDexFile(),
     code_item,
     resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
@@ -358,8 +356,10 @@
   HConstantFolding fold(callee_graph);
   ReferenceTypePropagation type_propagation(callee_graph, handles_);
   InstructionSimplifier simplify(callee_graph, stats_);
+  IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_);
 
   HOptimization* optimizations[] = {
+    &intrinsics,
     &dce,
     &fold,
     &type_propagation,
@@ -371,7 +371,7 @@
     optimization->Run();
   }
 
-  if (depth_ + 1 < kDepthLimit) {
+  if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) {
     HInliner inliner(callee_graph,
                      outer_compilation_unit_,
                      dex_compilation_unit,
@@ -450,7 +450,33 @@
     }
   }
 
-  callee_graph->InlineInto(graph_, invoke_instruction);
+  HInstruction* return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
+
+  // When merging the graph we might create a new NullConstant in the caller graph which does
+  // not have the chance to be typed. We assign the correct type here so that we can keep the
+  // assertion that every reference has a valid type. This also simplifies checks along the way.
+  HNullConstant* null_constant = graph_->GetNullConstant();
+  if (!null_constant->GetReferenceTypeInfo().IsValid()) {
+    ReferenceTypeInfo::TypeHandle obj_handle =
+            handles_->NewHandle(class_linker->GetClassRoot(ClassLinker::kJavaLangObject));
+    null_constant->SetReferenceTypeInfo(
+            ReferenceTypeInfo::Create(obj_handle, false /* is_exact */));
+  }
+
+  if ((return_replacement != nullptr)
+      && (return_replacement->GetType() == Primitive::kPrimNot)) {
+    if (!return_replacement->GetReferenceTypeInfo().IsValid()) {
+      // Make sure that we have a valid type for the return. We may get an invalid one when
+      // we inline invokes with multiple branches and create a Phi for the result.
+      // TODO: we could be more precise by merging the phi inputs but that requires
+      // some functionality from the reference type propagation.
+      DCHECK(return_replacement->IsPhi());
+      ReferenceTypeInfo::TypeHandle return_handle =
+        handles_->NewHandle(resolved_method->GetReturnType());
+      return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
+         return_handle, return_handle->IsFinal() /* is_exact */));
+    }
+  }
 
   return true;
 }
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index b30b6c7..d391145 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -195,16 +195,17 @@
 // Returns whether doing a type test between the class of `object` against `klass` has
 // a statically known outcome. The result of the test is stored in `outcome`.
 static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) {
-  if (!klass->IsResolved()) {
-    // If the class couldn't be resolve it's not safe to compare against it. It's
-    // default type would be Top which might be wider that the actual class type
-    // and thus producing wrong results.
+  DCHECK(!object->IsNullConstant()) << "Null constants should be special cased";
+  ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
+  ScopedObjectAccess soa(Thread::Current());
+  if (!obj_rti.IsValid()) {
+    // We run the simplifier before the reference type propagation so type info might not be
+    // available.
     return false;
   }
 
-  ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
   ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
-  ScopedObjectAccess soa(Thread::Current());
+  DCHECK(class_rti.IsValid() && class_rti.IsExact());
   if (class_rti.IsSupertypeOf(obj_rti)) {
     *outcome = true;
     return true;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 519fa00..61dadc2 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -564,6 +564,13 @@
   return false;
 }
 
+void HBasicBlock::AddExceptionalPredecessor(HInstruction* exceptional_predecessor) {
+  DCHECK(exceptional_predecessor->CanThrow());
+  DCHECK(exceptional_predecessor->GetBlock()->IsInTry());
+  DCHECK(exceptional_predecessor->GetBlock()->GetTryEntry()->HasExceptionHandler(*this));
+  exceptional_predecessors_.Add(exceptional_predecessor);
+}
+
 static void UpdateInputsUsers(HInstruction* instruction) {
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     instruction->InputAt(i)->AddUseAt(instruction, i);
@@ -1225,10 +1232,12 @@
     return false;
   }
 
-  // Exception handler lists cannot contain duplicates, which makes it
-  // sufficient to test inclusion only in one direction.
-  for (HExceptionHandlerIterator it(other); !it.Done(); it.Advance()) {
-    if (!HasExceptionHandler(*it.Current())) {
+  // Exception handlers need to be stored in the same order.
+  for (HExceptionHandlerIterator it1(*this), it2(other);
+       !it1.Done();
+       it1.Advance(), it2.Advance()) {
+    DCHECK(!it2.Done());
+    if (it1.Current() != it2.Current()) {
       return false;
     }
   }
@@ -1485,7 +1494,7 @@
   blocks_.Put(block->GetBlockId(), nullptr);
 }
 
-void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
+HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
   DCHECK(HasExitBlock()) << "Unimplemented scenario";
   // Update the environments in this graph to have the invoke's environment
   // as parent.
@@ -1510,6 +1519,7 @@
     outer_graph->SetHasBoundsChecks(true);
   }
 
+  HInstruction* return_value = nullptr;
   if (GetBlocks().Size() == 3) {
     // Simple case of an entry block, a body block, and an exit block.
     // Put the body block's instruction into `invoke`'s block.
@@ -1524,7 +1534,8 @@
 
     // Replace the invoke with the return value of the inlined graph.
     if (last->IsReturn()) {
-      invoke->ReplaceWith(last->InputAt(0));
+      return_value = last->InputAt(0);
+      invoke->ReplaceWith(return_value);
     } else {
       DCHECK(last->IsReturnVoid());
     }
@@ -1546,7 +1557,6 @@
 
     // Update all predecessors of the exit block (now the `to` block)
     // to not `HReturn` but `HGoto` instead.
-    HInstruction* return_value = nullptr;
     bool returns_void = to->GetPredecessors().Get(0)->GetLastInstruction()->IsReturnVoid();
     if (to->GetPredecessors().Size() == 1) {
       HBasicBlock* predecessor = to->GetPredecessors().Get(0);
@@ -1680,6 +1690,8 @@
 
   // Finally remove the invoke from the caller.
   invoke->GetBlock()->RemoveInstruction(invoke);
+
+  return return_value;
 }
 
 /*
@@ -1757,11 +1769,39 @@
   }
 }
 
+void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) {
+  if (kIsDebugBuild) {
+    DCHECK_EQ(GetType(), Primitive::kPrimNot);
+    ScopedObjectAccess soa(Thread::Current());
+    DCHECK(rti.IsValid()) << "Invalid RTI for " << DebugName();
+    if (IsBoundType()) {
+      // Having the test here spares us from making the method virtual just for
+      // the sake of a DCHECK.
+      ReferenceTypeInfo upper_bound_rti = AsBoundType()->GetUpperBound();
+      DCHECK(upper_bound_rti.IsSupertypeOf(rti))
+          << " upper_bound_rti: " << upper_bound_rti
+          << " rti: " << rti;
+      DCHECK(!upper_bound_rti.GetTypeHandle()->IsFinal() || rti.IsExact());
+    }
+  }
+  reference_type_info_ = rti;
+}
+
+ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {}
+
+ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact)
+    : type_handle_(type_handle), is_exact_(is_exact) {
+  if (kIsDebugBuild) {
+    ScopedObjectAccess soa(Thread::Current());
+    DCHECK(IsValidHandle(type_handle));
+  }
+}
+
 std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) {
   ScopedObjectAccess soa(Thread::Current());
   os << "["
-     << " is_top=" << rhs.IsTop()
-     << " type=" << (rhs.IsTop() ? "?" : PrettyClass(rhs.GetTypeHandle().Get()))
+     << " is_valid=" << rhs.IsValid()
+     << " type=" << (!rhs.IsValid() ? "?" : PrettyClass(rhs.GetTypeHandle().Get()))
      << " is_exact=" << rhs.IsExact()
      << " ]";
   return os;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 7f446d4..9b8521d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -58,6 +58,7 @@
 static const int kDefaultNumberOfBlocks = 8;
 static const int kDefaultNumberOfSuccessors = 2;
 static const int kDefaultNumberOfPredecessors = 2;
+static const int kDefaultNumberOfExceptionalPredecessors = 0;
 static const int kDefaultNumberOfDominatedBlocks = 1;
 static const int kDefaultNumberOfBackEdges = 1;
 
@@ -210,7 +211,9 @@
   void ComputeTryBlockInformation();
 
   // Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
-  void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+  // Returns the instruction used to replace the invoke expression or null if the
+  // invoke is for a void method.
+  HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke);
 
   // Need to add a couple of blocks to test if the loop body is entered and
   // put deoptimization instructions, etc.
@@ -306,7 +309,12 @@
   // already, it is created and inserted into the graph. This method is only for
   // integral types.
   HConstant* GetConstant(Primitive::Type type, int64_t value);
+
+  // TODO: This is problematic for the consistency of reference type propagation
+  // because it can be created anytime after the pass and thus it will be left
+  // with an invalid type.
   HNullConstant* GetNullConstant();
+
   HIntConstant* GetIntConstant(int32_t value) {
     return CreateConstant(value, &cached_int_constants_);
   }
@@ -557,6 +565,7 @@
   explicit HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
       : graph_(graph),
         predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors),
+        exceptional_predecessors_(graph->GetArena(), kDefaultNumberOfExceptionalPredecessors),
         successors_(graph->GetArena(), kDefaultNumberOfSuccessors),
         loop_information_(nullptr),
         dominator_(nullptr),
@@ -571,6 +580,10 @@
     return predecessors_;
   }
 
+  const GrowableArray<HInstruction*>& GetExceptionalPredecessors() const {
+    return exceptional_predecessors_;
+  }
+
   const GrowableArray<HBasicBlock*>& GetSuccessors() const {
     return successors_;
   }
@@ -639,6 +652,8 @@
   HInstruction* GetLastPhi() const { return phis_.last_instruction_; }
   const HInstructionList& GetPhis() const { return phis_; }
 
+  void AddExceptionalPredecessor(HInstruction* exceptional_predecessor);
+
   void AddSuccessor(HBasicBlock* block) {
     successors_.Add(block);
     block->predecessors_.Add(this);
@@ -678,6 +693,10 @@
     predecessors_.Delete(block);
   }
 
+  void RemoveExceptionalPredecessor(HInstruction* instruction) {
+    exceptional_predecessors_.Delete(instruction);
+  }
+
   void RemoveSuccessor(HBasicBlock* block) {
     successors_.Delete(block);
   }
@@ -714,6 +733,15 @@
     return -1;
   }
 
+  size_t GetExceptionalPredecessorIndexOf(HInstruction* exceptional_predecessor) const {
+    for (size_t i = 0, e = exceptional_predecessors_.Size(); i < e; ++i) {
+      if (exceptional_predecessors_.Get(i) == exceptional_predecessor) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
   size_t GetSuccessorIndexOf(HBasicBlock* successor) const {
     for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
       if (successors_.Get(i) == successor) {
@@ -874,6 +902,7 @@
  private:
   HGraph* graph_;
   GrowableArray<HBasicBlock*> predecessors_;
+  GrowableArray<HInstruction*> exceptional_predecessors_;
   GrowableArray<HBasicBlock*> successors_;
   HInstructionList instructions_;
   HInstructionList phis_;
@@ -1460,79 +1489,64 @@
  public:
   typedef Handle<mirror::Class> TypeHandle;
 
-  static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact)
-      SHARED_REQUIRES(Locks::mutator_lock_) {
-    if (type_handle->IsObjectClass()) {
-      // Override the type handle to be consistent with the case when we get to
-      // Top but don't have the Object class available. It avoids having to guess
-      // what value the type_handle has when it's Top.
-      return ReferenceTypeInfo(TypeHandle(), is_exact, true);
-    } else {
-      return ReferenceTypeInfo(type_handle, is_exact, false);
-    }
+  static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) {
+    // The constructor will check that the type_handle is valid.
+    return ReferenceTypeInfo(type_handle, is_exact);
   }
 
-  static ReferenceTypeInfo CreateTop(bool is_exact) {
-    return ReferenceTypeInfo(TypeHandle(), is_exact, true);
+  static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); }
+
+  static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) {
+    return handle.GetReference() != nullptr;
   }
 
+  bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) {
+    return IsValidHandle(type_handle_);
+  }
   bool IsExact() const { return is_exact_; }
-  bool IsTop() const { return is_top_; }
+
+  bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+    DCHECK(IsValid());
+    return GetTypeHandle()->IsObjectClass();
+  }
   bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) {
-    return !IsTop() && GetTypeHandle()->IsInterface();
+    DCHECK(IsValid());
+    return GetTypeHandle()->IsInterface();
   }
 
   Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
 
   bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
-    if (IsTop()) {
-      // Top (equivalent for java.lang.Object) is supertype of anything.
-      return true;
-    }
-    if (rti.IsTop()) {
-      // If we get here `this` is not Top() so it can't be a supertype.
-      return false;
-    }
+    DCHECK(IsValid());
+    DCHECK(rti.IsValid());
     return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
   }
 
   // Returns true if the type information provide the same amount of details.
   // Note that it does not mean that the instructions have the same actual type
-  // (e.g. tops are equal but they can be the result of a merge).
+  // (because the type can be the result of a merge).
   bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) {
-    if (IsExact() != rti.IsExact()) {
-      return false;
-    }
-    if (IsTop() && rti.IsTop()) {
-      // `Top` means java.lang.Object, so the types are equivalent.
+    if (!IsValid() && !rti.IsValid()) {
+      // Invalid types are equal.
       return true;
     }
-    if (IsTop() || rti.IsTop()) {
-      // If only one is top or object than they are not equivalent.
-      // NB: We need this extra check because the type_handle of `Top` is invalid
-      // and we cannot inspect its reference.
+    if (!IsValid() || !rti.IsValid()) {
+      // One is valid, the other not.
       return false;
     }
-
-    // Finally check the types.
-    return GetTypeHandle().Get() == rti.GetTypeHandle().Get();
+    return IsExact() == rti.IsExact()
+        && GetTypeHandle().Get() == rti.GetTypeHandle().Get();
   }
 
  private:
-  ReferenceTypeInfo() : ReferenceTypeInfo(TypeHandle(), false, true) {}
-  ReferenceTypeInfo(TypeHandle type_handle, bool is_exact, bool is_top)
-      : type_handle_(type_handle), is_exact_(is_exact), is_top_(is_top) {}
+  ReferenceTypeInfo();
+  ReferenceTypeInfo(TypeHandle type_handle, bool is_exact);
 
   // The class of the object.
   TypeHandle type_handle_;
   // Whether or not the type is exact or a superclass of the actual type.
   // Whether or not we have any information about this type.
   bool is_exact_;
-  // A true value here means that the object type should be java.lang.Object.
-  // We don't have access to the corresponding mirror object every time so this
-  // flag acts as a substitute. When true, the TypeHandle refers to a null
-  // pointer and should not be used.
-  bool is_top_;
 };
 
 std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs);
@@ -1550,7 +1564,7 @@
         live_interval_(nullptr),
         lifetime_position_(kNoLifetime),
         side_effects_(side_effects),
-        reference_type_info_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) {}
+        reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {}
 
   virtual ~HInstruction() {}
 
@@ -1596,6 +1610,7 @@
 
   // Does not apply for all instructions, but having this at top level greatly
   // simplifies the null check elimination.
+  // TODO: Consider merging can_be_null into ReferenceTypeInfo.
   virtual bool CanBeNull() const {
     DCHECK_EQ(GetType(), Primitive::kPrimNot) << "CanBeNull only applies to reference types";
     return true;
@@ -1606,10 +1621,7 @@
     return false;
   }
 
-  void SetReferenceTypeInfo(ReferenceTypeInfo reference_type_info) {
-    DCHECK_EQ(GetType(), Primitive::kPrimNot);
-    reference_type_info_ = reference_type_info;
-  }
+  void SetReferenceTypeInfo(ReferenceTypeInfo rti);
 
   ReferenceTypeInfo GetReferenceTypeInfo() const {
     DCHECK_EQ(GetType(), Primitive::kPrimNot);
@@ -3904,7 +3916,7 @@
         is_referrers_class_(is_referrers_class),
         dex_pc_(dex_pc),
         generate_clinit_check_(false),
-        loaded_class_rti_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) {
+        loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
     SetRawInputAt(0, current_method);
   }
 
@@ -3955,10 +3967,6 @@
     loaded_class_rti_ = rti;
   }
 
-  bool IsResolved() {
-    return loaded_class_rti_.IsExact();
-  }
-
   const DexFile& GetDexFile() { return dex_file_; }
 
   bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; }
@@ -4201,27 +4209,43 @@
 
 class HBoundType : public HExpression<1> {
  public:
-  HBoundType(HInstruction* input, ReferenceTypeInfo bound_type)
+  // Constructs an HBoundType with the given upper_bound.
+  // Ensures that the upper_bound is valid.
+  HBoundType(HInstruction* input, ReferenceTypeInfo upper_bound, bool upper_can_be_null)
       : HExpression(Primitive::kPrimNot, SideEffects::None()),
-        bound_type_(bound_type) {
+        upper_bound_(upper_bound),
+        upper_can_be_null_(upper_can_be_null),
+        can_be_null_(upper_can_be_null) {
     DCHECK_EQ(input->GetType(), Primitive::kPrimNot);
     SetRawInputAt(0, input);
+    SetReferenceTypeInfo(upper_bound_);
   }
 
-  const ReferenceTypeInfo& GetBoundType() const { return bound_type_; }
+  // GetUpper* should only be used in reference type propagation.
+  const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; }
+  bool GetUpperCanBeNull() const { return upper_can_be_null_; }
 
-  bool CanBeNull() const OVERRIDE {
-    // `null instanceof ClassX` always return false so we can't be null.
-    return false;
+  void SetCanBeNull(bool can_be_null) {
+    DCHECK(upper_can_be_null_ || !can_be_null);
+    can_be_null_ = can_be_null;
   }
 
+  bool CanBeNull() const OVERRIDE { return can_be_null_; }
+
   DECLARE_INSTRUCTION(BoundType);
 
  private:
   // Encodes the most upper class that this instruction can have. In other words
-  // it is always the case that GetBoundType().IsSupertypeOf(GetReferenceType()).
-  // It is used to bound the type in cases like `if (x instanceof ClassX) {}`
-  const ReferenceTypeInfo bound_type_;
+  // it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()).
+  // It is used to bound the type in cases like:
+  //   if (x instanceof ClassX) {
+  //     // uper_bound_ will be ClassX
+  //   }
+  const ReferenceTypeInfo upper_bound_;
+  // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
+  // is false then can_be_null_ cannot be true).
+  const bool upper_can_be_null_;
+  bool can_be_null_;
 
   DISALLOW_COPY_AND_ASSIGN(HBoundType);
 };
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index fe3bb1a..f455571 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -29,7 +29,7 @@
 namespace art {
 
 // Run the tests only on host.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
 
 class OptimizingCFITest : public CFITest {
  public:
@@ -125,6 +125,6 @@
 TEST_ISA(kX86)
 TEST_ISA(kX86_64)
 
-#endif  // HAVE_ANDROID_OS
+#endif  // __ANDROID__
 
 }  // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 1c0123e..6a50b7d 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -369,6 +369,36 @@
   }
 }
 
+static void MaybeRunInliner(HGraph* graph,
+                            CompilerDriver* driver,
+                            OptimizingCompilerStats* stats,
+                            const DexCompilationUnit& dex_compilation_unit,
+                            PassObserver* pass_observer,
+                            StackHandleScopeCollection* handles) {
+  const CompilerOptions& compiler_options = driver->GetCompilerOptions();
+  bool should_inline = (compiler_options.GetInlineDepthLimit() > 0)
+      && (compiler_options.GetInlineMaxCodeUnits() > 0);
+  if (!should_inline) {
+    return;
+  }
+
+  ArenaAllocator* arena = graph->GetArena();
+  HInliner* inliner = new (arena) HInliner(
+    graph, dex_compilation_unit, dex_compilation_unit, driver, handles, stats);
+  ReferenceTypePropagation* type_propagation =
+    new (arena) ReferenceTypePropagation(graph, handles,
+        "reference_type_propagation_after_inlining");
+
+  HOptimization* optimizations[] = {
+    inliner,
+    // Run another type propagation phase: inlining will open up more opportunities
+    // to remove checkcast/instanceof and null checks.
+    type_propagation,
+  };
+
+  RunOptimizations(optimizations, arraysize(optimizations), pass_observer);
+}
+
 static void RunOptimizations(HGraph* graph,
                              CompilerDriver* driver,
                              OptimizingCompilerStats* stats,
@@ -383,10 +413,6 @@
   HConstantFolding* fold1 = new (arena) HConstantFolding(graph);
   InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
   HBooleanSimplifier* boolean_simplify = new (arena) HBooleanSimplifier(graph);
-
-  HInliner* inliner = new (arena) HInliner(
-      graph, dex_compilation_unit, dex_compilation_unit, driver, handles, stats);
-
   HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining");
   SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
   GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects);
@@ -398,28 +424,29 @@
       graph, stats, "instruction_simplifier_after_types");
   InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
       graph, stats, "instruction_simplifier_after_bce");
-  ReferenceTypePropagation* type_propagation2 =
-      new (arena) ReferenceTypePropagation(graph, handles);
   InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
       graph, stats, "instruction_simplifier_before_codegen");
 
   IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver);
 
-  HOptimization* optimizations[] = {
+  HOptimization* optimizations1[] = {
     intrinsics,
     fold1,
     simplify1,
     type_propagation,
     dce1,
-    simplify2,
-    inliner,
-    // Run another type propagation phase: inlining will open up more opprotunities
-    // to remove checkast/instanceof and null checks.
-    type_propagation2,
+    simplify2
+  };
+
+  RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
+
+  MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles);
+
+  HOptimization* optimizations2[] = {
     // BooleanSimplifier depends on the InstructionSimplifier removing redundant
     // suspend checks to recognize empty blocks.
     boolean_simplify,
-    fold2,
+    fold2,  // TODO: if we don't inline we can also skip fold2.
     side_effects,
     gvn,
     licm,
@@ -432,7 +459,7 @@
     simplify4,
   };
 
-  RunOptimizations(optimizations, arraysize(optimizations), pass_observer);
+  RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
 }
 
 // The stack map we generate must be 4-byte aligned on ARM. Since existing
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 68316c2..d1c1134 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -25,19 +25,35 @@
 
 class RTPVisitor : public HGraphDelegateVisitor {
  public:
-  RTPVisitor(HGraph* graph, StackHandleScopeCollection* handles)
+  RTPVisitor(HGraph* graph,
+             StackHandleScopeCollection* handles,
+             GrowableArray<HInstruction*>* worklist,
+             ReferenceTypeInfo::TypeHandle object_class_handle,
+             ReferenceTypeInfo::TypeHandle class_class_handle,
+             ReferenceTypeInfo::TypeHandle string_class_handle)
     : HGraphDelegateVisitor(graph),
-      handles_(handles) {}
+      handles_(handles),
+      object_class_handle_(object_class_handle),
+      class_class_handle_(class_class_handle),
+      string_class_handle_(string_class_handle),
+      worklist_(worklist) {}
 
+  void VisitNullConstant(HNullConstant* null_constant) OVERRIDE;
   void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
+  void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
+  void VisitLoadString(HLoadString* instr) OVERRIDE;
   void VisitNewArray(HNewArray* instr) OVERRIDE;
+  void VisitParameterValue(HParameterValue* instr) OVERRIDE;
   void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info);
   void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
   void VisitInstanceFieldGet(HInstanceFieldGet* instr) OVERRIDE;
   void VisitStaticFieldGet(HStaticFieldGet* instr) OVERRIDE;
   void VisitInvoke(HInvoke* instr) OVERRIDE;
   void VisitArrayGet(HArrayGet* instr) OVERRIDE;
+  void VisitCheckCast(HCheckCast* instr) OVERRIDE;
+  void VisitNullCheck(HNullCheck* instr) OVERRIDE;
+  void VisitFakeString(HFakeString* instr) OVERRIDE;
   void UpdateReferenceTypeInfo(HInstruction* instr,
                                uint16_t type_idx,
                                const DexFile& dex_file,
@@ -45,8 +61,33 @@
 
  private:
   StackHandleScopeCollection* handles_;
+  ReferenceTypeInfo::TypeHandle object_class_handle_;
+  ReferenceTypeInfo::TypeHandle class_class_handle_;
+  ReferenceTypeInfo::TypeHandle string_class_handle_;
+  GrowableArray<HInstruction*>* worklist_;
+
+  static constexpr size_t kDefaultWorklistSize = 8;
 };
 
+ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
+                                                   StackHandleScopeCollection* handles,
+                                                   const char* name)
+    : HOptimization(graph, name),
+      handles_(handles),
+      worklist_(graph->GetArena(), kDefaultWorklistSize) {
+  ClassLinker* linker = Runtime::Current()->GetClassLinker();
+  object_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject));
+  string_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangString));
+  class_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangClass));
+
+  if (kIsDebugBuild) {
+    ScopedObjectAccess soa(Thread::Current());
+    DCHECK(ReferenceTypeInfo::IsValidHandle(object_class_handle_));
+    DCHECK(ReferenceTypeInfo::IsValidHandle(class_class_handle_));
+    DCHECK(ReferenceTypeInfo::IsValidHandle(string_class_handle_));
+  }
+}
+
 void ReferenceTypePropagation::Run() {
   // To properly propagate type info we need to visit in the dominator-based order.
   // Reverse post order guarantees a node's dominators are visited first.
@@ -55,29 +96,122 @@
     VisitBasicBlock(it.Current());
   }
   ProcessWorklist();
+
+  if (kIsDebugBuild) {
+    // TODO: move this to the graph checker.
+    ScopedObjectAccess soa(Thread::Current());
+    for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+      HBasicBlock* block = it.Current();
+      for (HInstructionIterator iti(block->GetInstructions()); !iti.Done(); iti.Advance()) {
+        HInstruction* instr = iti.Current();
+        if (instr->GetType() == Primitive::kPrimNot) {
+          DCHECK(instr->GetReferenceTypeInfo().IsValid())
+              << "Invalid RTI for instruction: " << instr->DebugName();
+          if (instr->IsBoundType()) {
+            DCHECK(instr->AsBoundType()->GetUpperBound().IsValid());
+          } else if (instr->IsLoadClass()) {
+            DCHECK(instr->AsLoadClass()->GetReferenceTypeInfo().IsExact());
+            DCHECK(instr->AsLoadClass()->GetLoadedClassRTI().IsValid());
+          } else if (instr->IsNullCheck()) {
+            DCHECK(instr->GetReferenceTypeInfo().IsEqual(instr->InputAt(0)->GetReferenceTypeInfo()))
+                << "NullCheck " << instr->GetReferenceTypeInfo()
+                << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo();
+          }
+        }
+      }
+    }
+  }
 }
 
 void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
-  // TODO: handle other instructions that give type info
-  // (array accesses)
+  RTPVisitor visitor(graph_,
+                     handles_,
+                     &worklist_,
+                     object_class_handle_,
+                     class_class_handle_,
+                     string_class_handle_);
+  // Handle Phis first as there might be instructions in the same block who depend on them.
+  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+    VisitPhi(it.Current()->AsPhi());
+  }
 
-  RTPVisitor visitor(graph_, handles_);
-  // Initialize exact types first for faster convergence.
+  // Handle instructions.
   for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
     HInstruction* instr = it.Current();
     instr->Accept(&visitor);
   }
 
-  // Handle Phis.
-  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-    VisitPhi(it.Current()->AsPhi());
-  }
-
   // Add extra nodes to bound types.
   BoundTypeForIfNotNull(block);
   BoundTypeForIfInstanceOf(block);
 }
 
+// Create a bound type for the given object narrowing the type as much as possible.
+// The BoundType upper values for the super type and can_be_null will be taken from
+// load_class.GetLoadedClassRTI() and upper_can_be_null.
+static HBoundType* CreateBoundType(ArenaAllocator* arena,
+                                   HInstruction* obj,
+                                   HLoadClass* load_class,
+                                   bool upper_can_be_null)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+  ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null);
+  // Narrow the type as much as possible.
+  if (class_rti.GetTypeHandle()->IsFinal()) {
+    bound_type->SetReferenceTypeInfo(
+        ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true));
+  } else if (obj_rti.IsValid() && class_rti.IsSupertypeOf(obj_rti)) {
+    bound_type->SetReferenceTypeInfo(obj_rti);
+  } else {
+    bound_type->SetReferenceTypeInfo(
+        ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
+  }
+  return bound_type;
+}
+
+// Check if we should create a bound type for the given object at the specified
+// position. Because of inlining and the fact we run RTP more than once and we
+// might have a HBoundType already. If we do, we should not create a new one.
+// In this case we also assert that there are no other uses of the object (except
+// the bound type) dominated by the specified dominator_instr or dominator_block.
+static bool ShouldCreateBoundType(HInstruction* position,
+                                  HInstruction* obj,
+                                  ReferenceTypeInfo upper_bound,
+                                  HInstruction* dominator_instr,
+                                  HBasicBlock* dominator_block)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  // If the position where we should insert the bound type is not already a
+  // a bound type then we need to create one.
+  if (position == nullptr || !position->IsBoundType()) {
+    return true;
+  }
+
+  HBoundType* existing_bound_type = position->AsBoundType();
+  if (existing_bound_type->GetUpperBound().IsSupertypeOf(upper_bound)) {
+    if (kIsDebugBuild) {
+      // Check that the existing HBoundType dominates all the uses.
+      for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
+        HInstruction* user = it.Current()->GetUser();
+        if (dominator_instr != nullptr) {
+          DCHECK(!dominator_instr->StrictlyDominates(user)
+              || user == existing_bound_type
+              || existing_bound_type->StrictlyDominates(user));
+        } else if (dominator_block != nullptr) {
+          DCHECK(!dominator_block->Dominates(user->GetBlock())
+              || user == existing_bound_type
+              || existing_bound_type->StrictlyDominates(user));
+        }
+      }
+    }
+  } else {
+    // TODO: if the current bound type is a refinement we could update the
+    // existing_bound_type with the a new upper limit. However, we also need to
+    // update its users and have access to the work list.
+  }
+  return false;
+}
+
 void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
   HIf* ifInstruction = block->GetLastInstruction()->AsIf();
   if (ifInstruction == nullptr) {
@@ -116,8 +250,23 @@
     HInstruction* user = it.Current()->GetUser();
     if (notNullBlock->Dominates(user->GetBlock())) {
       if (bound_type == nullptr) {
-        bound_type = new (graph_->GetArena()) HBoundType(obj, ReferenceTypeInfo::CreateTop(false));
-        notNullBlock->InsertInstructionBefore(bound_type, notNullBlock->GetFirstInstruction());
+        ScopedObjectAccess soa(Thread::Current());
+        HInstruction* insert_point = notNullBlock->GetFirstInstruction();
+        ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
+            object_class_handle_, /* is_exact */ true);
+        if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
+          bound_type = new (graph_->GetArena()) HBoundType(
+              obj, object_rti, /* bound_can_be_null */ false);
+          if (obj->GetReferenceTypeInfo().IsValid()) {
+            bound_type->SetReferenceTypeInfo(obj->GetReferenceTypeInfo());
+          }
+          notNullBlock->InsertInstructionBefore(bound_type, insert_point);
+        } else {
+          // We already have a bound type on the position we would need to insert
+          // the new one. The existing bound type should dominate all the users
+          // (dchecked) so there's no need to continue.
+          break;
+        }
       }
       user->ReplaceInput(bound_type, it.Current()->GetIndex());
     }
@@ -171,25 +320,23 @@
     HInstruction* user = it.Current()->GetUser();
     if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
       if (bound_type == nullptr) {
+        ScopedObjectAccess soa(Thread::Current());
         HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
-
-        ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
         ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
-        bound_type = new (graph_->GetArena()) HBoundType(obj, class_rti);
-
-        // Narrow the type as much as possible.
-        {
-          ScopedObjectAccess soa(Thread::Current());
-          if (!load_class->IsResolved() || class_rti.IsSupertypeOf(obj_rti)) {
-            bound_type->SetReferenceTypeInfo(obj_rti);
-          } else {
-            bound_type->SetReferenceTypeInfo(
-                ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
-          }
+        HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
+        if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
+          bound_type = CreateBoundType(
+              graph_->GetArena(),
+              obj,
+              load_class,
+              false /* InstanceOf ensures the object is not null. */);
+          instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
+        } else {
+          // We already have a bound type on the position we would need to insert
+          // the new one. The existing bound type should dominate all the users
+          // (dchecked) so there's no need to continue.
+          break;
         }
-
-        instanceOfTrueBlock->InsertInstructionBefore(
-            bound_type, instanceOfTrueBlock->GetFirstInstruction());
       }
       user->ReplaceInput(bound_type, it.Current()->GetIndex());
     }
@@ -199,11 +346,32 @@
 void RTPVisitor::SetClassAsTypeInfo(HInstruction* instr,
                                     mirror::Class* klass,
                                     bool is_exact) {
-  if (klass != nullptr) {
+  if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) {
+    // Calls to String.<init> are replaced with a StringFactory.
+    if (kIsDebugBuild) {
+      ScopedObjectAccess soa(Thread::Current());
+      ClassLinker* cl = Runtime::Current()->GetClassLinker();
+      mirror::DexCache* dex_cache = cl->FindDexCache(instr->AsInvoke()->GetDexFile());
+      ArtMethod* method = dex_cache->GetResolvedMethod(
+          instr->AsInvoke()->GetDexMethodIndex(), cl->GetImagePointerSize());
+      DCHECK(method != nullptr);
+      mirror::Class* declaring_class = method->GetDeclaringClass();
+      DCHECK(declaring_class != nullptr);
+      DCHECK(declaring_class->IsStringClass())
+          << "Expected String class: " << PrettyDescriptor(declaring_class);
+      DCHECK(method->IsConstructor())
+          << "Expected String.<init>: " << PrettyMethod(method);
+    }
+    instr->SetReferenceTypeInfo(
+        ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
+  } else if (klass != nullptr) {
     ScopedObjectAccess soa(Thread::Current());
-    MutableHandle<mirror::Class> handle = handles_->NewHandle(klass);
+    ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(klass);
     is_exact = is_exact || klass->IsFinal();
     instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
+  } else {
+    instr->SetReferenceTypeInfo(
+        ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
   }
 }
 
@@ -219,6 +387,13 @@
   SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
 }
 
+void RTPVisitor::VisitNullConstant(HNullConstant* instr) {
+  // TODO: The null constant could be bound contextually (e.g. based on return statements)
+  // to a more precise type.
+  instr->SetReferenceTypeInfo(
+      ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
+}
+
 void RTPVisitor::VisitNewInstance(HNewInstance* instr) {
   UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
 }
@@ -227,6 +402,13 @@
   UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
 }
 
+void RTPVisitor::VisitParameterValue(HParameterValue* instr) {
+  if (instr->GetType() == Primitive::kPrimNot) {
+    // TODO: parse the signature and add precise types for the parameters.
+    SetClassAsTypeInfo(instr, nullptr, /* is_exact */ false);
+  }
+}
+
 void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr,
                                            const FieldInfo& info) {
   // The field index is unknown only during tests.
@@ -238,10 +420,10 @@
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   mirror::DexCache* dex_cache = cl->FindDexCache(info.GetDexFile());
   ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache);
-  if (field != nullptr) {
-    mirror::Class* klass = field->GetType<false>();
-    SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
-  }
+  // TODO: There are certain cases where we can't resolve the field.
+  // b/21914925 is open to keep track of a repro case for this issue.
+  mirror::Class* klass = (field == nullptr) ? nullptr : field->GetType<false>();
+  SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
 
 void RTPVisitor::VisitInstanceFieldGet(HInstanceFieldGet* instr) {
@@ -258,12 +440,60 @@
       Runtime::Current()->GetClassLinker()->FindDexCache(instr->GetDexFile());
   // Get type from dex cache assuming it was populated by the verifier.
   mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
-  if (resolved_class != nullptr) {
-    Handle<mirror::Class> handle = handles_->NewHandle(resolved_class);
-    instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
+  // TODO: investigating why we are still getting unresolved classes: b/22821472.
+  ReferenceTypeInfo::TypeHandle handle = (resolved_class != nullptr)
+    ? handles_->NewHandle(resolved_class)
+    : object_class_handle_;
+  instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
+  instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true));
+}
+
+void RTPVisitor::VisitClinitCheck(HClinitCheck* instr) {
+  instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
+}
+
+void RTPVisitor::VisitLoadString(HLoadString* instr) {
+  instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
+}
+
+void RTPVisitor::VisitNullCheck(HNullCheck* instr) {
+  ScopedObjectAccess soa(Thread::Current());
+  ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
+  DCHECK(parent_rti.IsValid());
+  instr->SetReferenceTypeInfo(parent_rti);
+}
+
+void RTPVisitor::VisitFakeString(HFakeString* instr) {
+  instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
+}
+
+void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
+  HInstruction* obj = check_cast->InputAt(0);
+  HBoundType* bound_type = nullptr;
+  for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
+    HInstruction* user = it.Current()->GetUser();
+    if (check_cast->StrictlyDominates(user)) {
+      if (bound_type == nullptr) {
+        ScopedObjectAccess soa(Thread::Current());
+        HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+        ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+        if (ShouldCreateBoundType(check_cast->GetNext(), obj, class_rti, check_cast, nullptr)) {
+          bound_type = CreateBoundType(
+              GetGraph()->GetArena(),
+              obj,
+              load_class,
+              true /* CheckCast succeeds for nulls. */);
+          check_cast->GetBlock()->InsertInstructionAfter(bound_type, check_cast);
+        } else {
+          // We already have a bound type on the position we would need to insert
+          // the new one. The existing bound type should dominate all the users
+          // (dchecked) so there's no need to continue.
+          break;
+        }
+      }
+      user->ReplaceInput(bound_type, it.Current()->GetIndex());
+    }
   }
-  Handle<mirror::Class> class_handle = handles_->NewHandle(mirror::Class::GetJavaLangClass());
-  instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_handle, /* is_exact */ true));
 }
 
 void ReferenceTypePropagation::VisitPhi(HPhi* phi) {
@@ -290,29 +520,54 @@
 
 ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo& a,
                                                        const ReferenceTypeInfo& b) {
-  bool is_exact = a.IsExact() && b.IsExact();
-  bool is_top = a.IsTop() || b.IsTop();
-  Handle<mirror::Class> type_handle;
-
-  if (!is_top) {
-    if (a.GetTypeHandle().Get() == b.GetTypeHandle().Get()) {
-      type_handle = a.GetTypeHandle();
-    } else if (a.IsSupertypeOf(b)) {
-      type_handle = a.GetTypeHandle();
-      is_exact = false;
-    } else if (b.IsSupertypeOf(a)) {
-      type_handle = b.GetTypeHandle();
-      is_exact = false;
-    } else {
-      // TODO: Find a common super class.
-      is_top = true;
-      is_exact = false;
-    }
+  if (!b.IsValid()) {
+    return a;
+  }
+  if (!a.IsValid()) {
+    return b;
   }
 
-  return is_top
-      ? ReferenceTypeInfo::CreateTop(is_exact)
-      : ReferenceTypeInfo::Create(type_handle, is_exact);
+  bool is_exact = a.IsExact() && b.IsExact();
+  Handle<mirror::Class> type_handle;
+
+  if (a.GetTypeHandle().Get() == b.GetTypeHandle().Get()) {
+    type_handle = a.GetTypeHandle();
+  } else if (a.IsSupertypeOf(b)) {
+    type_handle = a.GetTypeHandle();
+    is_exact = false;
+  } else if (b.IsSupertypeOf(a)) {
+    type_handle = b.GetTypeHandle();
+    is_exact = false;
+  } else {
+    // TODO: Find the first common super class.
+    type_handle = object_class_handle_;
+    is_exact = false;
+  }
+
+  return ReferenceTypeInfo::Create(type_handle, is_exact);
+}
+
+static void UpdateArrayGet(HArrayGet* instr,
+                           StackHandleScopeCollection* handles,
+                           ReferenceTypeInfo::TypeHandle object_class_handle)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  DCHECK_EQ(Primitive::kPrimNot, instr->GetType());
+
+  ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
+  DCHECK(parent_rti.IsValid());
+
+  Handle<mirror::Class> handle = parent_rti.GetTypeHandle();
+  if (handle->IsObjectArrayClass()) {
+    ReferenceTypeInfo::TypeHandle component_handle = handles->NewHandle(handle->GetComponentType());
+    instr->SetReferenceTypeInfo(
+        ReferenceTypeInfo::Create(component_handle, /* is_exact */ false));
+  } else {
+    // We don't know what the parent actually is, so we fallback to object.
+    instr->SetReferenceTypeInfo(
+        ReferenceTypeInfo::Create(object_class_handle, /* is_exact */ false));
+  }
+
+  return;
 }
 
 bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr) {
@@ -323,6 +578,15 @@
     UpdateBoundType(instr->AsBoundType());
   } else if (instr->IsPhi()) {
     UpdatePhi(instr->AsPhi());
+  } else if (instr->IsNullCheck()) {
+    ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
+    if (parent_rti.IsValid()) {
+      instr->SetReferenceTypeInfo(parent_rti);
+    }
+  } else if (instr->IsArrayGet()) {
+    // TODO: consider if it's worth "looking back" and bounding the input object
+    // to an array type.
+    UpdateArrayGet(instr->AsArrayGet(), handles_, object_class_handle_);
   } else {
     LOG(FATAL) << "Invalid instruction (should not get here)";
   }
@@ -340,45 +604,45 @@
   mirror::DexCache* dex_cache = cl->FindDexCache(instr->GetDexFile());
   ArtMethod* method = dex_cache->GetResolvedMethod(
       instr->GetDexMethodIndex(), cl->GetImagePointerSize());
-  if (method != nullptr) {
-    mirror::Class* klass = method->GetReturnType(false);
-    SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
-  }
+  mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false);
+  SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
 
 void RTPVisitor::VisitArrayGet(HArrayGet* instr) {
   if (instr->GetType() != Primitive::kPrimNot) {
     return;
   }
-
-  HInstruction* parent = instr->InputAt(0);
   ScopedObjectAccess soa(Thread::Current());
-  Handle<mirror::Class> handle = parent->GetReferenceTypeInfo().GetTypeHandle();
-  if (handle.GetReference() != nullptr && handle->IsObjectArrayClass()) {
-    SetClassAsTypeInfo(instr, handle->GetComponentType(), /* is_exact */ false);
+  UpdateArrayGet(instr, handles_, object_class_handle_);
+  if (!instr->GetReferenceTypeInfo().IsValid()) {
+    worklist_->Add(instr);
   }
 }
 
 void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) {
   ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
-  // Be sure that we don't go over the bounded type.
-  ReferenceTypeInfo bound_rti = instr->GetBoundType();
-  if (!bound_rti.IsSupertypeOf(new_rti)) {
-    new_rti = bound_rti;
+  if (!new_rti.IsValid()) {
+    return;  // No new info yet.
+  }
+
+  // Make sure that we don't go over the bounded type.
+  ReferenceTypeInfo upper_bound_rti = instr->GetUpperBound();
+  if (!upper_bound_rti.IsSupertypeOf(new_rti)) {
+    new_rti = upper_bound_rti;
   }
   instr->SetReferenceTypeInfo(new_rti);
 }
 
 void ReferenceTypePropagation::UpdatePhi(HPhi* instr) {
   ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
-  if (new_rti.IsTop() && !new_rti.IsExact()) {
-    // Early return if we are Top and inexact.
+  if (new_rti.IsValid() && new_rti.IsObjectClass() && !new_rti.IsExact()) {
+    // Early return if we are Object and inexact.
     instr->SetReferenceTypeInfo(new_rti);
     return;
   }
   for (size_t i = 1; i < instr->InputCount(); i++) {
     new_rti = MergeTypes(new_rti, instr->InputAt(i)->GetReferenceTypeInfo());
-    if (new_rti.IsTop()) {
+    if (new_rti.IsValid() && new_rti.IsObjectClass()) {
       if (!new_rti.IsExact()) {
         break;
       } else {
@@ -392,21 +656,31 @@
 // Re-computes and updates the nullability of the instruction. Returns whether or
 // not the nullability was changed.
 bool ReferenceTypePropagation::UpdateNullability(HInstruction* instr) {
-  DCHECK(instr->IsPhi() || instr->IsBoundType());
+  DCHECK(instr->IsPhi()
+      || instr->IsBoundType()
+      || instr->IsNullCheck()
+      || instr->IsArrayGet());
 
-  if (!instr->IsPhi()) {
+  if (!instr->IsPhi() && !instr->IsBoundType()) {
     return false;
   }
 
-  HPhi* phi = instr->AsPhi();
-  bool existing_can_be_null = phi->CanBeNull();
-  bool new_can_be_null = false;
-  for (size_t i = 0; i < phi->InputCount(); i++) {
-    new_can_be_null |= phi->InputAt(i)->CanBeNull();
+  bool existing_can_be_null = instr->CanBeNull();
+  if (instr->IsPhi()) {
+    HPhi* phi = instr->AsPhi();
+    bool new_can_be_null = false;
+    for (size_t i = 0; i < phi->InputCount(); i++) {
+      if (phi->InputAt(i)->CanBeNull()) {
+        new_can_be_null = true;
+        break;
+      }
+    }
+    phi->SetCanBeNull(new_can_be_null);
+  } else if (instr->IsBoundType()) {
+    HBoundType* bound_type = instr->AsBoundType();
+    bound_type->SetCanBeNull(instr->InputAt(0)->CanBeNull() && bound_type->GetUpperCanBeNull());
   }
-  phi->SetCanBeNull(new_can_be_null);
-
-  return existing_can_be_null != new_can_be_null;
+  return existing_can_be_null != instr->CanBeNull();
 }
 
 void ReferenceTypePropagation::ProcessWorklist() {
@@ -419,14 +693,18 @@
 }
 
 void ReferenceTypePropagation::AddToWorklist(HInstruction* instruction) {
-  DCHECK_EQ(instruction->GetType(), Primitive::kPrimNot) << instruction->GetType();
+  DCHECK_EQ(instruction->GetType(), Primitive::kPrimNot)
+      << instruction->DebugName() << ":" << instruction->GetType();
   worklist_.Add(instruction);
 }
 
 void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
   for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
     HInstruction* user = it.Current()->GetUser();
-    if (user->IsPhi() || user->IsBoundType()) {
+    if (user->IsPhi()
+       || user->IsBoundType()
+       || user->IsNullCheck()
+       || (user->IsArrayGet() && (user->GetType() == Primitive::kPrimNot))) {
       AddToWorklist(user);
     }
   }
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 11f5ac9..14d4a82 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -30,10 +30,9 @@
  */
 class ReferenceTypePropagation : public HOptimization {
  public:
-  ReferenceTypePropagation(HGraph* graph, StackHandleScopeCollection* handles)
-    : HOptimization(graph, kReferenceTypePropagationPassName),
-      handles_(handles),
-      worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+  ReferenceTypePropagation(HGraph* graph,
+                           StackHandleScopeCollection* handles,
+                           const char* name = kReferenceTypePropagationPassName);
 
   void Run() OVERRIDE;
 
@@ -60,6 +59,10 @@
 
   GrowableArray<HInstruction*> worklist_;
 
+  ReferenceTypeInfo::TypeHandle object_class_handle_;
+  ReferenceTypeInfo::TypeHandle class_class_handle_;
+  ReferenceTypeInfo::TypeHandle string_class_handle_;
+
   static constexpr size_t kDefaultWorklistSize = 8;
 
   DISALLOW_COPY_AND_ASSIGN(ReferenceTypePropagation);
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index ff2e6ad..2c34e4d 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -570,7 +570,9 @@
   if (instruction->GetBlock()->IsInTry() && instruction->CanThrow()) {
     HTryBoundary* try_block = instruction->GetBlock()->GetTryEntry();
     for (HExceptionHandlerIterator it(*try_block); !it.Done(); it.Advance()) {
-      GrowableArray<HInstruction*>* handler_locals = GetLocalsFor(it.Current());
+      HBasicBlock* handler = it.Current();
+      handler->AddExceptionalPredecessor(instruction);
+      GrowableArray<HInstruction*>* handler_locals = GetLocalsFor(handler);
       for (size_t i = 0, e = current_locals_->Size(); i < e; ++i) {
         HInstruction* local_value = current_locals_->Get(i);
         if (local_value != nullptr) {
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 701dbb0..40502c1 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -225,7 +225,7 @@
         // SsaLivenessAnalysis.
         for (size_t i = 0, e = environment->Size(); i < e; ++i) {
           HInstruction* instruction = environment->GetInstructionAt(i);
-          bool should_be_live = ShouldBeLiveForEnvironment(instruction);
+          bool should_be_live = ShouldBeLiveForEnvironment(current, instruction);
           if (should_be_live) {
             DCHECK(instruction->HasSsaIndex());
             live_in->SetBit(instruction->GetSsaIndex());
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 220ee6a..a7044de 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -1201,8 +1201,14 @@
   // Update the live_out set of the block and returns whether it has changed.
   bool UpdateLiveOut(const HBasicBlock& block);
 
-  static bool ShouldBeLiveForEnvironment(HInstruction* instruction) {
+  // Returns whether `instruction` in an HEnvironment held by `env_holder`
+  // should be kept live by the HEnvironment.
+  static bool ShouldBeLiveForEnvironment(HInstruction* env_holder,
+                                         HInstruction* instruction) {
     if (instruction == nullptr) return false;
+    // A value that's not live in compiled code may still be needed in interpreter,
+    // due to code motion, etc.
+    if (env_holder->IsDeoptimize()) return true;
     if (instruction->GetBlock()->GetGraph()->IsDebuggable()) return true;
     return instruction->GetType() == Primitive::kPrimNot;
   }
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 20f61f9..cb01cea 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -32,7 +32,7 @@
 // Include results file (generated manually)
 #include "assembler_thumb_test_expected.cc.inc"
 
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
 // This controls whether the results are printed to the
 // screen or compared against the expected output.
 // To generate new expected output, set this to true and
@@ -72,7 +72,7 @@
 }
 
 std::string GetToolsDir() {
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
   // This will only work on the host.  There is no as, objcopy or objdump on the device.
   static std::string toolsdir;
 
@@ -89,7 +89,7 @@
 }
 
 void DumpAndCheck(std::vector<uint8_t>& code, const char* testname, const char* const* results) {
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
   static std::string toolsdir = GetToolsDir();
 
   ScratchFile file;
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index fa85ada..44efc65 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1515,6 +1515,14 @@
 }
 
 
+void X86Assembler::repe_cmpsw() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0xF3);
+  EmitUint8(0xA7);
+}
+
+
 X86Assembler* X86Assembler::lock() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF0);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index d1b4e1d..e2abcde 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -465,6 +465,7 @@
   void jmp(Label* label);
 
   void repne_scasw();
+  void repe_cmpsw();
 
   X86Assembler* lock();
   void cmpxchgl(const Address& address, Register reg);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index aacc57b..0e8c4ae 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -196,4 +196,10 @@
   DriverStr(expected, "Repnescasw");
 }
 
+TEST_F(AssemblerX86Test, Repecmpsw) {
+  GetAssembler()->repe_cmpsw();
+  const char* expected = "repe cmpsw\n";
+  DriverStr(expected, "Repecmpsw");
+}
+
 }  // namespace art
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index f35f51c..93c90db 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -2073,6 +2073,14 @@
 }
 
 
+void X86_64Assembler::repe_cmpsw() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0xF3);
+  EmitUint8(0xA7);
+}
+
+
 void X86_64Assembler::LoadDoubleConstant(XmmRegister dst, double value) {
   // TODO: Need to have a code constants table.
   int64_t constant = bit_cast<int64_t, double>(value);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 61ffeab..0cd3197 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -603,6 +603,7 @@
   void bswapq(CpuRegister dst);
 
   void repne_scasw();
+  void repe_cmpsw();
 
   //
   // Macros for High-level operations.
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 6da5c35..2c1a6a1 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -35,7 +35,7 @@
   ASSERT_EQ(static_cast<size_t>(5), buffer.Size());
 }
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 static constexpr size_t kRandomIterations = 1000;  // Devices might be puny, don't stress them...
 #else
 static constexpr size_t kRandomIterations = 100000;  // Hosts are pretty powerful.
@@ -1263,4 +1263,10 @@
   DriverStr(expected, "Repnescasw");
 }
 
+TEST_F(AssemblerX86_64Test, Repecmpsw) {
+  GetAssembler()->repe_cmpsw();
+  const char* expected = "repe cmpsw\n";
+  DriverStr(expected, "Repecmpsw");
+}
+
 }  // namespace art
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index bffb3b5..75d6137 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -280,6 +280,18 @@
   UsageError("      Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
   UsageError("      Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
   UsageError("");
+  UsageError("  --inline-depth-limit=<depth-limit>: the depth limit of inlining for fine tuning");
+  UsageError("      the compiler. A zero value will disable inlining. Honored only by Optimizing.");
+  UsageError("      Example: --inline-depth-limit=%d", CompilerOptions::kDefaultInlineDepthLimit);
+  UsageError("      Default: %d", CompilerOptions::kDefaultInlineDepthLimit);
+  UsageError("");
+  UsageError("  --inline-max-code-units=<code-units-count>: the maximum code units that a method");
+  UsageError("      can have to be considered for inlining. A zero value will disable inlining.");
+  UsageError("      Honored only by Optimizing.");
+  UsageError("      Example: --inline-max-code-units=%d",
+             CompilerOptions::kDefaultInlineMaxCodeUnits);
+  UsageError("      Default: %d", CompilerOptions::kDefaultInlineMaxCodeUnits);
+  UsageError("");
   UsageError("  --dump-timing: display a breakdown of where time was spent");
   UsageError("");
   UsageError("  --include-patch-information: Include patching information so the generated code");
@@ -550,6 +562,8 @@
     int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
     int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
     int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
+    int inline_depth_limit = CompilerOptions::kDefaultInlineDepthLimit;
+    int inline_max_code_units = CompilerOptions::kDefaultInlineMaxCodeUnits;
 
     // Profile file to use
     double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
@@ -720,6 +734,22 @@
         if (num_dex_methods_threshold < 0) {
           Usage("--num-dex-methods passed a negative value %s", num_dex_methods_threshold);
         }
+      } else if (option.starts_with("--inline-depth-limit=")) {
+        const char* limit = option.substr(strlen("--inline-depth-limit=")).data();
+        if (!ParseInt(limit, &inline_depth_limit)) {
+          Usage("Failed to parse --inline-depth-limit '%s' as an integer", limit);
+        }
+        if (inline_depth_limit < 0) {
+          Usage("--inline-depth-limit passed a negative value %s", inline_depth_limit);
+        }
+      } else if (option.starts_with("--inline-max-code-units=")) {
+        const char* code_units = option.substr(strlen("--inline-max-code-units=")).data();
+        if (!ParseInt(code_units, &inline_max_code_units)) {
+          Usage("Failed to parse --inline-max-code-units '%s' as an integer", code_units);
+        }
+        if (inline_max_code_units < 0) {
+          Usage("--inline-max-code-units passed a negative value %s", inline_max_code_units);
+        }
       } else if (option == "--host") {
         is_host_ = true;
       } else if (option == "--runtime-arg") {
@@ -992,6 +1022,8 @@
                                                 small_method_threshold,
                                                 tiny_method_threshold,
                                                 num_dex_methods_threshold,
+                                                inline_depth_limit,
+                                                inline_max_code_units,
                                                 include_patch_information,
                                                 top_k_profile_threshold,
                                                 debuggable,
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 2ead4a2..44787a7 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -1117,6 +1117,9 @@
       opcode1 = opcode_tmp.c_str();
     }
     break;
+  case 0xA7:
+    opcode1 = (prefix[2] == 0x66 ? "cmpsw" : "cmpsl");
+    break;
   case 0xAF:
     opcode1 = (prefix[2] == 0x66 ? "scasw" : "scasl");
     break;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 6da516c..466dacb 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -177,10 +177,15 @@
     PatchVisitor(PatchOat* patcher, mirror::Object* copy) : patcher_(patcher), copy_(copy) {}
     ~PatchVisitor() {}
     void operator() (mirror::Object* obj, MemberOffset off, bool b) const
-      REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+        REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
     // For reference classes.
     void operator() (mirror::Class* cls, mirror::Reference* ref) const
-      REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+        REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+    // TODO: Consider using these for updating native class roots?
+    void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+        const {}
+    void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
   private:
     PatchOat* const patcher_;
     mirror::Object* const copy_;
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 013e3f7..ce3e6d1 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -39,6 +39,7 @@
   base/unix_file/random_access_file_utils.cc \
   check_jni.cc \
   class_linker.cc \
+  class_table.cc \
   common_throws.cc \
   debugger.cc \
   dex_file.cc \
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 2f2654d..be9af98 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -171,6 +171,7 @@
 
   // Read barrier
   qpoints->pReadBarrierJni = ReadBarrierJni;
+  qpoints->pReadBarrierSlow = artReadBarrierSlow;
 }
 
 }  // namespace art
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index f8590d3..28d1942 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -16,7 +16,7 @@
 
 #include "instruction_set_features_arm.h"
 
-#if defined(HAVE_ANDROID_OS) && defined(__arm__)
+#if defined(__ANDROID__) && defined(__arm__)
 #include <sys/auxv.h>
 #include <asm/hwcap.h>
 #endif
@@ -166,7 +166,7 @@
   bool has_div = false;
   bool has_lpae = false;
 
-#if defined(HAVE_ANDROID_OS) && defined(__arm__)
+#if defined(__ANDROID__) && defined(__arm__)
   uint64_t hwcaps = getauxval(AT_HWCAP);
   LOG(INFO) << "hwcaps=" << hwcaps;
   if ((hwcaps & HWCAP_IDIVT) != 0) {
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 2000110..f6d954f 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -51,7 +51,6 @@
     sub sp, #12                                   @ 3 words of space, bottom word will hold Method*
     .cfi_adjust_cfa_offset 12
     RUNTIME_CURRENT1 \rTemp1, \rTemp2             @ Load Runtime::Current into rTemp1.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     ldr \rTemp1, [\rTemp1, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kSaveAll Method*.
     str \rTemp1, [sp, #0]                         @ Place Method* at bottom of stack.
     str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
@@ -79,7 +78,6 @@
     sub sp, #4                                    @ bottom word will hold Method*
     .cfi_adjust_cfa_offset 4
     RUNTIME_CURRENT2 \rTemp1, \rTemp2             @ Load Runtime::Current into rTemp1.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*.
     str \rTemp1, [sp, #0]                         @ Place Method* at bottom of stack.
     str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
@@ -139,7 +137,6 @@
 .macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp1, rTemp2
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
     RUNTIME_CURRENT3 \rTemp1, \rTemp2  @ Load Runtime::Current into rTemp1.
-    THIS_LOAD_REQUIRES_READ_BARRIER
      @ rTemp1 is kRefsAndArgs Method*.
     ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET]
     str \rTemp1, [sp, #0]                         @ Place Method* at bottom of stack.
@@ -171,7 +168,6 @@
     .cfi_adjust_cfa_offset -40
 .endm
 
-
 .macro RETURN_IF_RESULT_IS_ZERO
     cbnz   r0, 1f              @ result non-zero branch over
     bx     lr                  @ return
@@ -588,6 +584,59 @@
     bkpt
 END art_quick_check_cast
 
+// Restore rReg's value from [sp, #offset] if rReg is not the same as rExclude.
+.macro POP_REG_NE rReg, offset, rExclude
+    .ifnc \rReg, \rExclude
+        ldr \rReg, [sp, #\offset]   @ restore rReg
+        .cfi_restore \rReg
+    .endif
+.endm
+
+    /*
+     * Macro to insert read barrier, only used in art_quick_aput_obj.
+     * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET.
+     * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
+     */
+.macro READ_BARRIER rDest, rObj, offset
+#ifdef USE_READ_BARRIER
+    push {r0-r3, ip, lr}            @ 6 words for saved registers (used in art_quick_aput_obj)
+    .cfi_adjust_cfa_offset 24
+    .cfi_rel_offset r0, 0
+    .cfi_rel_offset r1, 4
+    .cfi_rel_offset r2, 8
+    .cfi_rel_offset r3, 12
+    .cfi_rel_offset ip, 16
+    .cfi_rel_offset lr, 20
+    sub sp, #8                      @ push padding
+    .cfi_adjust_cfa_offset 8
+    @ mov r0, r0                    @ pass ref in r0 (no-op for now since parameter ref is unused)
+    .ifnc \rObj, r1
+        mov r1, \rObj               @ pass rObj
+    .endif
+    mov r2, #\offset                @ pass offset
+    bl artReadBarrierSlow           @ artReadBarrierSlow(ref, rObj, offset)
+    @ No need to unpoison return value in r0, artReadBarrierSlow() would do the unpoisoning.
+    .ifnc \rDest, r0
+        mov \rDest, r0              @ save return value in rDest
+    .endif
+    add sp, #8                      @ pop padding
+    .cfi_adjust_cfa_offset -8
+    POP_REG_NE r0, 0, \rDest        @ conditionally restore saved registers
+    POP_REG_NE r1, 4, \rDest
+    POP_REG_NE r2, 8, \rDest
+    POP_REG_NE r3, 12, \rDest
+    POP_REG_NE ip, 16, \rDest
+    add sp, #20
+    .cfi_adjust_cfa_offset -20
+    pop {lr}                        @ restore lr
+    .cfi_adjust_cfa_offset -4
+    .cfi_restore lr
+#else
+    ldr \rDest, [\rObj, #\offset]
+    UNPOISON_HEAP_REF \rDest
+#endif  // USE_READ_BARRIER
+.endm
+
     /*
      * Entry from managed code for array put operations of objects where the value being stored
      * needs to be checked for compatibility.
@@ -609,15 +658,21 @@
     b art_quick_throw_array_bounds
 END art_quick_aput_obj_with_bound_check
 
+#ifdef USE_READ_BARRIER
+    .extern artReadBarrierSlow
+#endif
     .hidden art_quick_aput_obj
 ENTRY art_quick_aput_obj
+#ifdef USE_READ_BARRIER
+    @ The offset to .Ldo_aput_null is too large to use cbz due to expansion from READ_BARRIER macro.
+    tst r2, r2
+    beq .Ldo_aput_null
+#else
     cbz r2, .Ldo_aput_null
-    ldr r3, [r0, #MIRROR_OBJECT_CLASS_OFFSET]
-    UNPOISON_HEAP_REF r3
-    ldr ip, [r2, #MIRROR_OBJECT_CLASS_OFFSET]
-    UNPOISON_HEAP_REF ip
-    ldr r3, [r3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET]
-    UNPOISON_HEAP_REF r3
+#endif  // USE_READ_BARRIER
+    READ_BARRIER r3, r0, MIRROR_OBJECT_CLASS_OFFSET
+    READ_BARRIER ip, r2, MIRROR_OBJECT_CLASS_OFFSET
+    READ_BARRIER r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
     cmp r3, ip  @ value's type == array's component type - trivial assignability
     bne .Lcheck_assignability
 .Ldo_aput:
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 2ce2a29..0f06727 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -155,6 +155,7 @@
 
   // Read barrier
   qpoints->pReadBarrierJni = ReadBarrierJni;
+  qpoints->pReadBarrierSlow = artReadBarrierSlow;
 };
 
 }  // namespace art
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 6d9b44a..548ab47 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -31,8 +31,6 @@
     ldr xIP0, [xIP0]  // xIP0 = & (art::Runtime * art::Runtime.instance_) .
 
     // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs]  .
-    THIS_LOAD_REQUIRES_READ_BARRIER
-
     // Loads appropriate callee-save-method.
     ldr xIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET ]
 
@@ -95,8 +93,6 @@
     ldr xIP0, [xIP0]  // xIP0 = & (art::Runtime * art::Runtime.instance_) .
 
     // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefOnly]  .
-    THIS_LOAD_REQUIRES_READ_BARRIER
-
     // Loads appropriate callee-save-method.
     ldr xIP0, [xIP0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET ]
 
@@ -251,7 +247,6 @@
     ldr xIP0, [xIP0]  // xIP0 = & (art::Runtime * art::Runtime.instance_) .
 
     // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs]  .
-    THIS_LOAD_REQUIRES_READ_BARRIER
     ldr xIP0, [xIP0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ]
 
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
@@ -1119,6 +1114,62 @@
     brk 0                             // We should not return here...
 END art_quick_check_cast
 
+// Restore xReg's value from [sp, #offset] if xReg is not the same as xExclude.
+.macro POP_REG_NE xReg, offset, xExclude
+    .ifnc \xReg, \xExclude
+        ldr \xReg, [sp, #\offset]     // restore xReg
+        .cfi_restore \xReg
+    .endif
+.endm
+
+    /*
+     * Macro to insert read barrier, only used in art_quick_aput_obj.
+     * xDest, wDest and xObj are registers, offset is a defined literal such as
+     * MIRROR_OBJECT_CLASS_OFFSET. Dest needs both x and w versions of the same register to handle
+     * name mismatch between instructions. This macro uses the lower 32b of register when possible.
+     * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
+     */
+.macro READ_BARRIER xDest, wDest, xObj, offset
+#ifdef USE_READ_BARRIER
+    // Store registers used in art_quick_aput_obj (x0-x4, LR), stack is 16B aligned.
+    stp x0, x1, [sp, #-48]!
+    .cfi_adjust_cfa_offset 48
+    .cfi_rel_offset x0, 0
+    .cfi_rel_offset x1, 8
+    stp x2, x3, [sp, #16]
+    .cfi_rel_offset x2, 16
+    .cfi_rel_offset x3, 24
+    stp x4, xLR, [sp, #32]
+    .cfi_rel_offset x4, 32
+    .cfi_rel_offset x30, 40
+
+    // mov x0, x0                   // pass ref in x0 (no-op for now since parameter ref is unused)
+    .ifnc \xObj, x1
+        mov x1, \xObj               // pass xObj
+    .endif
+    mov w2, #\offset                // pass offset
+    bl artReadBarrierSlow           // artReadBarrierSlow(ref, xObj, offset)
+    // No need to unpoison return value in w0, artReadBarrierSlow() would do the unpoisoning.
+    .ifnc \wDest, w0
+        mov \wDest, w0              // save return value in wDest
+    .endif
+
+    // Conditionally restore saved registers
+    POP_REG_NE x0, 0, \xDest
+    POP_REG_NE x1, 8, \xDest
+    POP_REG_NE x2, 16, \xDest
+    POP_REG_NE x3, 24, \xDest
+    POP_REG_NE x4, 32, \xDest
+    ldr xLR, [sp, #40]
+    .cfi_restore x30
+    add sp, sp, #48
+    .cfi_adjust_cfa_offset -48
+#else
+    ldr \wDest, [\xObj, #\offset]   // Heap reference = 32b. This also zero-extends to \xDest.
+    UNPOISON_HEAP_REF \wDest
+#endif  // USE_READ_BARRIER
+.endm
+
     /*
      * Entry from managed code for array put operations of objects where the value being stored
      * needs to be checked for compatibility.
@@ -1146,17 +1197,17 @@
     b art_quick_throw_array_bounds
 END art_quick_aput_obj_with_bound_check
 
+#ifdef USE_READ_BARRIER
+    .extern artReadBarrierSlow
+#endif
 ENTRY art_quick_aput_obj
     cbz x2, .Ldo_aput_null
-    ldr w3, [x0, #MIRROR_OBJECT_CLASS_OFFSET]            // Heap reference = 32b
+    READ_BARRIER x3, w3, x0, MIRROR_OBJECT_CLASS_OFFSET     // Heap reference = 32b
                                                          // This also zero-extends to x3
-    UNPOISON_HEAP_REF w3
-    ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET]            // Heap reference = 32b
+    READ_BARRIER x4, w4, x2, MIRROR_OBJECT_CLASS_OFFSET     // Heap reference = 32b
                                                          // This also zero-extends to x4
-    UNPOISON_HEAP_REF w4
-    ldr w3, [x3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET]    // Heap reference = 32b
+    READ_BARRIER x3, w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET // Heap reference = 32b
                                                          // This also zero-extends to x3
-    UNPOISON_HEAP_REF w3
     cmp w3, w4  // value's type == array's component type - trivial assignability
     bne .Lcheck_assignability
 .Ldo_aput:
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index e6f4e7a..99c2d4d 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -18,7 +18,7 @@
 
 #include <gtest/gtest.h>
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #include "cutils/properties.h"
 #endif
 
@@ -26,7 +26,7 @@
 
 namespace art {
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #if defined(__aarch64__)
 TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) {
   LOG(WARNING) << "Test disabled due to no CPP define for A53 erratum 835769";
@@ -111,7 +111,7 @@
 }
 #endif
 
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
 TEST(InstructionSetFeaturesTest, HostFeaturesFromCppDefines) {
   std::string error_msg;
   std::unique_ptr<const InstructionSetFeatures> default_features(
diff --git a/runtime/arch/mips/entrypoints_direct_mips.h b/runtime/arch/mips/entrypoints_direct_mips.h
index b1aa3ee..f9c5315 100644
--- a/runtime/arch/mips/entrypoints_direct_mips.h
+++ b/runtime/arch/mips/entrypoints_direct_mips.h
@@ -44,7 +44,8 @@
       entrypoint == kQuickCmpgDouble ||
       entrypoint == kQuickCmpgFloat ||
       entrypoint == kQuickCmplDouble ||
-      entrypoint == kQuickCmplFloat;
+      entrypoint == kQuickCmplFloat ||
+      entrypoint == kQuickReadBarrierSlow;
 }
 
 }  // namespace art
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 09a018e..4e4b91f 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -279,6 +279,8 @@
 
   qpoints->pReadBarrierJni = ReadBarrierJni;
   static_assert(!IsDirectEntrypoint(kQuickReadBarrierJni), "Non-direct C stub marked direct.");
+  qpoints->pReadBarrierSlow = artReadBarrierSlow;
+  static_assert(IsDirectEntrypoint(kQuickReadBarrierSlow), "Direct C stub not marked direct.");
 };
 
 }  // namespace art
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 2819f92..4d5004f 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -79,7 +79,6 @@
 
     lw $t0, %got(_ZN3art7Runtime9instance_E)($gp)
     lw $t0, 0($t0)
-    THIS_LOAD_REQUIRES_READ_BARRIER
     lw $t0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($t0)
     sw $t0, 0($sp)                                # Place Method* at bottom of stack.
     sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
@@ -127,7 +126,6 @@
 
     lw $t0, %got(_ZN3art7Runtime9instance_E)($gp)
     lw $t0, 0($t0)
-    THIS_LOAD_REQUIRES_READ_BARRIER
     lw $t0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($t0)
     sw $t0, 0($sp)                                # Place Method* at bottom of stack.
     sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
@@ -219,7 +217,6 @@
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
     lw $t0, %got(_ZN3art7Runtime9instance_E)($gp)
     lw $t0, 0($t0)
-    THIS_LOAD_REQUIRES_READ_BARRIER
     lw $t0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($t0)
     sw $t0, 0($sp)                                # Place Method* at bottom of stack.
     sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
@@ -627,6 +624,76 @@
 END art_quick_check_cast
 
     /*
+     * Restore rReg's value from offset($sp) if rReg is not the same as rExclude.
+     * nReg is the register number for rReg.
+     */
+.macro POP_REG_NE rReg, nReg, offset, rExclude
+    .ifnc \rReg, \rExclude
+        lw \rReg, \offset($sp)      # restore rReg
+        .cfi_restore \nReg
+    .endif
+.endm
+
+    /*
+     * Macro to insert read barrier, only used in art_quick_aput_obj.
+     * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET.
+     * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
+     */
+.macro READ_BARRIER rDest, rObj, offset
+#ifdef USE_READ_BARRIER
+    # saved registers used in art_quick_aput_obj: a0-a2, t0-t1, t9, ra. 8 words for 16B alignment.
+    addiu  $sp, $sp, -32
+    .cfi_adjust_cfa_offset 32
+    sw     $ra, 28($sp)
+    .cfi_rel_offset 31, 28
+    sw     $t9, 24($sp)
+    .cfi_rel_offset 25, 24
+    sw     $t1, 20($sp)
+    .cfi_rel_offset 9, 20
+    sw     $t0, 16($sp)
+    .cfi_rel_offset 8, 16
+    sw     $a2, 8($sp)              # padding slot at offset 12 (padding can be any slot in the 32B)
+    .cfi_rel_offset 6, 8
+    sw     $a1, 4($sp)
+    .cfi_rel_offset 5, 4
+    sw     $a0, 0($sp)
+    .cfi_rel_offset 4, 0
+
+    # move $a0, $a0                 # pass ref in a0 (no-op for now since parameter ref is unused)
+    .ifnc \rObj, $a1
+        move $a1, \rObj             # pass rObj
+    .endif
+    addiu $a2, $zero, \offset       # pass offset
+    jal artReadBarrierSlow          # artReadBarrierSlow(ref, rObj, offset)
+    addiu  $sp, $sp, -16            # Use branch delay slot to reserve argument slots on the stack
+                                    # before the call to artReadBarrierSlow.
+    addiu  $sp, $sp, 16             # restore stack after call to artReadBarrierSlow
+    # No need to unpoison return value in v0, artReadBarrierSlow() would do the unpoisoning.
+    move \rDest, $v0                # save return value in rDest
+                                    # (rDest cannot be v0 in art_quick_aput_obj)
+
+    lw     $a0, 0($sp)              # restore registers except rDest
+                                    # (rDest can only be t0 or t1 in art_quick_aput_obj)
+    .cfi_restore 4
+    lw     $a1, 4($sp)
+    .cfi_restore 5
+    lw     $a2, 8($sp)
+    .cfi_restore 6
+    POP_REG_NE $t0, 8, 16, \rDest
+    POP_REG_NE $t1, 9, 20, \rDest
+    lw     $t9, 24($sp)
+    .cfi_restore 25
+    lw     $ra, 28($sp)             # restore $ra
+    .cfi_restore 31
+    addiu  $sp, $sp, 32
+    .cfi_adjust_cfa_offset -32
+#else
+    lw     \rDest, \offset(\rObj)
+    UNPOISON_HEAP_REF \rDest
+#endif  // USE_READ_BARRIER
+.endm
+
+    /*
      * Entry from managed code for array put operations of objects where the value being stored
      * needs to be checked for compatibility.
      * a0 = array, a1 = index, a2 = value
@@ -648,15 +715,15 @@
     move $a1, $t0
 END art_quick_aput_obj_with_bound_check
 
+#ifdef USE_READ_BARRIER
+    .extern artReadBarrierSlow
+#endif
 ENTRY art_quick_aput_obj
     beqz $a2, .Ldo_aput_null
     nop
-    lw $t0, MIRROR_OBJECT_CLASS_OFFSET($a0)
-    UNPOISON_HEAP_REF $t0
-    lw $t1, MIRROR_OBJECT_CLASS_OFFSET($a2)
-    UNPOISON_HEAP_REF $t1
-    lw $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0)
-    UNPOISON_HEAP_REF $t0
+    READ_BARRIER $t0, $a0, MIRROR_OBJECT_CLASS_OFFSET
+    READ_BARRIER $t1, $a2, MIRROR_OBJECT_CLASS_OFFSET
+    READ_BARRIER $t0, $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
     bne $t1, $t0, .Lcheck_assignability  # value's type == array's component type - trivial assignability
     nop
 .Ldo_aput:
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index 4904af9..ec02d5a 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -186,6 +186,7 @@
 
   // Read barrier
   qpoints->pReadBarrierJni = ReadBarrierJni;
+  qpoints->pReadBarrierSlow = artReadBarrierSlow;
 };
 
 }  // namespace art
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index abca70b..c30e6ca 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -89,7 +89,6 @@
     # load appropriate callee-save-method
     ld      $t1, %got(_ZN3art7Runtime9instance_E)($gp)
     ld      $t1, 0($t1)
-    THIS_LOAD_REQUIRES_READ_BARRIER
     ld      $t1, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($t1)
     sd      $t1, 0($sp)                                # Place ArtMethod* at bottom of stack.
     sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
@@ -132,7 +131,6 @@
     # load appropriate callee-save-method
     ld      $t1, %got(_ZN3art7Runtime9instance_E)($gp)
     ld      $t1, 0($t1)
-    THIS_LOAD_REQUIRES_READ_BARRIER
     ld      $t1, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($t1)
     sd      $t1, 0($sp)                                # Place Method* at bottom of stack.
     sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
@@ -255,7 +253,6 @@
     # load appropriate callee-save-method
     ld      $t1, %got(_ZN3art7Runtime9instance_E)($gp)
     ld      $t1, 0($t1)
-    THIS_LOAD_REQUIRES_READ_BARRIER
     ld      $t1, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($t1)
     sd      $t1, 0($sp)                                # Place Method* at bottom of stack.
     sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
@@ -888,6 +885,77 @@
     move $a2, rSELF                 # pass Thread::Current
 END art_quick_check_cast
 
+
+    /*
+     * Restore rReg's value from offset($sp) if rReg is not the same as rExclude.
+     * nReg is the register number for rReg.
+     */
+.macro POP_REG_NE rReg, nReg, offset, rExclude
+    .ifnc \rReg, \rExclude
+        ld \rReg, \offset($sp)      # restore rReg
+        .cfi_restore \nReg
+    .endif
+.endm
+
+    /*
+     * Macro to insert read barrier, only used in art_quick_aput_obj.
+     * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET.
+     * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
+     */
+.macro READ_BARRIER rDest, rObj, offset
+#ifdef USE_READ_BARRIER
+    # saved registers used in art_quick_aput_obj: a0-a2, t0-t1, t9, ra. 16B-aligned.
+    daddiu  $sp, $sp, -64
+    .cfi_adjust_cfa_offset 64
+    sd     $ra, 56($sp)
+    .cfi_rel_offset 31, 56
+    sd     $t9, 48($sp)
+    .cfi_rel_offset 25, 48
+    sd     $t1, 40($sp)
+    .cfi_rel_offset 13, 40
+    sd     $t0, 32($sp)
+    .cfi_rel_offset 12, 32
+    sd     $a2, 16($sp)             # padding slot at offset 24 (padding can be any slot in the 64B)
+    .cfi_rel_offset 6, 16
+    sd     $a1, 8($sp)
+    .cfi_rel_offset 5, 8
+    sd     $a0, 0($sp)
+    .cfi_rel_offset 4, 0
+
+    # move $a0, $a0                 # pass ref in a0 (no-op for now since parameter ref is unused)
+    .ifnc \rObj, $a1
+        move $a1, \rObj             # pass rObj
+    .endif
+    daddiu $a2, $zero, \offset      # pass offset
+    jal artReadBarrierSlow          # artReadBarrierSlow(ref, rObj, offset)
+    .cpreturn                       # Restore gp from t8 in branch delay slot.
+                                    # t8 may be clobbered in artReadBarrierSlow.
+    # No need to unpoison return value in v0, artReadBarrierSlow() would do the unpoisoning.
+    move \rDest, $v0                # save return value in rDest
+                                    # (rDest cannot be v0 in art_quick_aput_obj)
+
+    ld     $a0, 0($sp)              # restore registers except rDest
+                                    # (rDest can only be t0 or t1 in art_quick_aput_obj)
+    .cfi_restore 4
+    ld     $a1, 8($sp)
+    .cfi_restore 5
+    ld     $a2, 16($sp)
+    .cfi_restore 6
+    POP_REG_NE $t0, 12, 32, \rDest
+    POP_REG_NE $t1, 13, 40, \rDest
+    ld     $t9, 48($sp)
+    .cfi_restore 25
+    ld     $ra, 56($sp)             # restore $ra
+    .cfi_restore 31
+    daddiu  $sp, $sp, 64
+    .cfi_adjust_cfa_offset -64
+    SETUP_GP                        # set up gp because we are not returning
+#else
+    lwu     \rDest, \offset(\rObj)
+    UNPOISON_HEAP_REF \rDest
+#endif  // USE_READ_BARRIER
+.endm
+
     /*
      * Entry from managed code for array put operations of objects where the value being stored
      * needs to be checked for compatibility.
@@ -913,12 +981,9 @@
 ENTRY art_quick_aput_obj
     beq  $a2, $zero, .Ldo_aput_null
     nop
-    lwu $t0, MIRROR_OBJECT_CLASS_OFFSET($a0)
-    UNPOISON_HEAP_REF $t0
-    lwu $t1, MIRROR_OBJECT_CLASS_OFFSET($a2)
-    UNPOISON_HEAP_REF $t1
-    lwu $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0)
-    UNPOISON_HEAP_REF $t0
+    READ_BARRIER $t0, $a0, MIRROR_OBJECT_CLASS_OFFSET
+    READ_BARRIER $t1, $a2, MIRROR_OBJECT_CLASS_OFFSET
+    READ_BARRIER $t0, $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
     bne $t1, $t0, .Lcheck_assignability  # value's type == array's component type - trivial assignability
     nop
 .Ldo_aput:
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 0831c26..cf7db34 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1124,8 +1124,6 @@
 
 
 TEST_F(StubTest, APutObj) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
     (defined(__x86_64__) && !defined(__APPLE__))
   Thread* self = Thread::Current();
@@ -1258,8 +1256,6 @@
 }
 
 TEST_F(StubTest, AllocObject) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
     (defined(__x86_64__) && !defined(__APPLE__))
   // This will lead to OOM  error messages in the log.
@@ -1385,8 +1381,6 @@
 }
 
 TEST_F(StubTest, AllocObjectArray) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
     (defined(__x86_64__) && !defined(__APPLE__))
   // TODO: Check the "Unresolved" allocation stubs
@@ -1474,8 +1468,6 @@
 
 
 TEST_F(StubTest, StringCompareTo) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
   // TODO: Check the "Unresolved" allocation stubs
 
@@ -2152,8 +2144,6 @@
 }
 
 TEST_F(StubTest, Fields8) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
   Thread* self = Thread::Current();
 
   self->TransitionFromSuspendedToRunnable();
@@ -2166,8 +2156,6 @@
 }
 
 TEST_F(StubTest, Fields16) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
   Thread* self = Thread::Current();
 
   self->TransitionFromSuspendedToRunnable();
@@ -2180,8 +2168,6 @@
 }
 
 TEST_F(StubTest, Fields32) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
   Thread* self = Thread::Current();
 
   self->TransitionFromSuspendedToRunnable();
@@ -2193,8 +2179,6 @@
 }
 
 TEST_F(StubTest, FieldsObj) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
   Thread* self = Thread::Current();
 
   self->TransitionFromSuspendedToRunnable();
@@ -2206,8 +2190,6 @@
 }
 
 TEST_F(StubTest, Fields64) {
-  TEST_DISABLED_FOR_READ_BARRIER();
-
   Thread* self = Thread::Current();
 
   self->TransitionFromSuspendedToRunnable();
@@ -2221,8 +2203,6 @@
 TEST_F(StubTest, IMT) {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
     (defined(__x86_64__) && !defined(__APPLE__))
-  TEST_DISABLED_FOR_READ_BARRIER();
-
   Thread* self = Thread::Current();
 
   ScopedObjectAccess soa(self);
@@ -2342,8 +2322,6 @@
 
 TEST_F(StubTest, StringIndexOf) {
 #if defined(__arm__) || defined(__aarch64__)
-  TEST_DISABLED_FOR_READ_BARRIER();
-
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   // garbage is created during ClassLinker::Init
@@ -2416,4 +2394,40 @@
 #endif
 }
 
+TEST_F(StubTest, ReadBarrier) {
+#if defined(ART_USE_READ_BARRIER) && (defined(__i386__) || defined(__arm__) || \
+      defined(__aarch64__) || defined(__mips__) || (defined(__x86_64__) && !defined(__APPLE__)))
+  Thread* self = Thread::Current();
+
+  const uintptr_t readBarrierSlow = StubTest::GetEntrypoint(self, kQuickReadBarrierSlow);
+
+  // Create an object
+  ScopedObjectAccess soa(self);
+  // garbage is created during ClassLinker::Init
+
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> c(
+      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
+
+  // Build an object instance
+  Handle<mirror::Object> obj(hs.NewHandle(c->AllocObject(soa.Self())));
+
+  EXPECT_FALSE(self->IsExceptionPending());
+
+  size_t result = Invoke3(0U, reinterpret_cast<size_t>(obj.Get()),
+                          mirror::Object::ClassOffset().SizeValue(), readBarrierSlow, self);
+
+  EXPECT_FALSE(self->IsExceptionPending());
+  EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
+  mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
+  EXPECT_EQ(klass, obj->GetClass());
+
+  // Tests done.
+#else
+  LOG(INFO) << "Skipping read_barrier_slow";
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping read_barrier_slow" << std::endl;
+#endif
+}
+
 }  // namespace art
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index 737f4d1..e2632c1 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -28,6 +28,9 @@
 extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass,
                                             const mirror::Class* ref_class);
 
+// Read barrier entrypoints.
+extern "C" mirror::Object* art_quick_read_barrier_slow(mirror::Object*, mirror::Object*, uint32_t);
+
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                      QuickEntryPoints* qpoints) {
   // Interpreter
@@ -141,6 +144,7 @@
 
   // Read barrier
   qpoints->pReadBarrierJni = ReadBarrierJni;
+  qpoints->pReadBarrierSlow = art_quick_read_barrier_slow;
 };
 
 }  // namespace art
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index ebfb3fa..1da5a2f 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -33,7 +33,6 @@
     movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg)
     movl (REG_VAR(temp_reg)), REG_VAR(temp_reg)
     // Push save all callee-save method.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     pushl RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg))
     CFI_ADJUST_CFA_OFFSET(4)
     // Store esp as the top quick frame.
@@ -60,7 +59,6 @@
     movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg)
     movl (REG_VAR(temp_reg)), REG_VAR(temp_reg)
     // Push save all callee-save method.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     pushl RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg))
     CFI_ADJUST_CFA_OFFSET(4)
     // Store esp as the top quick frame.
@@ -106,7 +104,6 @@
     movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg)
     movl (REG_VAR(temp_reg)), REG_VAR(temp_reg)
     // Push save all callee-save method.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     pushl RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg))
     CFI_ADJUST_CFA_OFFSET(4)
     // Store esp as the stop quick frame.
@@ -1126,6 +1123,53 @@
     UNREACHABLE
 END_FUNCTION art_quick_check_cast
 
+// Restore reg's value if reg is not the same as exclude_reg, otherwise just adjust stack.
+MACRO2(POP_REG_NE, reg, exclude_reg)
+    .ifc RAW_VAR(reg), RAW_VAR(exclude_reg)
+      addl MACRO_LITERAL(4), %esp
+      CFI_ADJUST_CFA_OFFSET(-4)
+    .else
+      POP RAW_VAR(reg)
+    .endif
+END_MACRO
+
+    /*
+     * Macro to insert read barrier, only used in art_quick_aput_obj.
+     * obj_reg and dest_reg are registers, offset is a defined literal such as
+     * MIRROR_OBJECT_CLASS_OFFSET.
+     * pop_eax is a boolean flag, indicating if eax is popped after the call.
+     * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
+     */
+MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax)
+#ifdef USE_READ_BARRIER
+    PUSH eax                        // save registers used in art_quick_aput_obj
+    PUSH ebx
+    PUSH edx
+    PUSH ecx
+    // Outgoing argument set up
+    pushl MACRO_LITERAL((RAW_VAR(offset)))  // pass offset, double parentheses are necessary
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH RAW_VAR(obj_reg)           // pass obj_reg
+    PUSH eax                        // pass ref, just pass eax for now since parameter ref is unused
+    call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj_reg, offset)
+    // No need to unpoison return value in eax, artReadBarrierSlow() would do the unpoisoning.
+    .ifnc RAW_VAR(dest_reg), eax
+      movl %eax, REG_VAR(dest_reg)  // save loaded ref in dest_reg
+    .endif
+    addl MACRO_LITERAL(12), %esp    // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-12)
+    POP_REG_NE ecx, RAW_VAR(dest_reg) // Restore args except dest_reg
+    POP_REG_NE edx, RAW_VAR(dest_reg)
+    POP_REG_NE ebx, RAW_VAR(dest_reg)
+    .ifc RAW_VAR(pop_eax), true
+      POP_REG_NE eax, RAW_VAR(dest_reg)
+    .endif
+#else
+    movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg)
+    UNPOISON_HEAP_REF RAW_VAR(dest_reg)
+#endif  // USE_READ_BARRIER
+END_MACRO
+
     /*
      * Entry from managed code for array put operations of objects where the value being stored
      * needs to be checked for compatibility.
@@ -1149,17 +1193,20 @@
 DEFINE_FUNCTION art_quick_aput_obj
     test %edx, %edx              // store of null
     jz .Ldo_aput_null
-    movl MIRROR_OBJECT_CLASS_OFFSET(%eax), %ebx
-    UNPOISON_HEAP_REF ebx
-    movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
-    UNPOISON_HEAP_REF ebx
+    READ_BARRIER eax, MIRROR_OBJECT_CLASS_OFFSET, ebx, true
+    READ_BARRIER ebx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ebx, true
     // value's type == array's component type - trivial assignability
-#ifdef USE_HEAP_POISONING
-    PUSH eax  // save eax
+#if defined(USE_READ_BARRIER)
+    READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, false
+    cmpl %eax, %ebx
+    POP eax                      // restore eax from the push in the beginning of READ_BARRIER macro
+#elif defined(USE_HEAP_POISONING)
+    PUSH eax                     // save eax
+    // Cannot call READ_BARRIER macro here, because the above push messes up stack alignment.
     movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
     UNPOISON_HEAP_REF eax
     cmpl %eax, %ebx
-    POP  eax  // restore eax
+    POP eax                      // restore eax
 #else
     cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx
 #endif
@@ -1181,6 +1228,8 @@
     subl LITERAL(8), %esp         // alignment padding
     CFI_ADJUST_CFA_OFFSET(8)
 #ifdef USE_HEAP_POISONING
+    // This load does not need read barrier, since edx is unchanged and there's no GC safe point
+    // from last read of MIRROR_OBJECT_CLASS_OFFSET(%edx).
     movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax  // pass arg2 - type of the value to be stored
     UNPOISON_HEAP_REF eax
     PUSH eax
@@ -1696,5 +1745,15 @@
     UNREACHABLE
 END_FUNCTION art_nested_signal_return
 
+DEFINE_FUNCTION art_quick_read_barrier_slow
+    PUSH edx                        // pass arg3 - offset
+    PUSH ecx                        // pass arg2 - obj
+    PUSH eax                        // pass arg1 - ref
+    call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj, offset)
+    addl LITERAL(12), %esp          // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-12)
+    ret
+END_FUNCTION art_quick_read_barrier_slow
+
     // TODO: implement these!
 UNIMPLEMENTED art_quick_memcmp16
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index 706ae58..cf0039c 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -24,6 +24,7 @@
 #define MACRO1(macro_name, macro_arg1) .macro macro_name macro_arg1
 #define MACRO2(macro_name, macro_arg1, macro_arg2) .macro macro_name macro_arg1, macro_arg2
 #define MACRO3(macro_name, macro_arg1, macro_arg2, macro_arg3) .macro macro_name macro_arg1, macro_arg2, macro_arg3
+#define MACRO4(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4) .macro macro_name macro_arg1, macro_arg2, macro_arg3, macro_arg4
 #define END_MACRO .endm
 
 #if defined(__clang__)
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index d0ab9d5..ef1bb5f 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -29,6 +29,9 @@
 extern "C" uint32_t art_quick_assignable_from_code(const mirror::Class* klass,
                                                    const mirror::Class* ref_class);
 
+// Read barrier entrypoints.
+extern "C" mirror::Object* art_quick_read_barrier_slow(mirror::Object*, mirror::Object*, uint32_t);
+
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                      QuickEntryPoints* qpoints) {
 #if defined(__APPLE__)
@@ -145,6 +148,7 @@
 
   // Read barrier
   qpoints->pReadBarrierJni = ReadBarrierJni;
+  qpoints->pReadBarrierSlow = art_quick_read_barrier_slow;
 #endif  // __APPLE__
 };
 
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 0eeb03a..f4c9488 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -66,7 +66,6 @@
     movq %xmm14, 24(%rsp)
     movq %xmm15, 32(%rsp)
     // R10 := ArtMethod* for save all callee save frame method.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     movq RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
     // Store ArtMethod* to bottom of stack.
     movq %r10, 0(%rsp)
@@ -109,7 +108,6 @@
     movq %xmm14, 24(%rsp)
     movq %xmm15, 32(%rsp)
     // R10 := ArtMethod* for refs only callee save frame method.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     movq RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
     // Store ArtMethod* to bottom of stack.
     movq %r10, 0(%rsp)
@@ -168,7 +166,6 @@
     subq MACRO_LITERAL(80 + 4 * 8), %rsp
     CFI_ADJUST_CFA_OFFSET(80 + 4 * 8)
     // R10 := ArtMethod* for ref and args callee save frame method.
-    THIS_LOAD_REQUIRES_READ_BARRIER
     movq RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
     // Save FPRs.
     movq %xmm0, 16(%rsp)
@@ -920,8 +917,12 @@
     // Fast path tlab allocation.
     // RDI: uint32_t type_idx, RSI: ArtMethod*
     // RDX, RCX, R8, R9: free. RAX: return val.
+    // TODO: Add read barrier when this function is used.
+    // Might need a special macro since rsi and edx is 32b/64b mismatched.
     movl ART_METHOD_DEX_CACHE_TYPES_OFFSET(%rsi), %edx  // Load dex cache resolved types array
     UNPOISON_HEAP_REF edx
+    // TODO: Add read barrier when this function is used.
+    // Might need to break down into multiple instructions to get the base address in a register.
                                                                // Load the class
     movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdx, %rdi, MIRROR_OBJECT_ARRAY_COMPONENT_SIZE), %edx
     UNPOISON_HEAP_REF edx
@@ -1153,6 +1154,60 @@
 END_FUNCTION art_quick_check_cast
 
 
+// Restore reg's value if reg is not the same as exclude_reg, otherwise just adjust stack.
+MACRO2(POP_REG_NE, reg, exclude_reg)
+    .ifc RAW_VAR(reg), RAW_VAR(exclude_reg)
+      addq MACRO_LITERAL(8), %rsp
+      CFI_ADJUST_CFA_OFFSET(-8)
+    .else
+      POP RAW_VAR(reg)
+    .endif
+END_MACRO
+
+    /*
+     * Macro to insert read barrier, used in art_quick_aput_obj and art_quick_alloc_object_tlab.
+     * obj_reg and dest_reg{32|64} are registers, offset is a defined literal such as
+     * MIRROR_OBJECT_CLASS_OFFSET. dest_reg needs two versions to handle the mismatch between
+     * 64b PUSH/POP and 32b argument.
+     * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
+     *
+     * As with art_quick_aput_obj* functions, the 64b versions are in comments.
+     */
+MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
+#ifdef USE_READ_BARRIER
+    PUSH rax                            // save registers that might be used
+    PUSH rdi
+    PUSH rsi
+    PUSH rdx
+    PUSH rcx
+    SETUP_FP_CALLEE_SAVE_FRAME
+    // Outgoing argument set up
+    // movl %edi, %edi                  // pass ref, no-op for now since parameter ref is unused
+    // // movq %rdi, %rdi
+    movl REG_VAR(obj_reg), %esi         // pass obj_reg
+    // movq REG_VAR(obj_reg), %rsi
+    movl MACRO_LITERAL((RAW_VAR(offset))), %edx // pass offset, double parentheses are necessary
+    // movq MACRO_LITERAL((RAW_VAR(offset))), %rdx
+    call SYMBOL(artReadBarrierSlow)     // artReadBarrierSlow(ref, obj_reg, offset)
+    // No need to unpoison return value in rax, artReadBarrierSlow() would do the unpoisoning.
+    .ifnc RAW_VAR(dest_reg32), eax
+    // .ifnc RAW_VAR(dest_reg64), rax
+      movl %eax, REG_VAR(dest_reg32)    // save loaded ref in dest_reg
+      // movq %rax, REG_VAR(dest_reg64)
+    .endif
+    RESTORE_FP_CALLEE_SAVE_FRAME
+    POP_REG_NE rcx, RAW_VAR(dest_reg64) // Restore registers except dest_reg
+    POP_REG_NE rdx, RAW_VAR(dest_reg64)
+    POP_REG_NE rsi, RAW_VAR(dest_reg64)
+    POP_REG_NE rdi, RAW_VAR(dest_reg64)
+    POP_REG_NE rax, RAW_VAR(dest_reg64)
+#else
+    movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg32)
+    // movq RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg64)
+    UNPOISON_HEAP_REF RAW_VAR(dest_reg32) // UNPOISON_HEAP_REF only takes a 32b register
+#endif  // USE_READ_BARRIER
+END_MACRO
+
     /*
      * Entry from managed code for array put operations of objects where the value being stored
      * needs to be checked for compatibility.
@@ -1197,15 +1252,13 @@
     testl %edx, %edx                // store of null
 //  test %rdx, %rdx
     jz .Ldo_aput_null
-    movl MIRROR_OBJECT_CLASS_OFFSET(%edi), %ecx
-//  movq MIRROR_OBJECT_CLASS_OFFSET(%rdi), %rcx
-    UNPOISON_HEAP_REF ecx
-    movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ecx), %ecx
-//  movq MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %rcx
-    UNPOISON_HEAP_REF ecx
-#ifdef USE_HEAP_POISONING
-    movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax  // rax is free.
-    UNPOISON_HEAP_REF eax
+    READ_BARRIER edi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
+    // READ_BARRIER rdi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
+    READ_BARRIER ecx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
+    // READ_BARRIER rcx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
+#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
+    READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax  // rax is free.
+    // READ_BARRIER rdx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax
     cmpl %eax, %ecx  // value's type == array's component type - trivial assignability
 #else
     cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability
@@ -1232,9 +1285,14 @@
     PUSH rdx
     SETUP_FP_CALLEE_SAVE_FRAME
 
-                                  // "Uncompress" = do nothing, as already zero-extended on load.
-    movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class.
-    UNPOISON_HEAP_REF esi
+#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
+    // The load of MIRROR_OBJECT_CLASS_OFFSET(%edx) is redundant, eax still holds the value.
+    movl %eax, %esi               // Pass arg2 = value's class.
+    // movq %rax, %rsi
+#else
+                                     // "Uncompress" = do nothing, as already zero-extended on load.
+    movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi  // Pass arg2 = value's class.
+#endif
     movq %rcx, %rdi               // Pass arg1 = array's component type.
 
     call SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
@@ -1735,3 +1793,14 @@
     call PLT_SYMBOL(longjmp)
     UNREACHABLE
 END_FUNCTION art_nested_signal_return
+
+DEFINE_FUNCTION art_quick_read_barrier_slow
+    SETUP_FP_CALLEE_SAVE_FRAME
+    subq LITERAL(8), %rsp           // Alignment padding.
+    CFI_ADJUST_CFA_OFFSET(8)
+    call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj, offset)
+    addq LITERAL(8), %rsp
+    CFI_ADJUST_CFA_OFFSET(-8)
+    RESTORE_FP_CALLEE_SAVE_FRAME
+    ret
+END_FUNCTION art_quick_read_barrier_slow
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 1a0ee0f..fa0694b 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -151,8 +151,9 @@
   void SetObj(mirror::Object* object, mirror::Object* new_value)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
   template<typename RootVisitorType>
-  void VisitRoots(RootVisitorType& visitor) SHARED_REQUIRES(Locks::mutator_lock_);
+  void VisitRoots(RootVisitorType& visitor) NO_THREAD_SAFETY_ANALYSIS;
 
   bool IsVolatile() SHARED_REQUIRES(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccVolatile) != 0;
diff --git a/runtime/art_method.h b/runtime/art_method.h
index a5bd2f0..90352b7 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -447,8 +447,9 @@
                           bool* has_no_move_exception)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
   template<typename RootVisitorType>
-  void VisitRoots(RootVisitorType& visitor) SHARED_REQUIRES(Locks::mutator_lock_);
+  void VisitRoots(RootVisitorType& visitor) NO_THREAD_SAFETY_ANALYSIS;
 
   const DexFile* GetDexFile() SHARED_REQUIRES(Locks::mutator_lock_);
 
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index f4f8eaf..350a0d4 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -109,7 +109,7 @@
             art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
 
 // Offset of field Thread::tlsPtr_.thread_local_pos.
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 150 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 151 * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
             art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
 // Offset of field Thread::tlsPtr_.thread_local_end.
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 859de4b..7a620e3 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -26,7 +26,7 @@
 #include "utils.h"
 
 // Headers for LogMessage::LogLine.
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #include "cutils/log.h"
 #else
 #include <sys/types.h>
@@ -47,7 +47,7 @@
 // Print INTERNAL_FATAL messages directly instead of at destruction time. This only works on the
 // host right now: for the device, a stream buf collating output into lines and calling LogLine or
 // lower-level logging is necessary.
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 static constexpr bool kPrintInternalFatalDirectly = false;
 #else
 static constexpr bool kPrintInternalFatalDirectly = !kIsTargetBuild;
@@ -234,7 +234,7 @@
   return data_->GetBuffer();
 }
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
   ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN,
   ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL
@@ -245,7 +245,7 @@
 
 void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity log_severity,
                          const char* message) {
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   const char* tag = ProgramInvocationShortName();
   int priority = kLogSeverityToAndroidLogPriority[log_severity];
   if (priority == ANDROID_LOG_FATAL) {
@@ -264,7 +264,7 @@
 
 void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverity log_severity,
                                  const char* message) {
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   // Use android_writeLog() to avoid stack-based buffers used by android_printLog().
   const char* tag = ProgramInvocationShortName();
   int priority = kLogSeverityToAndroidLogPriority[log_severity];
diff --git a/runtime/base/out.h b/runtime/base/out.h
new file mode 100644
index 0000000..7199b63
--- /dev/null
+++ b/runtime/base/out.h
@@ -0,0 +1,274 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_BASE_OUT_H_
+#define ART_RUNTIME_BASE_OUT_H_
+
+#include <base/macros.h>
+#include <base/logging.h>
+
+#include <memory>
+// A zero-overhead abstraction marker that means this value is meant to be used as an out
+// parameter for functions. It mimics semantics of a pointer that the function will
+// dereference and output its value into.
+//
+// Inspired by the 'out' language keyword in C#.
+//
+// Declaration example:
+//   int do_work(size_t args, out<int> result);
+//               // returns 0 on success, sets result, otherwise error code
+//
+// Use-site example:
+// // (1) -- out of a local variable or field
+//   int res;
+//   if (do_work(1, outof(res)) {
+//     cout << "success: " << res;
+//   }
+// // (2) -- out of an iterator
+//   std::vector<int> list = {1};
+//   std::vector<int>::iterator it = list.begin();
+//   if (do_work(2, outof_iterator(*it)) {
+//     cout << "success: " << list[0];
+//   }
+// // (3) -- out of a pointer
+//   int* array = &some_other_value;
+//   if (do_work(3, outof_ptr(array))) {
+//     cout << "success: " << *array;
+//   }
+//
+// The type will also automatically decay into a C-style pointer for compatibility
+// with calling legacy code that expect pointers.
+//
+// Declaration example:
+//   void write_data(int* res) { *res = 5; }
+//
+// Use-site example:
+//   int data;
+//   write_data(outof(res));
+//   // data is now '5'
+// (The other outof_* functions can be used analogously when the target is a C-style pointer).
+//
+// ---------------
+//
+// Other typical pointer operations such as addition, subtraction, etc are banned
+// since there is exactly one value being output.
+//
+namespace art {
+
+// Forward declarations. See below for specific functions.
+template <typename T>
+struct out_convertible;  // Implicitly converts to out<T> or T*.
+
+// Helper function that automatically infers 'T'
+//
+// Returns a type that is implicitly convertible to either out<T> or T* depending
+// on the call site.
+//
+// Example:
+//   int do_work(size_t args, out<int> result);
+//               // returns 0 on success, sets result, otherwise error code
+//
+// Usage:
+//   int res;
+//   if (do_work(1, outof(res)) {
+//     cout << "success: " << res;
+//   }
+template <typename T>
+out_convertible<T> outof(T& param) ALWAYS_INLINE;
+
+// Helper function that automatically infers 'T' from a container<T>::iterator.
+// To use when the argument is already inside an iterator.
+//
+// Returns a type that is implicitly convertible to either out<T> or T* depending
+// on the call site.
+//
+// Example:
+//   int do_work(size_t args, out<int> result);
+//               // returns 0 on success, sets result, otherwise error code
+//
+// Usage:
+//   std::vector<int> list = {1};
+//   std::vector<int>::iterator it = list.begin();
+//   if (do_work(2, outof_iterator(*it)) {
+//     cout << "success: " << list[0];
+//   }
+template <typename It>
+auto ALWAYS_INLINE outof_iterator(It iter)
+    -> out_convertible<typename std::remove_reference<decltype(*iter)>::type>;
+
+// Helper function that automatically infers 'T'.
+// To use when the argument is already a pointer.
+//
+// ptr must be not-null, else a DCHECK failure will occur.
+//
+// Returns a type that is implicitly convertible to either out<T> or T* depending
+// on the call site.
+//
+// Example:
+//   int do_work(size_t args, out<int> result);
+//               // returns 0 on success, sets result, otherwise error code
+//
+// Usage:
+//   int* array = &some_other_value;
+//   if (do_work(3, outof_ptr(array))) {
+//     cout << "success: " << *array;
+//   }
+template <typename T>
+out_convertible<T> outof_ptr(T* ptr) ALWAYS_INLINE;
+
+// Zero-overhead wrapper around a non-null non-const pointer meant to be used to output
+// the result of parameters. There are no other extra guarantees.
+//
+// The most common use case is to treat this like a typical pointer argument, for example:
+//
+// void write_out_5(out<int> x) {
+//   *x = 5;
+// }
+//
+// The following operations are supported:
+//   operator* -> use like a pointer (guaranteed to be non-null)
+//   == and != -> compare against other pointers for (in)equality
+//   begin/end -> use in standard C++ algorithms as if it was an iterator
+template <typename T>
+struct out {
+  // Has to be mutable lref. Otherwise how would you write something as output into it?
+  explicit inline out(T& param)
+    : param_(param) {}
+
+  // Model a single-element iterator (or pointer) to the parameter.
+  inline T& operator *() {
+    return param_;
+  }
+
+  //
+  // Comparison against this or other pointers.
+  //
+  template <typename T2>
+  inline bool operator==(const T2* other) const {
+    return std::addressof(param_) == other;
+  }
+
+  template <typename T2>
+  inline bool operator==(const out<T>& other) const {
+    return std::addressof(param_) == std::addressof(other.param_);
+  }
+
+  // An out-parameter is never null.
+  inline bool operator==(std::nullptr_t) const {
+    return false;
+  }
+
+  template <typename T2>
+  inline bool operator!=(const T2* other) const {
+    return std::addressof(param_) != other;
+  }
+
+  template <typename T2>
+  inline bool operator!=(const out<T>& other) const {
+    return std::addressof(param_) != std::addressof(other.param_);
+  }
+
+  // An out-parameter is never null.
+  inline bool operator!=(std::nullptr_t) const {
+    return true;
+  }
+
+  //
+  // Iterator interface implementation. Use with standard algorithms.
+  // TODO: (add items in iterator_traits if this is truly useful).
+  //
+
+  inline T* begin() {
+    return std::addressof(param_);
+  }
+
+  inline const T* begin() const {
+    return std::addressof(param_);
+  }
+
+  inline T* end() {
+    return std::addressof(param_) + 1;
+  }
+
+  inline const T* end() const {
+    return std::addressof(param_) + 1;
+  }
+
+ private:
+  T& param_;
+};
+
+//
+// IMPLEMENTATION DETAILS
+//
+
+//
+// This intermediate type should not be used directly by user code.
+//
+// It enables 'outof(x)' to be passed into functions that expect either
+// an out<T> **or** a regular C-style pointer (T*).
+//
+template <typename T>
+struct out_convertible {
+  explicit inline out_convertible(T& param)
+    : param_(param) {
+  }
+
+  // Implicitly convert into an out<T> for standard usage.
+  inline operator out<T>() {
+    return out<T>(param_);
+  }
+
+  // Implicitly convert into a '*' for legacy usage.
+  inline operator T*() {
+    return std::addressof(param_);
+  }
+ private:
+  T& param_;
+};
+
+// Helper function that automatically infers 'T'
+template <typename T>
+inline out_convertible<T> outof(T& param) {
+  return out_convertible<T>(param);
+}
+
+// Helper function that automatically infers 'T'.
+// To use when the argument is already inside an iterator.
+template <typename It>
+inline auto outof_iterator(It iter)
+    -> out_convertible<typename std::remove_reference<decltype(*iter)>::type> {
+  return outof(*iter);
+}
+
+// Helper function that automatically infers 'T'.
+// To use when the argument is already a pointer.
+template <typename T>
+inline out_convertible<T> outof_ptr(T* ptr) {
+  DCHECK(ptr != nullptr);
+  return outof(*ptr);
+}
+
+// Helper function that automatically infers 'T'.
+// Forwards an out parameter from one function into another.
+template <typename T>
+inline out_convertible<T> outof_forward(out<T>& out_param) {
+  T& param = std::addressof(*out_param);
+  return out_convertible<T>(param);
+}
+
+}  // namespace art
+#endif  // ART_RUNTIME_BASE_OUT_H_
diff --git a/runtime/base/out_test.cc b/runtime/base/out_test.cc
new file mode 100644
index 0000000..4274200
--- /dev/null
+++ b/runtime/base/out_test.cc
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#include "out.h"
+
+#include <algorithm>
+#include <gtest/gtest.h>
+
+namespace art {
+
+struct OutTest : public testing::Test {
+  // Multiplies values less than 10 by two, stores the result and returns 0.
+  // Returns -1 if the original value was not multiplied by two.
+  static int multiply_small_values_by_two(size_t args, out<int> result) {
+    if (args < 10) {
+      *result = args * 2;
+      return 0;
+    } else {
+      return -1;
+    }
+  }
+};
+
+extern "C" int multiply_small_values_by_two_legacy(size_t args, int* result) {
+  if (args < 10) {
+    *result = args * 2;
+    return 0;
+  } else {
+    return -1;
+  }
+}
+
+TEST_F(OutTest, TraditionalCall) {
+  // For calling traditional C++ functions.
+  int res;
+  EXPECT_EQ(multiply_small_values_by_two(1, outof(res)), 0);
+  EXPECT_EQ(2, res);
+}
+
+TEST_F(OutTest, LegacyCall) {
+  // For calling legacy, e.g. C-style functions.
+  int res2;
+  EXPECT_EQ(0, multiply_small_values_by_two_legacy(1, outof(res2)));
+  EXPECT_EQ(2, res2);
+}
+
+TEST_F(OutTest, CallFromIterator) {
+  // For calling a function with a parameter originating as an iterator.
+  std::vector<int> list = {1, 2, 3};  // NOLINT [whitespace/labels] [4]
+  std::vector<int>::iterator it = list.begin();
+
+  EXPECT_EQ(0, multiply_small_values_by_two(2, outof_iterator(it)));
+  EXPECT_EQ(4, list[0]);
+}
+
+TEST_F(OutTest, CallFromPointer) {
+  // For calling a function with a parameter originating as a C-pointer.
+  std::vector<int> list = {1, 2, 3};  // NOLINT [whitespace/labels] [4]
+
+  int* list_ptr = &list[2];  // 3
+
+  EXPECT_EQ(0, multiply_small_values_by_two(2, outof_ptr(list_ptr)));
+  EXPECT_EQ(4, list[2]);
+}
+
+TEST_F(OutTest, OutAsIterator) {
+  // For using the out<T> parameter as an iterator inside of the callee.
+  std::vector<int> list;
+  int x = 100;
+  out<int> out_from_x = outof(x);
+
+  for (const int& val : out_from_x) {
+    list.push_back(val);
+  }
+
+  ASSERT_EQ(1u, list.size());
+  EXPECT_EQ(100, list[0]);
+
+  // A more typical use-case would be to use std algorithms
+  EXPECT_NE(out_from_x.end(),
+            std::find(out_from_x.begin(),
+                      out_from_x.end(),
+                      100));  // Search for '100' in out.
+}
+
+}  // namespace art
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 40b3669..38bc818 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -16,6 +16,7 @@
 
 #include "check_jni.h"
 
+#include <iomanip>
 #include <sys/mman.h>
 #include <zlib.h>
 
@@ -1083,10 +1084,29 @@
     }
 
     const char* errorKind = nullptr;
-    uint8_t utf8 = CheckUtfBytes(bytes, &errorKind);
+    const uint8_t* utf8 = CheckUtfBytes(bytes, &errorKind);
     if (errorKind != nullptr) {
+      // This is an expensive loop that will resize often, but this isn't supposed to hit in
+      // practice anyways.
+      std::ostringstream oss;
+      oss << std::hex;
+      const uint8_t* tmp = reinterpret_cast<const uint8_t*>(bytes);
+      while (*tmp != 0) {
+        if (tmp == utf8) {
+          oss << "<";
+        }
+        oss << "0x" << std::setfill('0') << std::setw(2) << static_cast<uint32_t>(*tmp);
+        if (tmp == utf8) {
+          oss << '>';
+        }
+        tmp++;
+        if (*tmp != 0) {
+          oss << ' ';
+        }
+      }
+
       AbortF("input is not valid Modified UTF-8: illegal %s byte %#x\n"
-          "    string: '%s'", errorKind, utf8, bytes);
+          "    string: '%s'\n    input: '%s'", errorKind, *utf8, bytes, oss.str().c_str());
       return false;
     }
     return true;
@@ -1094,11 +1114,11 @@
 
   // Checks whether |bytes| is valid modified UTF-8. We also accept 4 byte UTF
   // sequences in place of encoded surrogate pairs.
-  static uint8_t CheckUtfBytes(const char* bytes, const char** errorKind) {
+  static const uint8_t* CheckUtfBytes(const char* bytes, const char** errorKind) {
     while (*bytes != '\0') {
-      uint8_t utf8 = *(bytes++);
+      const uint8_t* utf8 = reinterpret_cast<const uint8_t*>(bytes++);
       // Switch on the high four bits.
-      switch (utf8 >> 4) {
+      switch (*utf8 >> 4) {
       case 0x00:
       case 0x01:
       case 0x02:
@@ -1118,11 +1138,11 @@
         return utf8;
       case 0x0f:
         // Bit pattern 1111, which might be the start of a 4 byte sequence.
-        if ((utf8 & 0x08) == 0) {
+        if ((*utf8 & 0x08) == 0) {
           // Bit pattern 1111 0xxx, which is the start of a 4 byte sequence.
           // We consume one continuation byte here, and fall through to consume two more.
-          utf8 = *(bytes++);
-          if ((utf8 & 0xc0) != 0x80) {
+          utf8 = reinterpret_cast<const uint8_t*>(bytes++);
+          if ((*utf8 & 0xc0) != 0x80) {
             *errorKind = "continuation";
             return utf8;
           }
@@ -1135,8 +1155,8 @@
         FALLTHROUGH_INTENDED;
       case 0x0e:
         // Bit pattern 1110, so there are two additional bytes.
-        utf8 = *(bytes++);
-        if ((utf8 & 0xc0) != 0x80) {
+        utf8 = reinterpret_cast<const uint8_t*>(bytes++);
+        if ((*utf8 & 0xc0) != 0x80) {
           *errorKind = "continuation";
           return utf8;
         }
@@ -1146,8 +1166,8 @@
       case 0x0c:
       case 0x0d:
         // Bit pattern 110x, so there is one additional byte.
-        utf8 = *(bytes++);
-        if ((utf8 & 0xc0) != 0x80) {
+        utf8 = reinterpret_cast<const uint8_t*>(bytes++);
+        if ((*utf8 & 0xc0) != 0x80) {
           *errorKind = "continuation";
           return utf8;
         }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index fc59d50..56fae81 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1236,11 +1236,8 @@
 
 bool ClassLinker::ClassInClassTable(mirror::Class* klass) {
   ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  auto it = class_table_.Find(GcRoot<mirror::Class>(klass));
-  if (it == class_table_.end()) {
-    return false;
-  }
-  return it->Read() == klass;
+  ClassTable* const class_table = ClassTableForClassLoader(klass->GetClassLoader());
+  return class_table != nullptr && class_table->Contains(klass);
 }
 
 void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
@@ -1252,8 +1249,7 @@
     // There is 3 GC cases to handle:
     // Non moving concurrent:
     // This case is easy to handle since the reference members of ArtMethod and ArtFields are held
-    // live by the class and class roots. In this case we probably don't even need to call
-    // VisitNativeRoots.
+    // live by the class and class roots.
     //
     // Moving non-concurrent:
     // This case needs to call visit VisitNativeRoots in case the classes or dex cache arrays move.
@@ -1264,35 +1260,30 @@
     // Moving concurrent:
     // Need to make sure to not copy ArtMethods without doing read barriers since the roots are
     // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
-    for (GcRoot<mirror::Class>& root : class_table_) {
-      buffered_visitor.VisitRoot(root);
-      if ((flags & kVisitRootFlagNonMoving) == 0) {
-        // Don't bother visiting ArtField and ArtMethod if kVisitRootFlagNonMoving is set since
-        // these roots are all reachable from the class or dex cache.
-        root.Read()->VisitNativeRoots(buffered_visitor, image_pointer_size_);
+    std::vector<std::pair<GcRoot<mirror::ClassLoader>, ClassTable*>> reinsert;
+    for (auto it = classes_.begin(); it != classes_.end(); ) {
+      it->second->VisitRoots(visitor, flags);
+      const GcRoot<mirror::ClassLoader>& root = it->first;
+      mirror::ClassLoader* old_ref = root.Read<kWithoutReadBarrier>();
+      root.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+      mirror::ClassLoader* new_ref = root.Read<kWithoutReadBarrier>();
+      if (new_ref != old_ref) {
+        reinsert.push_back(*it);
+        it = classes_.erase(it);
+      } else {
+        ++it;
       }
     }
-    // PreZygote classes can't move so we won't need to update fields' declaring classes.
-    for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
-      buffered_visitor.VisitRoot(root);
-      if ((flags & kVisitRootFlagNonMoving) == 0) {
-        root.Read()->VisitNativeRoots(buffered_visitor, image_pointer_size_);
-      }
+    for (auto& pair : reinsert) {
+      classes_.Put(pair.first, pair.second);
     }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_class_roots_) {
       mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
-      old_ref->VisitNativeRoots(buffered_visitor, image_pointer_size_);
       root.VisitRoot(visitor, RootInfo(kRootStickyClass));
       mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
-      if (UNLIKELY(new_ref != old_ref)) {
-        // Uh ohes, GC moved a root in the log. Need to search the class_table and update the
-        // corresponding object. This is slow, but luckily for us, this may only happen with a
-        // concurrent moving GC.
-        auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref));
-        DCHECK(it != class_table_.end());
-        *it = GcRoot<mirror::Class>(new_ref);
-      }
+      // Concurrent moving GC marked new roots through the to-space invariant.
+      CHECK_EQ(new_ref, old_ref);
     }
   }
   buffered_visitor.Flush();  // Flush before clearing new_class_roots_.
@@ -1341,91 +1332,103 @@
   }
 }
 
-void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
+void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) {
+  for (auto& pair : classes_) {
+    ClassTable* const class_table = pair.second;
+    if (!class_table->Visit(visitor)) {
+      return;
+    }
+  }
+}
+
+void ClassLinker::VisitClasses(ClassVisitor* visitor) {
   if (dex_cache_image_class_lookup_required_) {
     MoveImageClassesToClassTable();
   }
-  // TODO: why isn't this a ReaderMutexLock?
-  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  for (GcRoot<mirror::Class>& root : class_table_) {
-    if (!visitor(root.Read(), arg)) {
-      return;
-    }
-  }
-  for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
-    if (!visitor(root.Read(), arg)) {
-      return;
-    }
+  Thread* const self = Thread::Current();
+  ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
+  // Not safe to have thread suspension when we are holding a lock.
+  if (self != nullptr) {
+    ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
+    VisitClassesInternal(visitor);
+  } else {
+    VisitClassesInternal(visitor);
   }
 }
 
-static bool GetClassesVisitorSet(mirror::Class* c, void* arg) {
-  std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg);
-  classes->insert(c);
-  return true;
-}
-
-struct GetClassesVisitorArrayArg {
-  Handle<mirror::ObjectArray<mirror::Class>>* classes;
-  int32_t index;
-  bool success;
+class GetClassesInToVector : public ClassVisitor {
+ public:
+  bool Visit(mirror::Class* klass) OVERRIDE {
+    classes_.push_back(klass);
+    return true;
+  }
+  std::vector<mirror::Class*> classes_;
 };
 
-static bool GetClassesVisitorArray(mirror::Class* c, void* varg)
-    SHARED_REQUIRES(Locks::mutator_lock_) {
-  GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg);
-  if (arg->index < (*arg->classes)->GetLength()) {
-    (*arg->classes)->Set(arg->index, c);
-    arg->index++;
-    return true;
-  } else {
-    arg->success = false;
+class GetClassInToObjectArray : public ClassVisitor {
+ public:
+  explicit GetClassInToObjectArray(mirror::ObjectArray<mirror::Class>* arr)
+      : arr_(arr), index_(0) {}
+
+  bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    ++index_;
+    if (index_ <= arr_->GetLength()) {
+      arr_->Set(index_ - 1, klass);
+      return true;
+    }
     return false;
   }
-}
 
-void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) {
+  bool Succeeded() const SHARED_REQUIRES(Locks::mutator_lock_) {
+    return index_ <= arr_->GetLength();
+  }
+
+ private:
+  mirror::ObjectArray<mirror::Class>* const arr_;
+  int32_t index_;
+};
+
+void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) {
   // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
   // is avoiding duplicates.
   if (!kMovingClasses) {
-    std::set<mirror::Class*> classes;
-    VisitClasses(GetClassesVisitorSet, &classes);
-    for (mirror::Class* klass : classes) {
-      if (!visitor(klass, arg)) {
+    GetClassesInToVector accumulator;
+    VisitClasses(&accumulator);
+    for (mirror::Class* klass : accumulator.classes_) {
+      if (!visitor->Visit(klass)) {
         return;
       }
     }
   } else {
-    Thread* self = Thread::Current();
+    Thread* const self = Thread::Current();
     StackHandleScope<1> hs(self);
-    MutableHandle<mirror::ObjectArray<mirror::Class>> classes =
-        hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
-    GetClassesVisitorArrayArg local_arg;
-    local_arg.classes = &classes;
-    local_arg.success = false;
+    auto classes = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
     // We size the array assuming classes won't be added to the class table during the visit.
     // If this assumption fails we iterate again.
-    while (!local_arg.success) {
+    while (true) {
       size_t class_table_size;
       {
         ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
-        class_table_size = class_table_.Size() + pre_zygote_class_table_.Size();
+        // Add 100 in case new classes get loaded when we are filling in the object array.
+        class_table_size = NumZygoteClasses() + NumNonZygoteClasses() + 100;
       }
       mirror::Class* class_type = mirror::Class::GetJavaLangClass();
       mirror::Class* array_of_class = FindArrayClass(self, &class_type);
       classes.Assign(
           mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
       CHECK(classes.Get() != nullptr);  // OOME.
-      local_arg.index = 0;
-      local_arg.success = true;
-      VisitClasses(GetClassesVisitorArray, &local_arg);
+      GetClassInToObjectArray accumulator(classes.Get());
+      VisitClasses(&accumulator);
+      if (accumulator.Succeeded()) {
+        break;
+      }
     }
     for (int32_t i = 0; i < classes->GetLength(); ++i) {
       // If the class table shrank during creation of the clases array we expect null elements. If
       // the class table grew then the loop repeats. If classes are created after the loop has
       // finished then we don't visit.
       mirror::Class* klass = classes->Get(i);
-      if (klass != nullptr && !visitor(klass, arg)) {
+      if (klass != nullptr && !visitor->Visit(klass)) {
         return;
       }
     }
@@ -1453,6 +1456,7 @@
   mirror::LongArray::ResetArrayClass();
   mirror::ShortArray::ResetArrayClass();
   STLDeleteElements(&oat_files_);
+  STLDeleteValues(&classes_);
 }
 
 mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
@@ -2387,6 +2391,8 @@
     }
     DCHECK(!it.HasNext());
   }
+  // Ensure that the card is marked so that remembered sets pick up native roots.
+  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get());
   self->AllowThreadSuspension();
 }
 
@@ -2468,8 +2474,8 @@
 
 bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) {
   dex_lock_.AssertSharedHeld(Thread::Current());
-  for (size_t i = 0; i != dex_caches_.size(); ++i) {
-    mirror::DexCache* dex_cache = GetDexCache(i);
+  for (GcRoot<mirror::DexCache>& root : dex_caches_) {
+    mirror::DexCache* dex_cache = root.Read();
     if (dex_cache->GetDexFile() == &dex_file) {
       return true;
     }
@@ -2767,8 +2773,7 @@
   return nullptr;
 }
 
-mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass,
-                                        size_t hash) {
+mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass, size_t hash) {
   if (VLOG_IS_ON(class_linker)) {
     mirror::DexCache* dex_cache = klass->GetDexCache();
     std::string source;
@@ -2779,11 +2784,13 @@
     LOG(INFO) << "Loaded class " << descriptor << source;
   }
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  mirror::Class* existing = LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
+  mirror::ClassLoader* const class_loader = klass->GetClassLoader();
+  ClassTable* const class_table = InsertClassTableForClassLoader(class_loader);
+  mirror::Class* existing = class_table->Lookup(descriptor, hash);
   if (existing != nullptr) {
     return existing;
   }
-  if (kIsDebugBuild && !klass->IsTemp() && klass->GetClassLoader() == nullptr &&
+  if (kIsDebugBuild && !klass->IsTemp() && class_loader == nullptr &&
       dex_cache_image_class_lookup_required_) {
     // Check a class loaded with the system class loader matches one in the image if the class
     // is in the image.
@@ -2793,7 +2800,7 @@
     }
   }
   VerifyObject(klass);
-  class_table_.InsertWithHash(GcRoot<mirror::Class>(klass), hash);
+  class_table->InsertWithHash(klass, hash);
   if (log_new_class_table_roots_) {
     new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
   }
@@ -2802,105 +2809,48 @@
 
 void ClassLinker::UpdateClassVirtualMethods(mirror::Class* klass, ArtMethod* new_methods,
                                             size_t new_num_methods) {
-  // classlinker_classes_lock_ is used to guard against races between root marking and changing the
-  // direct and virtual method pointers.
-  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  // TODO: Fix the race condition here. b/22832610
   klass->SetNumVirtualMethods(new_num_methods);
   klass->SetVirtualMethodsPtr(new_methods);
-  if (log_new_class_table_roots_) {
-    new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
-  }
-}
-
-mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass,
-                                        size_t hash) {
-  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  auto existing_it = class_table_.FindWithHash(std::make_pair(descriptor, klass->GetClassLoader()),
-                                               hash);
-  CHECK(existing_it != class_table_.end());
-  mirror::Class* existing = existing_it->Read();
-  CHECK_NE(existing, klass) << descriptor;
-  CHECK(!existing->IsResolved()) << descriptor;
-  CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor;
-
-  CHECK(!klass->IsTemp()) << descriptor;
-  if (kIsDebugBuild && klass->GetClassLoader() == nullptr &&
-      dex_cache_image_class_lookup_required_) {
-    // Check a class loaded with the system class loader matches one in the image if the class
-    // is in the image.
-    existing = LookupClassFromImage(descriptor);
-    if (existing != nullptr) {
-      CHECK_EQ(klass, existing) << descriptor;
-    }
-  }
-  VerifyObject(klass);
-
-  // Update the element in the hash set.
-  *existing_it = GcRoot<mirror::Class>(klass);
-  if (log_new_class_table_roots_) {
-    new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
-  }
-
-  return existing;
+  // Need to mark the card so that the remembered sets and mod union tables get update.
+  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass);
 }
 
 bool ClassLinker::RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader) {
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  auto pair = std::make_pair(descriptor, class_loader);
-  auto it = class_table_.Find(pair);
-  if (it != class_table_.end()) {
-    class_table_.Erase(it);
-    return true;
-  }
-  it = pre_zygote_class_table_.Find(pair);
-  if (it != pre_zygote_class_table_.end()) {
-    pre_zygote_class_table_.Erase(it);
-    return true;
-  }
-  return false;
+  ClassTable* const class_table = ClassTableForClassLoader(class_loader);
+  return class_table != nullptr && class_table->Remove(descriptor);
 }
 
 mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash,
                                         mirror::ClassLoader* class_loader) {
   {
     ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
-    mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
-    if (result != nullptr) {
-      return result;
+    ClassTable* const class_table = ClassTableForClassLoader(class_loader);
+    if (class_table != nullptr) {
+      mirror::Class* result = class_table->Lookup(descriptor, hash);
+      if (result != nullptr) {
+        return result;
+      }
     }
   }
   if (class_loader != nullptr || !dex_cache_image_class_lookup_required_) {
     return nullptr;
+  }
+  // Lookup failed but need to search dex_caches_.
+  mirror::Class* result = LookupClassFromImage(descriptor);
+  if (result != nullptr) {
+    result = InsertClass(descriptor, result, hash);
   } else {
-    // Lookup failed but need to search dex_caches_.
-    mirror::Class* result = LookupClassFromImage(descriptor);
-    if (result != nullptr) {
-      InsertClass(descriptor, result, hash);
-    } else {
-      // Searching the image dex files/caches failed, we don't want to get into this situation
-      // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image
-      // classes into the class table.
-      constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
-      if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
-        MoveImageClassesToClassTable();
-      }
-    }
-    return result;
-  }
-}
-
-mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor,
-                                                       mirror::ClassLoader* class_loader,
-                                                       size_t hash) {
-  auto descriptor_pair = std::make_pair(descriptor, class_loader);
-  auto it = pre_zygote_class_table_.FindWithHash(descriptor_pair, hash);
-  if (it == pre_zygote_class_table_.end()) {
-    it = class_table_.FindWithHash(descriptor_pair, hash);
-    if (it == class_table_.end()) {
-      return nullptr;
+    // Searching the image dex files/caches failed, we don't want to get into this situation
+    // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image
+    // classes into the class table.
+    constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
+    if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
+      MoveImageClassesToClassTable();
     }
   }
-  return it->Read();
+  return result;
 }
 
 static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches()
@@ -2920,6 +2870,7 @@
   ScopedAssertNoThreadSuspension ants(self, "Moving image classes to class table");
   mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
   std::string temp;
+  ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
   for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
     mirror::DexCache* dex_cache = dex_caches->Get(i);
     mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes();
@@ -2929,12 +2880,12 @@
         DCHECK(klass->GetClassLoader() == nullptr);
         const char* descriptor = klass->GetDescriptor(&temp);
         size_t hash = ComputeModifiedUtf8Hash(descriptor);
-        mirror::Class* existing = LookupClassFromTableLocked(descriptor, nullptr, hash);
+        mirror::Class* existing = class_table->Lookup(descriptor, hash);
         if (existing != nullptr) {
           CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != "
               << PrettyClassAndClassLoader(klass);
         } else {
-          class_table_.Insert(GcRoot<mirror::Class>(klass));
+          class_table->Insert(klass);
           if (log_new_class_table_roots_) {
             new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
           }
@@ -2947,9 +2898,9 @@
 
 void ClassLinker::MoveClassTableToPreZygote() {
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  DCHECK(pre_zygote_class_table_.Empty());
-  pre_zygote_class_table_ = std::move(class_table_);
-  class_table_.Clear();
+  for (auto& class_table : classes_) {
+    class_table.second->FreezeSnapshot();
+  }
 }
 
 mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
@@ -2981,31 +2932,13 @@
     MoveImageClassesToClassTable();
   }
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  while (true) {
-    auto it = class_table_.Find(descriptor);
-    if (it == class_table_.end()) {
-      break;
+  for (auto& pair : classes_) {
+    // There can only be one class with the same descriptor per class loader.
+    ClassTable* const class_table  = pair.second;
+    mirror::Class* klass = class_table->Lookup(descriptor, ComputeModifiedUtf8Hash(descriptor));
+    if (klass != nullptr) {
+      result.push_back(klass);
     }
-    result.push_back(it->Read());
-    class_table_.Erase(it);
-  }
-  for (mirror::Class* k : result) {
-    class_table_.Insert(GcRoot<mirror::Class>(k));
-  }
-  size_t pre_zygote_start = result.size();
-  // Now handle the pre zygote table.
-  // Note: This dirties the pre-zygote table but shouldn't be an issue since LookupClasses is only
-  // called from the debugger.
-  while (true) {
-    auto it = pre_zygote_class_table_.Find(descriptor);
-    if (it == pre_zygote_class_table_.end()) {
-      break;
-    }
-    result.push_back(it->Read());
-    pre_zygote_class_table_.Erase(it);
-  }
-  for (size_t i = pre_zygote_start; i < result.size(); ++i) {
-    pre_zygote_class_table_.Insert(GcRoot<mirror::Class>(result[i]));
   }
 }
 
@@ -3313,7 +3246,7 @@
   klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache());
   mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
   std::string descriptor(GetDescriptorForProxy(klass.Get()));
-  size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
+  const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
 
   // Insert the class before loading the fields as the field roots
   // (ArtField::declaring_class_) are only visited from the class
@@ -4026,7 +3959,7 @@
 void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class,
                                                mirror::Class* new_class) {
   ArtField* fields = new_class->GetIFields();
-  DCHECK_EQ(temp_class->NumInstanceFields(), new_class->NumInstanceFields());
+  DCHECK_EQ(temp_class->NumInstanceFields(), 0u);
   for (size_t i = 0, count = new_class->NumInstanceFields(); i < count; i++) {
     if (fields[i].GetDeclaringClass() == temp_class) {
       fields[i].SetDeclaringClass(new_class);
@@ -4034,26 +3967,49 @@
   }
 
   fields = new_class->GetSFields();
-  DCHECK_EQ(temp_class->NumStaticFields(), new_class->NumStaticFields());
+  DCHECK_EQ(temp_class->NumStaticFields(), 0u);
   for (size_t i = 0, count = new_class->NumStaticFields(); i < count; i++) {
     if (fields[i].GetDeclaringClass() == temp_class) {
       fields[i].SetDeclaringClass(new_class);
     }
   }
 
-  DCHECK_EQ(temp_class->NumDirectMethods(), new_class->NumDirectMethods());
+  DCHECK_EQ(temp_class->NumDirectMethods(), 0u);
   for (auto& method : new_class->GetDirectMethods(image_pointer_size_)) {
     if (method.GetDeclaringClass() == temp_class) {
       method.SetDeclaringClass(new_class);
     }
   }
 
-  DCHECK_EQ(temp_class->NumVirtualMethods(), new_class->NumVirtualMethods());
+  DCHECK_EQ(temp_class->NumVirtualMethods(), 0u);
   for (auto& method : new_class->GetVirtualMethods(image_pointer_size_)) {
     if (method.GetDeclaringClass() == temp_class) {
       method.SetDeclaringClass(new_class);
     }
   }
+
+  // Make sure the remembered set and mod-union tables know that we updated some of the native
+  // roots.
+  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(new_class);
+}
+
+ClassTable* ClassLinker::InsertClassTableForClassLoader(mirror::ClassLoader* class_loader) {
+  auto it = classes_.find(GcRoot<mirror::ClassLoader>(class_loader));
+  if (it != classes_.end()) {
+    return it->second;
+  }
+  // Class table for loader not found, add it to the table.
+  auto* const class_table = new ClassTable;
+  classes_.Put(GcRoot<mirror::ClassLoader>(class_loader), class_table);
+  return class_table;
+}
+
+ClassTable* ClassLinker::ClassTableForClassLoader(mirror::ClassLoader* class_loader) {
+  auto it = classes_.find(GcRoot<mirror::ClassLoader>(class_loader));
+  if (it != classes_.end()) {
+    return it->second;
+  }
+  return nullptr;
 }
 
 bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
@@ -4097,6 +4053,14 @@
     // Retire the temporary class and create the correctly sized resolved class.
     StackHandleScope<1> hs(self);
     auto h_new_class = hs.NewHandle(klass->CopyOf(self, class_size, imt, image_pointer_size_));
+    // Set array lengths to 0 since we don't want the GC to visit two different classes with the
+    // same ArtFields with the same If this occurs, it causes bugs in remembered sets since the GC
+    // may not see any references to the from space and clean the card. Though there was references
+    // to the from space that got marked by the first class.
+    klass->SetNumDirectMethods(0);
+    klass->SetNumVirtualMethods(0);
+    klass->SetNumStaticFields(0);
+    klass->SetNumInstanceFields(0);
     if (UNLIKELY(h_new_class.Get() == nullptr)) {
       self->AssertPendingOOMException();
       mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
@@ -4106,9 +4070,26 @@
     CHECK_EQ(h_new_class->GetClassSize(), class_size);
     ObjectLock<mirror::Class> lock(self, h_new_class);
     FixupTemporaryDeclaringClass(klass.Get(), h_new_class.Get());
-    mirror::Class* existing = UpdateClass(descriptor, h_new_class.Get(),
-                                          ComputeModifiedUtf8Hash(descriptor));
-    CHECK(existing == nullptr || existing == klass.Get());
+
+    {
+      WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+      mirror::ClassLoader* const class_loader = h_new_class.Get()->GetClassLoader();
+      ClassTable* const table = InsertClassTableForClassLoader(class_loader);
+      mirror::Class* existing = table->UpdateClass(descriptor, h_new_class.Get(),
+                                                   ComputeModifiedUtf8Hash(descriptor));
+      CHECK_EQ(existing, klass.Get());
+      if (kIsDebugBuild && class_loader == nullptr && dex_cache_image_class_lookup_required_) {
+        // Check a class loaded with the system class loader matches one in the image if the class
+        // is in the image.
+        mirror::Class* const image_class = LookupClassFromImage(descriptor);
+        if (image_class != nullptr) {
+          CHECK_EQ(klass.Get(), existing) << descriptor;
+        }
+      }
+      if (log_new_class_table_roots_) {
+        new_class_roots_.push_back(GcRoot<mirror::Class>(h_new_class.Get()));
+      }
+    }
 
     // This will notify waiters on temp class that saw the not yet resolved class in the
     // class_table_ during EnsureResolved.
@@ -5599,23 +5580,22 @@
   return dex_file.GetMethodShorty(method_id, length);
 }
 
-void ClassLinker::DumpAllClasses(int flags) {
-  if (dex_cache_image_class_lookup_required_) {
-    MoveImageClassesToClassTable();
-  }
-  // TODO: at the time this was written, it wasn't safe to call PrettyField with the ClassLinker
-  // lock held, because it might need to resolve a field's type, which would try to take the lock.
-  std::vector<mirror::Class*> all_classes;
-  {
-    ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-    for (GcRoot<mirror::Class>& it : class_table_) {
-      all_classes.push_back(it.Read());
-    }
+class DumpClassVisitor : public ClassVisitor {
+ public:
+  explicit DumpClassVisitor(int flags) : flags_(flags) {}
+
+  bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    klass->DumpClass(LOG(ERROR), flags_);
+    return true;
   }
 
-  for (size_t i = 0; i < all_classes.size(); ++i) {
-    all_classes[i]->DumpClass(std::cerr, flags);
-  }
+ private:
+  const int flags_;
+};
+
+void ClassLinker::DumpAllClasses(int flags) {
+  DumpClassVisitor visitor(flags);
+  VisitClasses(&visitor);
 }
 
 static OatFile::OatMethod CreateOatMethod(const void* code) {
@@ -5668,8 +5648,24 @@
     MoveImageClassesToClassTable();
   }
   ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
-  os << "Zygote loaded classes=" << pre_zygote_class_table_.Size() << " post zygote classes="
-     << class_table_.Size() << "\n";
+  os << "Zygote loaded classes=" << NumZygoteClasses() << " post zygote classes="
+     << NumNonZygoteClasses() << "\n";
+}
+
+size_t ClassLinker::NumZygoteClasses() const {
+  size_t sum = 0;
+  for (auto& pair : classes_) {
+    sum += pair.second->NumZygoteClasses();
+  }
+  return sum;
+}
+
+size_t ClassLinker::NumNonZygoteClasses() const {
+  size_t sum = 0;
+  for (auto& pair : classes_) {
+    sum += pair.second->NumNonZygoteClasses();
+  }
+  return sum;
 }
 
 size_t ClassLinker::NumLoadedClasses() {
@@ -5678,7 +5674,7 @@
   }
   ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
   // Only return non zygote classes since these are the ones which apps which care about.
-  return class_table_.Size();
+  return NumNonZygoteClasses();
 }
 
 pid_t ClassLinker::GetClassesLockOwner() {
@@ -5749,43 +5745,6 @@
   return descriptor;
 }
 
-std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root)
-    const {
-  std::string temp;
-  return ComputeModifiedUtf8Hash(root.Read()->GetDescriptor(&temp));
-}
-
-bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
-                                                        const GcRoot<mirror::Class>& b) const {
-  if (a.Read()->GetClassLoader() != b.Read()->GetClassLoader()) {
-    return false;
-  }
-  std::string temp;
-  return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp));
-}
-
-std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(
-    const std::pair<const char*, mirror::ClassLoader*>& element) const {
-  return ComputeModifiedUtf8Hash(element.first);
-}
-
-bool ClassLinker::ClassDescriptorHashEquals::operator()(
-    const GcRoot<mirror::Class>& a, const std::pair<const char*, mirror::ClassLoader*>& b) const {
-  if (a.Read()->GetClassLoader() != b.second) {
-    return false;
-  }
-  return a.Read()->DescriptorEquals(b.first);
-}
-
-bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
-                                                        const char* descriptor) const {
-  return a.Read()->DescriptorEquals(descriptor);
-}
-
-std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descriptor) const {
-  return ComputeModifiedUtf8Hash(descriptor);
-}
-
 bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) {
   if (Runtime::Current()->UseJit()) {
     // JIT can have direct code pointers from any method to any other method.
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 05a809e..c53ff61 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -25,6 +25,7 @@
 #include "base/hash_set.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "class_table.h"
 #include "dex_file.h"
 #include "gc_root.h"
 #include "jni.h"
@@ -56,8 +57,6 @@
 class ScopedObjectAccessAlreadyRunnable;
 template<size_t kNumReferences> class PACKED(4) StackHandleScope;
 
-typedef bool (ClassVisitor)(mirror::Class* c, void* arg);
-
 enum VisitRootFlags : uint8_t;
 
 class ClassLinker {
@@ -289,14 +288,14 @@
   const OatFile* GetPrimaryOatFile()
       REQUIRES(!dex_lock_);
 
-  void VisitClasses(ClassVisitor* visitor, void* arg)
+  void VisitClasses(ClassVisitor* visitor)
       REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Less efficient variant of VisitClasses that copies the class_table_ into secondary storage
   // so that it can visit individual classes without holding the doesn't hold the
   // Locks::classlinker_classes_lock_. As the Locks::classlinker_classes_lock_ isn't held this code
   // can race with insertion and deletion of classes while the visitor is being called.
-  void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg)
+  void VisitClassesWithoutClassesLock(ClassVisitor* visitor)
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
 
   void VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags)
@@ -476,9 +475,28 @@
   void DropFindArrayClassCache() SHARED_REQUIRES(Locks::mutator_lock_);
 
  private:
+  class CompareClassLoaderGcRoot {
+   public:
+    bool operator()(const GcRoot<mirror::ClassLoader>& a, const GcRoot<mirror::ClassLoader>& b)
+        const SHARED_REQUIRES(Locks::mutator_lock_) {
+      return a.Read() < b.Read();
+    }
+  };
+
+  typedef SafeMap<GcRoot<mirror::ClassLoader>, ClassTable*, CompareClassLoaderGcRoot>
+      ClassLoaderClassTable;
+
+  void VisitClassesInternal(ClassVisitor* visitor)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Returns the number of zygote and image classes.
+  size_t NumZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+
+  // Returns the number of non zygote nor image classes.
+  size_t NumNonZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+
   OatFile& GetImageOatFile(gc::space::ImageSpace* space)
-      REQUIRES(!dex_lock_)
-      SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
 
   void FinishInit(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
@@ -543,8 +561,7 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
-      REQUIRES(dex_lock_)
-      SHARED_REQUIRES(Locks::mutator_lock_);
+      REQUIRES(dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
   bool IsDexFileRegisteredLocked(const DexFile& dex_file)
       SHARED_REQUIRES(dex_lock_, Locks::mutator_lock_);
 
@@ -568,7 +585,7 @@
   bool LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
                  Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                  MutableHandle<mirror::Class>* h_new_class_out)
-      SHARED_REQUIRES(Locks::mutator_lock_);
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_);
 
   bool LinkSuperClass(Handle<mirror::Class> klass)
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -576,7 +593,8 @@
   bool LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file)
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
 
-  bool LinkMethods(Thread* self, Handle<mirror::Class> klass,
+  bool LinkMethods(Thread* self,
+                   Handle<mirror::Class> klass,
                    Handle<mirror::ObjectArray<mirror::Class>> interfaces,
                    ArtMethod** out_imt)
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -632,18 +650,16 @@
   void EnsurePreverifiedMethods(Handle<mirror::Class> c)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  mirror::Class* LookupClassFromTableLocked(const char* descriptor,
-                                            mirror::ClassLoader* class_loader,
-                                            size_t hash)
-      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
-
-  mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash)
-      REQUIRES(!Locks::classlinker_classes_lock_)
-      SHARED_REQUIRES(Locks::mutator_lock_);
-
   mirror::Class* LookupClassFromImage(const char* descriptor)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Returns null if not found.
+  ClassTable* ClassTableForClassLoader(mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_);
+  // Insert a new class table if not found.
+  ClassTable* InsertClassTableForClassLoader(mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::classlinker_classes_lock_);
+
   // EnsureResolved is called to make sure that a class in the class_table_ has been resolved
   // before returning it to the caller. Its the responsibility of the thread that placed the class
   // in the table to make it resolved. The thread doing resolution must notify on the class' lock
@@ -690,43 +706,11 @@
   std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_);
   std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
 
-  class ClassDescriptorHashEquals {
-   public:
-    // Same class loader and descriptor.
-    std::size_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS;
-    bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const
-        NO_THREAD_SAFETY_ANALYSIS;
-    // Same class loader and descriptor.
-    std::size_t operator()(const std::pair<const char*, mirror::ClassLoader*>& element) const
-        NO_THREAD_SAFETY_ANALYSIS;
-    bool operator()(const GcRoot<mirror::Class>& a,
-                    const std::pair<const char*, mirror::ClassLoader*>& b) const
-        NO_THREAD_SAFETY_ANALYSIS;
-    // Same descriptor.
-    bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor) const
-        NO_THREAD_SAFETY_ANALYSIS;
-    std::size_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS;
-  };
-  class GcRootEmptyFn {
-   public:
-    void MakeEmpty(GcRoot<mirror::Class>& item) const {
-      item = GcRoot<mirror::Class>();
-    }
-    bool IsEmpty(const GcRoot<mirror::Class>& item) const {
-      return item.IsNull();
-    }
-  };
+  // This contains strong roots. To enable concurrent root scanning of the class table.
+  ClassLoaderClassTable classes_ GUARDED_BY(Locks::classlinker_classes_lock_);
 
-  // hash set which hashes class descriptor, and compares descriptors nad class loaders. Results
-  // should be compared for a matching Class descriptor and class loader.
-  typedef HashSet<GcRoot<mirror::Class>, GcRootEmptyFn, ClassDescriptorHashEquals,
-      ClassDescriptorHashEquals, TrackingAllocator<GcRoot<mirror::Class>, kAllocatorTagClassTable>>
-      Table;
-  // This contains strong roots. To enable concurrent root scanning of
-  // the class table, be careful to use a read barrier when accessing this.
-  Table class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
-  Table pre_zygote_class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
-  std::vector<GcRoot<mirror::Class>> new_class_roots_;
+  // New class roots, only used by CMS since the GC needs to mark these in the pause.
+  std::vector<GcRoot<mirror::Class>> new_class_roots_ GUARDED_BY(Locks::classlinker_classes_lock_);
 
   // Do we need to search dex caches to find image classes?
   bool dex_cache_image_class_lookup_required_;
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
new file mode 100644
index 0000000..c245d4e
--- /dev/null
+++ b/runtime/class_table.cc
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#include "class_table.h"
+
+#include "mirror/class-inl.h"
+
+namespace art {
+
+ClassTable::ClassTable() {
+  classes_.push_back(ClassSet());
+}
+
+void ClassTable::FreezeSnapshot() {
+  classes_.push_back(ClassSet());
+}
+
+bool ClassTable::Contains(mirror::Class* klass) {
+  for (ClassSet& class_set : classes_) {
+    auto it = class_set.Find(GcRoot<mirror::Class>(klass));
+    if (it != class_set.end()) {
+      return it->Read() == klass;
+    }
+  }
+  return false;
+}
+
+mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) {
+  // Should only be updating latest table.
+  auto existing_it = classes_.back().FindWithHash(descriptor, hash);
+  if (kIsDebugBuild && existing_it == classes_.back().end()) {
+    for (const ClassSet& class_set : classes_) {
+      if (class_set.FindWithHash(descriptor, hash) != class_set.end()) {
+        LOG(FATAL) << "Updating class found in frozen table " << descriptor;
+      }
+    }
+    LOG(FATAL) << "Updating class not found " << descriptor;
+  }
+  mirror::Class* const existing = existing_it->Read();
+  CHECK_NE(existing, klass) << descriptor;
+  CHECK(!existing->IsResolved()) << descriptor;
+  CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor;
+  CHECK(!klass->IsTemp()) << descriptor;
+  VerifyObject(klass);
+  // Update the element in the hash set with the new class. This is safe to do since the descriptor
+  // doesn't change.
+  *existing_it = GcRoot<mirror::Class>(klass);
+  return existing;
+}
+
+void ClassTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags ATTRIBUTE_UNUSED) {
+  BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
+      visitor, RootInfo(kRootStickyClass));
+  for (ClassSet& class_set : classes_) {
+    for (GcRoot<mirror::Class>& root : class_set) {
+      buffered_visitor.VisitRoot(root);
+    }
+  }
+}
+
+bool ClassTable::Visit(ClassVisitor* visitor) {
+  for (ClassSet& class_set : classes_) {
+    for (GcRoot<mirror::Class>& root : class_set) {
+      if (!visitor->Visit(root.Read())) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+size_t ClassTable::NumZygoteClasses() const {
+  size_t sum = 0;
+  for (size_t i = 0; i < classes_.size() - 1; ++i) {
+    sum += classes_[i].Size();
+  }
+  return sum;
+}
+
+size_t ClassTable::NumNonZygoteClasses() const {
+  return classes_.back().Size();
+}
+
+mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) {
+  for (ClassSet& class_set : classes_) {
+    auto it = class_set.FindWithHash(descriptor, hash);
+    if (it != class_set.end()) {
+     return it->Read();
+    }
+  }
+  return nullptr;
+}
+
+void ClassTable::Insert(mirror::Class* klass) {
+  classes_.back().Insert(GcRoot<mirror::Class>(klass));
+}
+
+void ClassTable::InsertWithHash(mirror::Class* klass, size_t hash) {
+  classes_.back().InsertWithHash(GcRoot<mirror::Class>(klass), hash);
+}
+
+bool ClassTable::Remove(const char* descriptor) {
+  for (ClassSet& class_set : classes_) {
+    auto it = class_set.Find(descriptor);
+    if (it != class_set.end()) {
+      class_set.Erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
+std::size_t ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root)
+    const {
+  std::string temp;
+  return ComputeModifiedUtf8Hash(root.Read()->GetDescriptor(&temp));
+}
+
+bool ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
+                                                       const GcRoot<mirror::Class>& b) const {
+  DCHECK_EQ(a.Read()->GetClassLoader(), b.Read()->GetClassLoader());
+  std::string temp;
+  return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp));
+}
+
+bool ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
+                                                       const char* descriptor) const {
+  return a.Read()->DescriptorEquals(descriptor);
+}
+
+std::size_t ClassTable::ClassDescriptorHashEquals::operator()(const char* descriptor) const {
+  return ComputeModifiedUtf8Hash(descriptor);
+}
+
+}  // namespace art
diff --git a/runtime/class_table.h b/runtime/class_table.h
new file mode 100644
index 0000000..252a47d
--- /dev/null
+++ b/runtime/class_table.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_CLASS_TABLE_H_
+#define ART_RUNTIME_CLASS_TABLE_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/allocator.h"
+#include "base/hash_set.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "dex_file.h"
+#include "gc_root.h"
+#include "object_callbacks.h"
+#include "runtime.h"
+
+namespace art {
+
+namespace mirror {
+  class ClassLoader;
+}  // namespace mirror
+
+class ClassVisitor {
+ public:
+  virtual ~ClassVisitor() {}
+  // Return true to continue visiting.
+  virtual bool Visit(mirror::Class* klass) = 0;
+};
+
+// Each loader has a ClassTable
+class ClassTable {
+ public:
+  ClassTable();
+
+  // Used by image writer for checking.
+  bool Contains(mirror::Class* klass)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Freeze the current class tables by allocating a new table and never updating or modifying the
+  // existing table. This helps prevents dirty pages after caused by inserting after zygote fork.
+  void FreezeSnapshot()
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Returns the number of classes in previous snapshots.
+  size_t NumZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+
+  // Returns all off the classes in the lastest snapshot.
+  size_t NumNonZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+
+  // Update a class in the table with the new class. Returns the existing class which was replaced.
+  mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Return false if the callback told us to exit.
+  bool Visit(ClassVisitor* visitor)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  mirror::Class* Lookup(const char* descriptor, size_t hash)
+      SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
+  void Insert(mirror::Class* klass)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+  void InsertWithHash(mirror::Class* klass, size_t hash)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Returns true if the class was found and removed, false otherwise.
+  bool Remove(const char* descriptor)
+      REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ private:
+  class ClassDescriptorHashEquals {
+   public:
+    // Same class loader and descriptor.
+    std::size_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS;
+    bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const
+        NO_THREAD_SAFETY_ANALYSIS;;
+    // Same descriptor.
+    bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor) const
+        NO_THREAD_SAFETY_ANALYSIS;
+    std::size_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS;
+  };
+  class GcRootEmptyFn {
+   public:
+    void MakeEmpty(GcRoot<mirror::Class>& item) const {
+      item = GcRoot<mirror::Class>();
+    }
+    bool IsEmpty(const GcRoot<mirror::Class>& item) const {
+      return item.IsNull();
+    }
+  };
+  // hash set which hashes class descriptor, and compares descriptors nad class loaders. Results
+  // should be compared for a matching Class descriptor and class loader.
+  typedef HashSet<GcRoot<mirror::Class>, GcRootEmptyFn, ClassDescriptorHashEquals,
+      ClassDescriptorHashEquals, TrackingAllocator<GcRoot<mirror::Class>, kAllocatorTagClassTable>>
+      ClassSet;
+
+  // TODO: shard lock to have one per class loader.
+  std::vector<ClassSet> classes_ GUARDED_BY(Locks::classlinker_classes_lock_);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_CLASS_TABLE_H_
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 287a50b..c9ae9b8 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -948,33 +948,27 @@
   return JDWP::ERR_NONE;
 }
 
+// Get the complete list of reference classes (i.e. all classes except
+// the primitive types).
+// Returns a newly-allocated buffer full of RefTypeId values.
+class ClassListCreator : public ClassVisitor {
+ public:
+  explicit ClassListCreator(std::vector<JDWP::RefTypeId>* classes) : classes_(classes) {}
+
+  bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!c->IsPrimitive()) {
+      classes_->push_back(Dbg::GetObjectRegistry()->AddRefType(c));
+    }
+    return true;
+  }
+
+ private:
+  std::vector<JDWP::RefTypeId>* const classes_;
+};
+
 void Dbg::GetClassList(std::vector<JDWP::RefTypeId>* classes) {
-  // Get the complete list of reference classes (i.e. all classes except
-  // the primitive types).
-  // Returns a newly-allocated buffer full of RefTypeId values.
-  struct ClassListCreator {
-    explicit ClassListCreator(std::vector<JDWP::RefTypeId>* classes_in) : classes(classes_in) {
-    }
-
-    static bool Visit(mirror::Class* c, void* arg) {
-      return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
-    }
-
-    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
-    // annotalysis.
-    bool Visit(mirror::Class* c) NO_THREAD_SAFETY_ANALYSIS {
-      if (!c->IsPrimitive()) {
-        classes->push_back(gRegistry->AddRefType(c));
-      }
-      return true;
-    }
-
-    std::vector<JDWP::RefTypeId>* const classes;
-  };
-
   ClassListCreator clc(classes);
-  Runtime::Current()->GetClassLinker()->VisitClassesWithoutClassesLock(ClassListCreator::Visit,
-                                                                       &clc);
+  Runtime::Current()->GetClassLinker()->VisitClassesWithoutClassesLock(&clc);
 }
 
 JDWP::JdwpError Dbg::GetClassInfo(JDWP::RefTypeId class_id, JDWP::JdwpTypeTag* pTypeTag,
@@ -4632,7 +4626,7 @@
   // Send a series of heap segment chunks.
   HeapChunkContext context(what == HPSG_WHAT_MERGED_OBJECTS, native);
   if (native) {
-#if defined(HAVE_ANDROID_OS) && defined(USE_DLMALLOC)
+#if defined(__ANDROID__) && defined(USE_DLMALLOC)
     dlmalloc_inspect_all(HeapChunkContext::HeapChunkNativeCallback, &context);
     HeapChunkContext::HeapChunkNativeCallback(nullptr, nullptr, 0, &context);  // Indicate end of a space.
 #else
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index cef2510..3d3f7a1 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -20,6 +20,7 @@
 #include <jni.h>
 
 #include "base/macros.h"
+#include "base/mutex.h"
 #include "offsets.h"
 
 #define QUICK_ENTRYPOINT_OFFSET(ptr_size, x) \
@@ -71,6 +72,16 @@
                            Thread* self)
     NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
 
+// Read barrier entrypoints.
+// Compilers for ARM, ARM64, MIPS, MIPS64 can insert a call to this function directly.
+// For x86 and x86_64, compilers need a wrapper assembly function, to handle mismatch in ABI.
+// This is the read barrier slow path for instance and static fields and reference-type arrays.
+// TODO: Currently the read barrier does not have a fast path for compilers to directly generate.
+// Ideally the slow path should only take one parameter "ref".
+extern "C" mirror::Object* artReadBarrierSlow(mirror::Object* ref, mirror::Object* obj,
+                                              uint32_t offset)
+    SHARED_REQUIRES(Locks::mutator_lock_) HOT_ATTR;
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_H_
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 60bbf4a..73d8ae7 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -145,7 +145,8 @@
   V(NewStringFromStringBuffer, void) \
   V(NewStringFromStringBuilder, void) \
 \
-  V(ReadBarrierJni, void, mirror::CompressedReference<mirror::Object>*, Thread*)
+  V(ReadBarrierJni, void, mirror::CompressedReference<mirror::Object>*, Thread*) \
+  V(ReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t)
 
 #endif  // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_
 #undef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_   // #define is only for lint.
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 25a943a..0a1d806 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -557,4 +557,16 @@
   return -1;  // failure
 }
 
+// TODO: Currently the read barrier does not have a fast path. Ideally the slow path should only
+// take one parameter "ref", which is generated by the fast path.
+extern "C" mirror::Object* artReadBarrierSlow(mirror::Object* ref ATTRIBUTE_UNUSED,
+                                              mirror::Object* obj, uint32_t offset) {
+  DCHECK(kUseReadBarrier);
+  uint8_t* raw_addr = reinterpret_cast<uint8_t*>(obj) + offset;
+  mirror::HeapReference<mirror::Object>* ref_addr =
+      reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_addr);
+  return ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, true>(obj, MemberOffset(offset),
+                                                                      ref_addr);
+}
+
 }  // namespace art
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index c05c935..f7a3cd5 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -311,8 +311,9 @@
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuilder, pReadBarrierJni,
                          sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pReadBarrierJni, pReadBarrierSlow, sizeof(void*));
 
-    CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pReadBarrierJni)
+    CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pReadBarrierSlow)
             + sizeof(void*) == sizeof(QuickEntryPoints), QuickEntryPoints_all);
   }
 };
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 68e7fa0..157f609 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -110,6 +110,18 @@
     }
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (kIsDebugBuild && !root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    DCHECK(from_space_->HasAddress(root->AsMirrorPtr()));
+  }
+
  private:
   MarkObjectVisitor* const visitor_;
   // Space which we are scanning
@@ -163,7 +175,7 @@
   }
 
   // Extra parameters are required since we use this same visitor signature for checking objects.
-  void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const
+  void operator()(Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::mutator_lock_) {
     mirror::HeapReference<Object>* ref_ptr = obj->GetFieldObjectReferenceAddr(offset);
     mirror::Object* ref = ref_ptr->AsMirrorPtr();
@@ -174,6 +186,18 @@
     }
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (kIsDebugBuild && !root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    DCHECK(!mod_union_table_->ShouldAddReference(root->AsMirrorPtr()));
+  }
+
  private:
   ModUnionTableReferenceCache* const mod_union_table_;
   std::vector<mirror::HeapReference<Object>*>* const references_;
@@ -208,7 +232,7 @@
   }
 
   // Extra parameters are required since we use this same visitor signature for checking objects.
-  void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const
+  void operator()(Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
     mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
     if (ref != nullptr && mod_union_table_->ShouldAddReference(ref) &&
@@ -228,6 +252,18 @@
     }
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (kIsDebugBuild && !root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    DCHECK(!mod_union_table_->ShouldAddReference(root->AsMirrorPtr()));
+  }
+
  private:
   ModUnionTableReferenceCache* const mod_union_table_;
   const std::set<const Object*>& references_;
diff --git a/runtime/gc/accounting/remembered_set.cc b/runtime/gc/accounting/remembered_set.cc
index 994a0ad..b9f24f3 100644
--- a/runtime/gc/accounting/remembered_set.cc
+++ b/runtime/gc/accounting/remembered_set.cc
@@ -67,7 +67,7 @@
       : collector_(collector), target_space_(target_space),
         contains_reference_to_target_space_(contains_reference_to_target_space) {}
 
-  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::mutator_lock_) {
     DCHECK(obj != nullptr);
     mirror::HeapReference<mirror::Object>* ref_ptr = obj->GetFieldObjectReferenceAddr(offset);
@@ -79,14 +79,29 @@
   }
 
   void operator()(mirror::Class* klass, mirror::Reference* ref) const
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(Locks::heap_bitmap_lock_) {
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
     if (target_space_->HasAddress(ref->GetReferent())) {
       *contains_reference_to_target_space_ = true;
       collector_->DelayReferenceReferent(klass, ref);
     }
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (target_space_->HasAddress(root->AsMirrorPtr())) {
+      *contains_reference_to_target_space_ = true;
+      root->Assign(collector_->MarkObject(root->AsMirrorPtr()));
+      DCHECK(!target_space_->HasAddress(root->AsMirrorPtr()));
+    }
+  }
+
  private:
   collector::GarbageCollector* const collector_;
   space::ContinuousSpace* const target_space_;
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index ec4d626..16c9354 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -20,7 +20,7 @@
 #include "base/stl_util.h"
 #include "stack.h"
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #include "cutils/properties.h"
 #endif
 
@@ -42,7 +42,7 @@
 }
 
 void AllocRecordObjectMap::SetProperties() {
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   // Check whether there's a system property overriding the max number of records.
   const char* propertyName = "dalvik.vm.allocTrackerMax";
   char allocMaxString[PROPERTY_VALUE_MAX];
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
index 0e91a43..0558921 100644
--- a/runtime/gc/allocator/dlmalloc.h
+++ b/runtime/gc/allocator/dlmalloc.h
@@ -35,7 +35,7 @@
 #include "../../bionic/libc/upstream-dlmalloc/malloc.h"
 #pragma GCC diagnostic pop
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 // Define dlmalloc routines from bionic that cannot be included directly because of redefining
 // symbols from the include above.
 extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg);
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index abaa97f..470bc1c 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1170,7 +1170,7 @@
 
   // First mark slots to free in the bulk free bit map without locking the
   // size bracket locks. On host, unordered_set is faster than vector + flag.
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   std::vector<Run*> runs;
 #else
   std::unordered_set<Run*, hash_run, eq_run> runs;
@@ -1237,7 +1237,7 @@
     DCHECK_EQ(run->magic_num_, kMagicNum);
     // Set the bit in the bulk free bit map.
     freed_bytes += run->MarkBulkFreeBitMap(ptr);
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
     if (!run->to_be_bulk_freed_) {
       run->to_be_bulk_freed_ = true;
       runs.push_back(run);
@@ -1252,7 +1252,7 @@
   // union the bulk free bit map into the thread-local free bit map
   // (for thread-local runs.)
   for (Run* run : runs) {
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
     DCHECK(run->to_be_bulk_freed_);
     run->to_be_bulk_freed_ = false;
 #endif
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index baa33b3..ec689f8 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -638,7 +638,7 @@
   explicit ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor(ConcurrentCopying* collector)
       : collector_(collector) {}
 
-  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
     mirror::Object* ref =
         obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
@@ -651,6 +651,19 @@
     this->operator()(ref, mirror::Reference::ReferentOffset(), false);
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector_);
+    visitor(root->AsMirrorPtr());
+  }
+
  private:
   ConcurrentCopying* const collector_;
 };
@@ -750,18 +763,31 @@
   explicit ConcurrentCopyingAssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector)
       : collector_(collector) {}
 
-  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
     mirror::Object* ref =
         obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
     ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_);
     visitor(ref);
   }
-  void operator()(mirror::Class* klass, mirror::Reference* /* ref */) const
+  void operator()(mirror::Class* klass, mirror::Reference* ref ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
     CHECK(klass->IsTypeOfReferenceClass());
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_);
+    visitor(root->AsMirrorPtr());
+  }
+
  private:
   ConcurrentCopying* const collector_;
 };
@@ -1500,6 +1526,18 @@
     collector_->DelayReferenceReferent(klass, ref);
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    collector_->MarkRoot(root);
+  }
+
  private:
   ConcurrentCopying* const collector_;
 };
@@ -1513,7 +1551,8 @@
 
 // Process a field.
 inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
-  mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
+  mirror::Object* ref = obj->GetFieldObject<
+      mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
   if (ref == nullptr || region_space_->IsInToSpace(ref)) {
     return;
   }
@@ -1530,8 +1569,8 @@
       // It was updated by the mutator.
       break;
     }
-  } while (!obj->CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier<false, false, kVerifyNone>(
-      offset, expected_ref, new_ref));
+  } while (!obj->CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier<
+      false, false, kVerifyNone>(offset, expected_ref, new_ref));
 }
 
 // Process some roots.
@@ -1559,22 +1598,18 @@
   }
 }
 
-void ConcurrentCopying::VisitRoots(
-    mirror::CompressedReference<mirror::Object>** roots, size_t count,
-    const RootInfo& info ATTRIBUTE_UNUSED) {
-  for (size_t i = 0; i < count; ++i) {
-    mirror::CompressedReference<mirror::Object>* root = roots[i];
-    mirror::Object* ref = root->AsMirrorPtr();
-    if (ref == nullptr || region_space_->IsInToSpace(ref)) {
-      continue;
-    }
-    mirror::Object* to_ref = Mark(ref);
-    if (to_ref == ref) {
-      continue;
-    }
+void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
+  DCHECK(!root->IsNull());
+  mirror::Object* const ref = root->AsMirrorPtr();
+  if (region_space_->IsInToSpace(ref)) {
+    return;
+  }
+  mirror::Object* to_ref = Mark(ref);
+  if (to_ref != ref) {
     auto* addr = reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root);
     auto expected_ref = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref);
     auto new_ref = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(to_ref);
+    // If the cas fails, then it was updated by the mutator.
     do {
       if (ref != addr->LoadRelaxed().AsMirrorPtr()) {
         // It was updated by the mutator.
@@ -1584,6 +1619,17 @@
   }
 }
 
+void ConcurrentCopying::VisitRoots(
+    mirror::CompressedReference<mirror::Object>** roots, size_t count,
+    const RootInfo& info ATTRIBUTE_UNUSED) {
+  for (size_t i = 0; i < count; ++i) {
+    mirror::CompressedReference<mirror::Object>* const root = roots[i];
+    if (!root->IsNull()) {
+      MarkRoot(root);
+    }
+  }
+}
+
 // Fill the given memory block with a dummy object. Used to fill in a
 // copy of objects that was lost in race.
 void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) {
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index d324ce1..a4fd71c 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -122,6 +122,8 @@
   virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info)
       OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+  void MarkRoot(mirror::CompressedReference<mirror::Object>* root)
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
   virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
                           const RootInfo& info)
       OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_)
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index c5ad613..4b2c588 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -438,6 +438,19 @@
         ref->GetFieldObjectReferenceAddr<kVerifyNone>(mirror::Reference::ReferentOffset()));
   }
 
+  // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    root->Assign(collector_->GetMarkedForwardAddress(root->AsMirrorPtr()));
+  }
+
  private:
   MarkCompact* const collector_;
 };
@@ -575,6 +588,19 @@
     collector_->DelayReferenceReferent(klass, ref);
   }
 
+  // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    collector_->MarkObject(root->AsMirrorPtr());
+  }
+
  private:
   MarkCompact* const collector_;
 };
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 92dde51..7f2c204 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -365,7 +365,7 @@
       : mark_sweep_(mark_sweep), holder_(holder), offset_(offset) {
   }
 
-  void operator()(const mirror::Object* obj) const ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS {
+  void operator()(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
     if (kProfileLargeObjects) {
       // TODO: Differentiate between marking and testing somehow.
       ++mark_sweep_->large_object_test_;
@@ -597,8 +597,7 @@
       : mark_sweep_(mark_sweep) {}
 
   void operator()(mirror::Object* obj) const ALWAYS_INLINE
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(Locks::heap_bitmap_lock_) {
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
     if (kCheckLocks) {
       Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
       Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current());
@@ -649,13 +648,33 @@
  protected:
   class MarkObjectParallelVisitor {
    public:
-    explicit MarkObjectParallelVisitor(MarkStackTask<kUseFinger>* chunk_task,
-                                       MarkSweep* mark_sweep) ALWAYS_INLINE
-            : chunk_task_(chunk_task), mark_sweep_(mark_sweep) {}
+    ALWAYS_INLINE explicit MarkObjectParallelVisitor(MarkStackTask<kUseFinger>* chunk_task,
+                                                     MarkSweep* mark_sweep)
+        : chunk_task_(chunk_task), mark_sweep_(mark_sweep) {}
 
-    void operator()(mirror::Object* obj, MemberOffset offset, bool /* static */) const ALWAYS_INLINE
+    void operator()(mirror::Object* obj, MemberOffset offset, bool /* static */) const
+        ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+      Mark(obj->GetFieldObject<mirror::Object>(offset));
+    }
+
+    void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
         SHARED_REQUIRES(Locks::mutator_lock_) {
-      mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
+      if (!root->IsNull()) {
+        VisitRoot(root);
+      }
+    }
+
+    void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+        SHARED_REQUIRES(Locks::mutator_lock_) {
+      if (kCheckLocks) {
+        Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+        Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current());
+      }
+      Mark(root->AsMirrorPtr());
+    }
+
+   private:
+    void Mark(mirror::Object* ref) const ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
       if (ref != nullptr && mark_sweep_->MarkObjectParallel(ref)) {
         if (kUseFinger) {
           std::atomic_thread_fence(std::memory_order_seq_cst);
@@ -668,7 +687,6 @@
       }
     }
 
-   private:
     MarkStackTask<kUseFinger>* const chunk_task_;
     MarkSweep* const mark_sweep_;
   };
@@ -1268,6 +1286,22 @@
     mark_sweep_->MarkObject(obj->GetFieldObject<mirror::Object>(offset), obj, offset);
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+    if (kCheckLocks) {
+      Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+      Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current());
+    }
+    mark_sweep_->MarkObject(root->AsMirrorPtr());
+  }
+
  private:
   MarkSweep* const mark_sweep_;
 };
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 99e00f9..606be63 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -58,12 +58,12 @@
 
   ~MarkSweep() {}
 
-  virtual void RunPhases() OVERRIDE NO_THREAD_SAFETY_ANALYSIS;
+  virtual void RunPhases() OVERRIDE REQUIRES(!mark_stack_lock_);
   void InitializePhase();
-  void MarkingPhase() SHARED_REQUIRES(Locks::mutator_lock_, !mark_stack_lock_);
+  void MarkingPhase() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
   void PausePhase() REQUIRES(Locks::mutator_lock_, !mark_stack_lock_);
   void ReclaimPhase() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
-  void FinishPhase() SHARED_REQUIRES(Locks::mutator_lock_);
+  void FinishPhase();
   virtual void MarkReachableObjects()
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_, !mark_stack_lock_);
 
diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h
index a7de44f..06d20f5 100644
--- a/runtime/gc/collector/semi_space-inl.h
+++ b/runtime/gc/collector/semi_space-inl.h
@@ -83,6 +83,14 @@
   }
 }
 
+template<bool kPoisonReferences>
+inline void SemiSpace::MarkObjectIfNotInToSpace(
+    mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr) {
+  if (!to_space_->HasAddress(obj_ptr->AsMirrorPtr())) {
+    MarkObject(obj_ptr);
+  }
+}
+
 }  // namespace collector
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index e93ff05..63def24 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -295,8 +295,26 @@
       LOG(FATAL) << ref << " found in from space";
     }
   }
+
+  // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    if (kIsDebugBuild) {
+      Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+      Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current());
+    }
+    CHECK(!from_space_->HasAddress(root->AsMirrorPtr()));
+  }
+
  private:
-  space::ContinuousMemMapAllocSpace* from_space_;
+  space::ContinuousMemMapAllocSpace* const from_space_;
 };
 
 void SemiSpace::VerifyNoFromSpaceReferences(Object* obj) {
@@ -313,6 +331,7 @@
     DCHECK(obj != nullptr);
     semi_space_->VerifyNoFromSpaceReferences(obj);
   }
+
  private:
   SemiSpace* const semi_space_;
 };
@@ -670,11 +689,29 @@
   }
 
   void operator()(mirror::Class* klass, mirror::Reference* ref) const
-      SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(Locks::heap_bitmap_lock_) {
+      REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     collector_->DelayReferenceReferent(klass, ref);
   }
 
+  // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      NO_THREAD_SAFETY_ANALYSIS {
+    if (kIsDebugBuild) {
+      Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+      Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current());
+    }
+    // We may visit the same root multiple times, so avoid marking things in the to-space since
+    // this is not handled by the GC.
+    collector_->MarkObjectIfNotInToSpace(root);
+  }
+
  private:
   SemiSpace* const collector_;
 };
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index d5772a0..b9246ca 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -103,6 +103,10 @@
   void MarkObject(mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr)
       REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
+  template<bool kPoisonReferences>
+  void MarkObjectIfNotInToSpace(mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr)
+      REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+
   virtual mirror::Object* MarkObject(mirror::Object* root) OVERRIDE
       REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 7a9e418..5f617bd 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1277,7 +1277,7 @@
   FinishGC(self, collector::kGcTypeNone);
   size_t native_reclaimed = 0;
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   // Only trim the native heap if we don't care about pauses.
   if (!CareAboutPauseTimes()) {
 #if defined(USE_DLMALLOC)
@@ -1290,7 +1290,7 @@
     UNIMPLEMENTED(WARNING) << "Add trimming support";
 #endif
   }
-#endif  // HAVE_ANDROID_OS
+#endif  // __ANDROID__
   uint64_t end_ns = NanoTime();
   VLOG(heap) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns)
       << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration="
@@ -1780,7 +1780,7 @@
   }
 
   // For Object::VisitReferences.
-  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::mutator_lock_) {
     mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
     if (ref == object_ && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
@@ -1788,6 +1788,10 @@
     }
   }
 
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+      const {}
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
  private:
   const mirror::Object* const object_;
   const uint32_t max_count_;
@@ -2612,15 +2616,14 @@
     return fail_count_->LoadSequentiallyConsistent();
   }
 
-  void operator()(mirror::Class* klass, mirror::Reference* ref) const
+  void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    UNUSED(klass);
     if (verify_referent_) {
       VerifyReference(ref, ref->GetReferent(), mirror::Reference::ReferentOffset());
     }
   }
 
-  void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const
+  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
       SHARED_REQUIRES(Locks::mutator_lock_) {
     VerifyReference(obj, obj->GetFieldObject<mirror::Object>(offset), offset);
   }
@@ -2629,7 +2632,19 @@
     return heap_->IsLiveObjectLocked(obj, true, false, true);
   }
 
-  void VisitRoot(mirror::Object* root, const RootInfo& root_info) OVERRIDE
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    const_cast<VerifyReferenceVisitor*>(this)->VisitRoot(
+        root->AsMirrorPtr(), RootInfo(kRootVMInternal));
+  }
+
+  virtual void VisitRoot(mirror::Object* root, const RootInfo& root_info) OVERRIDE
       SHARED_REQUIRES(Locks::mutator_lock_) {
     if (root == nullptr) {
       LOG(ERROR) << "Root is null with info " << root_info.GetType();
@@ -2747,7 +2762,7 @@
       : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {
   }
 
-  void operator()(mirror::Object* obj) const
+  void operator()(mirror::Object* obj)
       SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     // Note: we are verifying the references in obj but not obj itself, this is because obj must
     // be live or else how did we find it in the live bitmap?
@@ -2762,8 +2777,7 @@
     visitor->operator()(obj);
   }
 
-  void VerifyRoots() SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(!Locks::heap_bitmap_lock_) {
+  void VerifyRoots() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_) {
     ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
     VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_);
     Runtime::Current()->VisitRoots(&visitor);
@@ -2860,6 +2874,11 @@
       : heap_(heap), failed_(failed) {
   }
 
+  // There is no card marks for native roots on a class.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+      const {}
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
   // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for
   // annotalysis on visitors.
   void operator()(mirror::Object* obj, MemberOffset offset, bool is_static) const
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 75fc84b..c9ba6cf 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -28,6 +28,8 @@
 
 namespace art {
 
+static constexpr bool kDumpStackOnNonLocalReference = false;
+
 template<typename T>
 class MutatorLockedDumpable {
  public:
@@ -183,7 +185,9 @@
       if (env->check_jni) {
         ScopedObjectAccess soa(self);
         LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
-        self->Dump(LOG(WARNING));
+        if (kDumpStackOnNonLocalReference) {
+          self->Dump(LOG(WARNING));
+        }
       }
       return true;
     }
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 9711cf2..e28d578 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -49,12 +49,20 @@
 static constexpr StackVisitor::StackWalkKind kInstrumentationStackWalk =
     StackVisitor::StackWalkKind::kSkipInlinedFrames;
 
-static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
-    REQUIRES(Locks::mutator_lock_) {
-  Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
-  instrumentation->InstallStubsForClass(klass);
-  return true;  // we visit all classes.
-}
+class InstallStubsClassVisitor : public ClassVisitor {
+ public:
+  explicit InstallStubsClassVisitor(Instrumentation* instrumentation)
+      : instrumentation_(instrumentation) {}
+
+  bool Visit(mirror::Class* klass) OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    instrumentation_->InstallStubsForClass(klass);
+    return true;  // we visit all classes.
+  }
+
+ private:
+  Instrumentation* const instrumentation_;
+};
+
 
 Instrumentation::Instrumentation()
     : instrumentation_stubs_installed_(false), entry_exit_stubs_installed_(false),
@@ -563,14 +571,16 @@
       entry_exit_stubs_installed_ = true;
       interpreter_stubs_installed_ = false;
     }
-    runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);
+    InstallStubsClassVisitor visitor(this);
+    runtime->GetClassLinker()->VisitClasses(&visitor);
     instrumentation_stubs_installed_ = true;
     MutexLock mu(self, *Locks::thread_list_lock_);
     runtime->GetThreadList()->ForEach(InstrumentationInstallStack, this);
   } else {
     interpreter_stubs_installed_ = false;
     entry_exit_stubs_installed_ = false;
-    runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);
+    InstallStubsClassVisitor visitor(this);
+    runtime->GetClassLinker()->VisitClasses(&visitor);
     // Restore stack only if there is no method currently deoptimized.
     bool empty;
     {
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 2486a98..a6cccef 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -553,7 +553,7 @@
   ArtMethod* unboxed_closure = nullptr;
   // Raise an exception if unboxing fails.
   if (!Runtime::Current()->GetLambdaBoxTable()->UnboxLambda(boxed_closure_object,
-                                                            &unboxed_closure)) {
+                                                            outof(unboxed_closure))) {
     CHECK(self->IsExceptionPending());
     return false;
   }
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index adc2912..51952c4 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -24,7 +24,7 @@
 #include "base/stringprintf.h"
 #include "jdwp/jdwp_priv.h"
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #include "cutils/sockets.h"
 #endif
 
@@ -224,7 +224,7 @@
        */
       int  ret = connect(control_sock_, &control_addr_.controlAddrPlain, control_addr_len_);
       if (!ret) {
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
         if (!socket_peer_is_trusted(control_sock_)) {
           if (shutdown(control_sock_, SHUT_RDWR)) {
             PLOG(ERROR) << "trouble shutting down socket";
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index 260abe7..5a9a0f5 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -248,7 +248,7 @@
     case kJdwpTransportSocket:
       InitSocketTransport(state.get(), options);
       break;
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
     case kJdwpTransportAndroidAdb:
       InitAdbTransport(state.get(), options);
       break;
diff --git a/runtime/lambda/box_table.cc b/runtime/lambda/box_table.cc
index 64a6076..22cc820 100644
--- a/runtime/lambda/box_table.cc
+++ b/runtime/lambda/box_table.cc
@@ -94,8 +94,7 @@
   return method_as_object;
 }
 
-bool BoxTable::UnboxLambda(mirror::Object* object, ClosureType* out_closure) {
-  DCHECK(object != nullptr);
+bool BoxTable::UnboxLambda(mirror::Object* object, out<ClosureType> out_closure) {
   *out_closure = nullptr;
 
   // Note that we do not need to access lambda_table_lock_ here
diff --git a/runtime/lambda/box_table.h b/runtime/lambda/box_table.h
index 312d811..c6d3d0c 100644
--- a/runtime/lambda/box_table.h
+++ b/runtime/lambda/box_table.h
@@ -18,6 +18,7 @@
 
 #include "base/allocator.h"
 #include "base/hash_map.h"
+#include "base/out.h"
 #include "gc_root.h"
 #include "base/macros.h"
 #include "base/mutex.h"
@@ -51,7 +52,7 @@
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::lambda_table_lock_);
 
   // Unboxes an object back into the lambda. Returns false and throws an exception on failure.
-  bool UnboxLambda(mirror::Object* object, ClosureType* out_closure)
+  bool UnboxLambda(mirror::Object* object, out<ClosureType> out_closure)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Sweep weak references to lambda boxes. Update the addresses if the objects have been
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 8df8f96..d9ad7dc 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -280,7 +280,7 @@
   ScopedFd fd(-1);
 
 #ifdef USE_ASHMEM
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   const bool use_ashmem = true;
 #else
   // When not on Android ashmem is faked using files in /tmp. Ensure that such files won't
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index fc27315..6568487 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -680,6 +680,8 @@
     // linked yet.
     VisitStaticFieldsReferences<kVisitClass>(this, visitor);
   }
+  // Since this class is reachable, we must also visit the associated roots when we scan it.
+  VisitNativeRoots(visitor, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
 }
 
 template<ReadBarrierOption kReadBarrierOption>
@@ -816,20 +818,22 @@
   if (sfields != nullptr) {
     for (size_t i = 0, count = NumStaticFields(); i < count; ++i) {
       auto* f = &sfields[i];
+      // Visit roots first in case the declaring class gets moved.
+      f->VisitRoots(visitor);
       if (kIsDebugBuild && IsResolved()) {
         CHECK_EQ(f->GetDeclaringClass(), this) << GetStatus();
       }
-      f->VisitRoots(visitor);
     }
   }
   ArtField* const ifields = GetIFieldsUnchecked();
   if (ifields != nullptr) {
     for (size_t i = 0, count = NumInstanceFields(); i < count; ++i) {
       auto* f = &ifields[i];
+      // Visit roots first in case the declaring class gets moved.
+      f->VisitRoots(visitor);
       if (kIsDebugBuild && IsResolved()) {
         CHECK_EQ(f->GetDeclaringClass(), this) << GetStatus();
       }
-      f->VisitRoots(visitor);
     }
   }
   // We may see GetDirectMethodsPtr() == null with NumDirectMethods() != 0 if the root marking
@@ -909,6 +913,33 @@
   DCHECK_EQ(pointer_size, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
 }
 
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+inline Class* Class::GetComponentType() {
+  return GetFieldObject<Class, kVerifyFlags, kReadBarrierOption>(ComponentTypeOffset());
+}
+
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+inline bool Class::IsArrayClass() {
+  return GetComponentType<kVerifyFlags, kReadBarrierOption>() != nullptr;
+}
+
+inline bool Class::IsAssignableFrom(Class* src) {
+  DCHECK(src != nullptr);
+  if (this == src) {
+    // Can always assign to things of the same type.
+    return true;
+  } else if (IsObjectClass()) {
+    // Can assign any reference to java.lang.Object.
+    return !src->IsPrimitive();
+  } else if (IsInterface()) {
+    return src->Implements(this);
+  } else if (src->IsArrayClass()) {
+    return IsAssignableFromArray(src);
+  } else {
+    return !src->IsInterface() && src->IsSubClass(this);
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index c01a5e8..d95bcd8 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -404,9 +404,8 @@
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-  bool IsArrayClass() SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetComponentType<kVerifyFlags, kReadBarrierOption>() != nullptr;
-  }
+
+  bool IsArrayClass() SHARED_REQUIRES(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
@@ -423,9 +422,7 @@
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-  Class* GetComponentType() SHARED_REQUIRES(Locks::mutator_lock_) {
-    return GetFieldObject<Class, kVerifyFlags, kReadBarrierOption>(ComponentTypeOffset());
-  }
+  Class* GetComponentType() SHARED_REQUIRES(Locks::mutator_lock_);
 
   void SetComponentType(Class* new_component_type) SHARED_REQUIRES(Locks::mutator_lock_) {
     DCHECK(GetComponentType() == nullptr);
@@ -617,22 +614,7 @@
   // downcast would be necessary. Similarly for interfaces, a class that implements (or an interface
   // that extends) another can be assigned to its parent, but not vice-versa. All Classes may assign
   // to themselves. Classes for primitive types may not assign to each other.
-  ALWAYS_INLINE bool IsAssignableFrom(Class* src) SHARED_REQUIRES(Locks::mutator_lock_) {
-    DCHECK(src != nullptr);
-    if (this == src) {
-      // Can always assign to things of the same type.
-      return true;
-    } else if (IsObjectClass()) {
-      // Can assign any reference to java.lang.Object.
-      return !src->IsPrimitive();
-    } else if (IsInterface()) {
-      return src->Implements(this);
-    } else if (src->IsArrayClass()) {
-      return IsAssignableFromArray(src);
-    } else {
-      return !src->IsInterface() && src->IsSubClass(this);
-    }
-  }
+  ALWAYS_INLINE bool IsAssignableFrom(Class* src) SHARED_REQUIRES(Locks::mutator_lock_);
 
   ALWAYS_INLINE Class* GetSuperClass() SHARED_REQUIRES(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 27dd743..80decaa 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -63,6 +63,11 @@
     this->operator()(ref, mirror::Reference::ReferentOffset(), false);
   }
 
+  // Unused since we don't copy class native roots.
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+      const {}
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
  private:
   Object* const dest_obj_;
 };
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 7abc546..9ea339a 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -16,7 +16,7 @@
 
 #include "dalvik_system_VMRuntime.h"
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 extern "C" void android_set_application_target_sdk_version(uint32_t version);
 #endif
 #include <limits.h>
@@ -196,7 +196,7 @@
   // Note that targetSdkVersion may be 0, meaning "current".
   Runtime::Current()->SetTargetSdkVersion(target_sdk_version);
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   // This part is letting libc/dynamic linker know about current app's
   // target sdk version to enable compatibility workarounds.
   android_set_application_target_sdk_version(static_cast<uint32_t>(target_sdk_version));
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index abac815..856a3e7 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -31,10 +31,10 @@
 #include "verify_object-inl.h"
 
 #include <sstream>
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 // This function is provided by android linker.
 extern "C" void android_update_LD_LIBRARY_PATH(const char* ld_library_path);
-#endif  // HAVE_ANDROID_OS
+#endif  // __ANDROID__
 
 namespace art {
 
@@ -53,7 +53,7 @@
 }
 
 static void SetLdLibraryPath(JNIEnv* env, jstring javaLdLibraryPathJstr) {
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   if (javaLdLibraryPathJstr != nullptr) {
     ScopedUtfChars ldLibraryPath(env, javaLdLibraryPathJstr);
     if (ldLibraryPath.c_str() != nullptr) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 01faf3c..5bbb0dc 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -253,9 +253,9 @@
     break;
   case Primitive::kPrimChar:
     if (is_volatile) {
-      o->SetFieldBooleanVolatile<false>(offset, new_value.GetC());
+      o->SetFieldCharVolatile<false>(offset, new_value.GetC());
     } else {
-      o->SetFieldBoolean<false>(offset, new_value.GetC());
+      o->SetFieldChar<false>(offset, new_value.GetC());
     }
     break;
   case Primitive::kPrimInt:
diff --git a/runtime/oat.h b/runtime/oat.h
index ee2f3f6..29dd76c 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '6', '7', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '6', '8', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 098fe61..a23d94d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -27,7 +27,7 @@
 #include <sstream>
 
 // dlopen_ext support from bionic.
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #include "android/dlext.h"
 #endif
 
@@ -229,7 +229,7 @@
     *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
     return false;
   }
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   android_dlextinfo extinfo;
   extinfo.flags = ANDROID_DLEXT_FORCE_LOAD | ANDROID_DLEXT_FORCE_FIXED_VADDR;
   dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 7772354..25b5e49 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -263,6 +263,9 @@
       .Define("--cpu-abilist=_")
           .WithType<std::string>()
           .IntoKey(M::CpuAbiList)
+      .Define("-Xfingerprint:_")
+          .WithType<std::string>()
+          .IntoKey(M::Fingerprint)
       .Define({"-Xexperimental-lambdas", "-Xnoexperimental-lambdas"})
           .WithType<bool>()
           .WithValues({true, false})
diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc
index 53bc876..a7f7bcd 100644
--- a/runtime/prebuilt_tools_test.cc
+++ b/runtime/prebuilt_tools_test.cc
@@ -23,7 +23,7 @@
 namespace art {
 
 // Run the tests only on host.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
 
 class PrebuiltToolsTest : public CommonRuntimeTest {
 };
@@ -61,6 +61,6 @@
   }
 }
 
-#endif  // HAVE_ANDROID_OS
+#endif  // __ANDROID__
 
 }  // namespace art
diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h
index 4f408dd..710c21f 100644
--- a/runtime/read_barrier_c.h
+++ b/runtime/read_barrier_c.h
@@ -47,9 +47,4 @@
 #error "Only one of Baker or Brooks can be enabled at a time."
 #endif
 
-// A placeholder marker to indicate places to add read barriers in the
-// assembly code. This is a development time aid and to be removed
-// after read barriers are added.
-#define THIS_LOAD_REQUIRES_READ_BARRIER
-
 #endif  // ART_RUNTIME_READ_BARRIER_C_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index c92f08f..1914124 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -607,14 +607,14 @@
   // See storage config details at http://source.android.com/tech/storage/
   // Create private mount namespace shared by all children
   if (unshare(CLONE_NEWNS) == -1) {
-    PLOG(WARNING) << "Failed to unshare()";
+    PLOG(ERROR) << "Failed to unshare()";
     return false;
   }
 
   // Mark rootfs as being a slave so that changes from default
   // namespace only flow into our children.
   if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
-    PLOG(WARNING) << "Failed to mount() rootfs as MS_SLAVE";
+    PLOG(ERROR) << "Failed to mount() rootfs as MS_SLAVE";
     return false;
   }
 
@@ -625,7 +625,7 @@
   if (target_base != nullptr) {
     if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,
               "uid=0,gid=1028,mode=0751") == -1) {
-      LOG(WARNING) << "Failed to mount tmpfs to " << target_base;
+      PLOG(ERROR) << "Failed to mount tmpfs to " << target_base;
       return false;
     }
   }
@@ -852,6 +852,8 @@
 
   Split(runtime_options.GetOrDefault(Opt::CpuAbiList), ',', &cpu_abilist_);
 
+  fingerprint_ = runtime_options.ReleaseOrDefault(Opt::Fingerprint);
+
   if (runtime_options.GetOrDefault(Opt::Interpret)) {
     GetInstrumentation()->ForceInterpretOnly();
   }
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8aed768..4577b75 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -563,6 +563,11 @@
 
   bool IsDebuggable() const;
 
+  // Returns the build fingerprint, if set. Otherwise an empty string is returned.
+  std::string GetFingerprint() {
+    return fingerprint_;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -757,6 +762,9 @@
 
   MethodRefToStringInitRegMap method_ref_string_init_reg_map_;
 
+  // Contains the build fingerprint, if given as a parameter.
+  std::string fingerprint_;
+
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
 std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 9922c5f..02ed3a2 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -112,6 +112,7 @@
 RUNTIME_OPTIONS_KEY (unsigned int,        ZygoteMaxFailedBoots,           10)
 RUNTIME_OPTIONS_KEY (Unit,                NoDexFileFallback)
 RUNTIME_OPTIONS_KEY (std::string,         CpuAbiList)
+RUNTIME_OPTIONS_KEY (std::string,         Fingerprint)
 RUNTIME_OPTIONS_KEY (bool,                ExperimentalLambdas,            false) // -X[no]experimental-lambdas
 
 // Not parse-able from command line, but can be provided explicitly.
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 9f8c55c..6cb7950 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -133,8 +133,11 @@
 
   DumpCmdLine(os);
 
-  // Note: The string "ABI:" is chosen to match the format used by debuggerd.
-  os << "ABI: " << GetInstructionSetString(runtime->GetInstructionSet()) << "\n";
+  // Note: The strings "Build fingerprint:" and "ABI:" are chosen to match the format used by
+  // debuggerd. This allows, for example, the stack tool to work.
+  std::string fingerprint = runtime->GetFingerprint();
+  os << "Build fingerprint: '" << (fingerprint.empty() ? "unknown" : fingerprint) << "'\n";
+  os << "ABI: '" << GetInstructionSetString(runtime->GetInstructionSet()) << "'\n";
 
   os << "Build type: " << (kIsDebugBuild ? "debug" : "optimized") << "\n";
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index b07b244..2916eaa 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -19,6 +19,7 @@
 #include "arch/context.h"
 #include "art_method-inl.h"
 #include "base/hex_dump.h"
+#include "base/out.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc_map.h"
@@ -180,7 +181,7 @@
     } else {
       uint16_t reg = code_item->registers_size_ - code_item->ins_size_;
       uint32_t value = 0;
-      bool success = GetVReg(m, reg, kReferenceVReg, &value);
+      bool success = GetVReg(m, reg, kReferenceVReg, outof(value));
       // We currently always guarantee the `this` object is live throughout the method.
       CHECK(success) << "Failed to read the this object in " << PrettyMethod(m);
       return reinterpret_cast<mirror::Object*>(value);
@@ -375,8 +376,8 @@
   QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
   uint32_t vmap_offset_lo, vmap_offset_hi;
   // TODO: IsInContext stops before spotting floating point registers.
-  if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) &&
-      vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) {
+  if (vmap_table.IsInContext(vreg, kind_lo, outof(vmap_offset_lo)) &&
+      vmap_table.IsInContext(vreg + 1, kind_hi, outof(vmap_offset_hi))) {
     bool is_float = (kind_lo == kDoubleLoVReg);
     uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
     uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
@@ -399,8 +400,8 @@
                                                 uint64_t* val) const {
   uint32_t low_32bits;
   uint32_t high_32bits;
-  bool success = GetVRegFromOptimizedCode(m, vreg, kind_lo, &low_32bits);
-  success &= GetVRegFromOptimizedCode(m, vreg + 1, kind_hi, &high_32bits);
+  bool success = GetVRegFromOptimizedCode(m, vreg, kind_lo, outof(low_32bits));
+  success &= GetVRegFromOptimizedCode(m, vreg + 1, kind_hi, outof(high_32bits));
   if (success) {
     *val = (static_cast<uint64_t>(high_32bits) << 32) | static_cast<uint64_t>(low_32bits);
   }
@@ -452,7 +453,7 @@
   QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
   uint32_t vmap_offset;
   // TODO: IsInContext stops before spotting floating point registers.
-  if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) {
+  if (vmap_table.IsInContext(vreg, kind, outof(vmap_offset))) {
     bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
     uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
     uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
@@ -532,8 +533,8 @@
   QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
   uint32_t vmap_offset_lo, vmap_offset_hi;
   // TODO: IsInContext stops before spotting floating point registers.
-  if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) &&
-      vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) {
+  if (vmap_table.IsInContext(vreg, kind_lo, outof(vmap_offset_lo)) &&
+      vmap_table.IsInContext(vreg + 1, kind_hi, outof(vmap_offset_hi))) {
     bool is_float = (kind_lo == kDoubleLoVReg);
     uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
     uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 6949b0b..b3efad0 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2139,7 +2139,7 @@
   std::string str(ss.str());
   // log to stderr for debugging command line processes
   std::cerr << str;
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   // log to logcat for debugging frameworks processes
   LOG(INFO) << str;
 #endif
@@ -2304,6 +2304,7 @@
   QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuffer)
   QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuilder)
   QUICK_ENTRY_POINT_INFO(pReadBarrierJni)
+  QUICK_ENTRY_POINT_INFO(pReadBarrierSlow)
 #undef QUICK_ENTRY_POINT_INFO
 
   os << offset;
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index 9d54eba..9563b99 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -44,7 +44,7 @@
 
 void Thread::SetUpAlternateSignalStack() {
   // Create and set an alternate signal stack.
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   LOG(FATAL) << "Invalid use of alternate signal stack on Android";
 #endif
   stack_t ss;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 194d9fe..20512f9 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1130,9 +1130,13 @@
     os << prefix << StringPrintf("#%02zu pc ", it->num);
     bool try_addr2line = false;
     if (!BacktraceMap::IsValid(it->map)) {
-      os << StringPrintf("%08" PRIxPTR "  ???", it->pc);
+      os << StringPrintf(Is64BitInstructionSet(kRuntimeISA) ? "%016" PRIxPTR "  ???"
+                                                            : "%08" PRIxPTR "  ???",
+                         it->pc);
     } else {
-      os << StringPrintf("%08" PRIxPTR "  ", BacktraceMap::GetRelativePc(it->map, it->pc));
+      os << StringPrintf(Is64BitInstructionSet(kRuntimeISA) ? "%016" PRIxPTR "  "
+                                                            : "%08" PRIxPTR "  ",
+                         BacktraceMap::GetRelativePc(it->map, it->pc));
       os << it->map.name;
       os << " (";
       if (!it->func_name.empty()) {
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 9319cc2..7fe8bb9 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -302,7 +302,9 @@
 PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::string& descriptor,
                                            uint16_t cache_id)
     : RegType(klass, descriptor, cache_id) {
-  DCHECK(klass->IsInstantiable());
+  // Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError
+  //       would be thrown at runtime, but we need to continue verification and *not* create a
+  //       hard failure or abort.
 }
 
 std::string UnresolvedMergedType::Dump() const {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index d656500..4469e64 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -427,9 +427,18 @@
         }
       }
       entry = new ReferenceType(klass, "", entries_.size());
-    } else if (klass->IsInstantiable()) {
+    } else if (!klass->IsPrimitive()) {
       // We're uninitialized because of allocation, look or create a precise type as allocations
       // may only create objects of that type.
+      // Note: we do not check whether the given klass is actually instantiable (besides being
+      //       primitive), that is, we allow interfaces and abstract classes here. The reasoning is
+      //       twofold:
+      //       1) The "new-instance" instruction to generate the uninitialized type will already
+      //          queue an instantiation error. This is a soft error that must be thrown at runtime,
+      //          and could potentially change if the class is resolved differently at runtime.
+      //       2) Checking whether the klass is instantiable and using conflict may produce a hard
+      //          error when the value is used, which leads to a VerifyError, which is not the
+      //          correct semantics.
       for (size_t i = primitive_count_; i < entries_.size(); i++) {
         const RegType* cur_entry = entries_[i];
         if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) {
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 1391d14..c984b17 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #include <android/log.h>
 #else
 #include <stdarg.h>
@@ -103,7 +103,7 @@
   va_list ap;
   va_start(ap, format);
   vsnprintf(buf, sizeof(buf), format, ap);
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
 #else
   std::cout << buf << "\n";
@@ -337,14 +337,16 @@
   // In case the chain isn't claimed, claim it for ourself so we can ensure the managed handler
   // goes first.
   if (!user_sigactions[signal].IsClaimed()) {
-    struct sigaction tmp;
-    tmp.sa_sigaction = sigchainlib_managed_handler_sigaction;
-    sigemptyset(&tmp.sa_mask);
-    tmp.sa_flags = SA_SIGINFO | SA_ONSTACK;
+    struct sigaction act, old_act;
+    act.sa_sigaction = sigchainlib_managed_handler_sigaction;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = SA_SIGINFO | SA_ONSTACK;
 #if !defined(__APPLE__) && !defined(__mips__)
-    tmp.sa_restorer = nullptr;
+    act.sa_restorer = nullptr;
 #endif
-    user_sigactions[signal].Claim(tmp);
+    if (sigaction(signal, &act, &old_act) != -1) {
+      user_sigactions[signal].Claim(old_act);
+    }
   }
 }
 
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
index 8495a54..dfe0c6f 100644
--- a/sigchainlib/sigchain_dummy.cc
+++ b/sigchainlib/sigchain_dummy.cc
@@ -17,7 +17,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
 #include <android/log.h>
 #else
 #include <stdarg.h>
@@ -38,7 +38,7 @@
   va_list ap;
   va_start(ap, format);
   vsnprintf(buf, sizeof(buf), format, ap);
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
   __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
 #else
   std::cout << buf << "\n";
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index fa053fb..d657d44 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -24,7 +24,7 @@
 SuperTarget constructor ()V
 Target constructor ()V
 Before, float is 3.1415925
-myMethod: hi there 3.1415925 Q !
+myMethod: hi there 3.1415925 ✔ !
 Result of invoke: 7
 Calling no-arg void-return method
 myNoargMethod ()V
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index 0d8e576..0c90109 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -147,7 +147,7 @@
             Object[] argList = new Object[] {
                 new String[] { "hi there" },
                 new Float(3.1415926f),
-                new Character('Q')
+                new Character('\u2714')
             };
             System.out.println("Before, float is "
                 + ((Float)argList[1]).floatValue());
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
index 2b8e675..4215207 100644
--- a/test/051-thread/thread_test.cc
+++ b/test/051-thread/thread_test.cc
@@ -28,7 +28,7 @@
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(
     JNIEnv* env ATTRIBUTE_UNUSED,
     jclass clazz ATTRIBUTE_UNUSED) {
-#if defined(HAVE_ANDROID_OS)
+#if defined(__ANDROID__)
   return JNI_TRUE;
 #else
   return JNI_FALSE;
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 7db61a1..c932761 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -1,6 +1,6 @@
 true
 8
-x
+✔
 3.141592653589793
 3.14
 32
diff --git a/test/100-reflect2/src/Main.java b/test/100-reflect2/src/Main.java
index 72e14b1..bf3a574 100644
--- a/test/100-reflect2/src/Main.java
+++ b/test/100-reflect2/src/Main.java
@@ -20,7 +20,7 @@
 class Main {
   private static boolean z = true;
   private static byte b = 8;
-  private static char c = 'x';
+  private static char c = '\u2714';
   private static double d = Math.PI;
   private static float f = 3.14f;
   private static int i = 32;
@@ -144,7 +144,7 @@
     /*
     private static boolean z = true;
     private static byte b = 8;
-    private static char c = 'x';
+    private static char c = '\u2714';
     private static double d = Math.PI;
     private static float f = 3.14f;
     private static int i = 32;
@@ -263,7 +263,7 @@
     show(ctor.newInstance((Object[]) null));
 
     ctor = String.class.getConstructor(char[].class, int.class, int.class);
-    show(ctor.newInstance(new char[] { 'x', 'y', 'z', '!' }, 1, 2));
+    show(ctor.newInstance(new char[] { '\u2714', 'y', 'z', '!' }, 1, 2));
   }
 
   private static void testPackagePrivateConstructor() {
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index 464d2c8..372ecd0 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -61,3 +61,4 @@
 trampoline_Java_Main_testNewStringObject called!
 Getting trampoline for Java_Main_testSignal with shorty I.
 NB signal handler with signal 11.
+NB signal handler with signal 4.
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index c8141a7..a6a6e08 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -200,8 +200,9 @@
 #if !defined(__APPLE__) && !defined(__mips__)
   tmp.sa_restorer = nullptr;
 #endif
-  sigaction(SIGSEGV, &tmp, nullptr);
 
+  // Test segv
+  sigaction(SIGSEGV, &tmp, nullptr);
 #if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
   // On supported architectures we cause a real SEGV.
   *go_away_compiler = 'a';
@@ -209,6 +210,11 @@
   // On other architectures we simulate SEGV.
   kill(getpid(), SIGSEGV);
 #endif
+
+  // Test sigill
+  sigaction(SIGILL, &tmp, nullptr);
+  kill(getpid(), SIGILL);
+
   return 1234;
 }
 
@@ -385,27 +391,29 @@
 // 004-SignalTest.
 static bool nb_signalhandler(int sig, siginfo_t* info ATTRIBUTE_UNUSED, void* context) {
   printf("NB signal handler with signal %d.\n", sig);
+  if (sig == SIGSEGV) {
 #if defined(__arm__)
-  struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
-  struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
-  sc->arm_pc += 2;          // Skip instruction causing segv.
+    struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+    struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+    sc->arm_pc += 2;          // Skip instruction causing segv & sigill.
 #elif defined(__aarch64__)
-  struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
-  struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
-  sc->pc += 4;          // Skip instruction causing segv.
+    struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+    struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+    sc->pc += 4;          // Skip instruction causing segv & sigill.
 #elif defined(__i386__) || defined(__x86_64__)
-  struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
-  uc->CTX_EIP += 3;
+    struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+    uc->CTX_EIP += 3;
 #else
-  UNUSED(context);
+    UNUSED(context);
 #endif
+  }
   // We handled this...
   return true;
 }
 
 static ::android::NativeBridgeSignalHandlerFn native_bridge_get_signal_handler(int signal) {
-  // Only test segfault handler.
-  if (signal == SIGSEGV) {
+  // Test segv for already claimed signal, and sigill for not claimed signal
+  if ((signal == SIGSEGV) || (signal == SIGILL)) {
     return &nb_signalhandler;
   }
   return nullptr;
diff --git a/test/411-optimizing-arith/expected.txt b/test/411-optimizing-arith-mul/expected.txt
similarity index 100%
rename from test/411-optimizing-arith/expected.txt
rename to test/411-optimizing-arith-mul/expected.txt
diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith-mul/info.txt
similarity index 100%
rename from test/411-optimizing-arith/info.txt
rename to test/411-optimizing-arith-mul/info.txt
diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith-mul/src/Main.java
similarity index 100%
rename from test/411-optimizing-arith/src/Main.java
rename to test/411-optimizing-arith-mul/src/Main.java
diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java
index 3899d7f..c108a90 100644
--- a/test/441-checker-inliner/src/Main.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
@@ -157,6 +157,31 @@
     return x;
   }
 
+  /// CHECK-START: int Main.returnAbs(int) intrinsics_recognition (before)
+  /// CHECK-DAG:     <<Result:i\d+>>      InvokeStaticOrDirect
+  /// CHECK-DAG:                          Return [<<Result>>]
+
+  /// CHECK-START: int Main.returnAbs(int) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Result:i\d+>>      InvokeStaticOrDirect intrinsic:MathAbsInt
+  /// CHECK-DAG:                          Return [<<Result>>]
+
+  private static int returnAbs(int i) {
+    return Math.abs(i);
+  }
+
+  /// CHECK-START: int Main.InlinedIntrinsicsAreStillIntrinsic() inliner (before)
+  /// CHECK-DAG:     <<ConstMinus1:i\d+>> IntConstant -1
+  /// CHECK-DAG:     <<Result:i\d+>>      InvokeStaticOrDirect
+  /// CHECK-DAG:                          Return [<<Result>>]
+
+  /// CHECK-START: int Main.InlinedIntrinsicsAreStillIntrinsic() inliner (after)
+  /// CHECK-DAG:     <<ConstMinus1:i\d+>> IntConstant -1
+  /// CHECK-DAG:     <<Result:i\d+>>      InvokeStaticOrDirect intrinsic:MathAbsInt
+  /// CHECK-DAG:                          Return [<<Result>>]
+
+  public static int InlinedIntrinsicsAreStillIntrinsic() {
+    return returnAbs(-1);
+  }
 
   private static void returnVoid() {
     return;
@@ -238,5 +263,13 @@
     if (InlineWithControlFlow(false) != 2) {
       throw new Error();
     }
+
+    if (InlinedIntrinsicsAreStillIntrinsic() != 1) {
+      throw new Error();
+    }
+
+    if (returnAbs(-1) != 1) {
+      throw new Error();
+    }
   }
 }
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index b7863be..20dac42 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
@@ -46,9 +46,9 @@
     }
   }
 
+
   /**
-   * Tiny three-register program exercising int constant folding
-   * on negation.
+   * Exercise constant folding on negation.
    */
 
   /// CHECK-START: int Main.IntNegation() constant_folding (before)
@@ -60,6 +60,9 @@
   /// CHECK-DAG:     <<ConstN42:i\d+>> IntConstant -42
   /// CHECK-DAG:                       Return [<<ConstN42>>]
 
+  /// CHECK-START: int Main.IntNegation() constant_folding (after)
+  /// CHECK-NOT:                       Neg
+
   public static int IntNegation() {
     int x, y;
     x = 42;
@@ -67,9 +70,9 @@
     return y;
   }
 
+
   /**
-   * Tiny three-register program exercising int constant folding
-   * on addition.
+   * Exercise constant folding on addition.
    */
 
   /// CHECK-START: int Main.IntAddition1() constant_folding (before)
@@ -82,6 +85,9 @@
   /// CHECK-DAG:     <<Const3:i\d+>>  IntConstant 3
   /// CHECK-DAG:                      Return [<<Const3>>]
 
+  /// CHECK-START: int Main.IntAddition1() constant_folding (after)
+  /// CHECK-NOT:                      Add
+
   public static int IntAddition1() {
     int a, b, c;
     a = 1;
@@ -90,11 +96,6 @@
     return c;
   }
 
- /**
-  * Small three-register program exercising int constant folding
-  * on addition.
-  */
-
   /// CHECK-START: int Main.IntAddition2() constant_folding (before)
   /// CHECK-DAG:     <<Const1:i\d+>>  IntConstant 1
   /// CHECK-DAG:     <<Const2:i\d+>>  IntConstant 2
@@ -109,6 +110,9 @@
   /// CHECK-DAG:     <<Const14:i\d+>> IntConstant 14
   /// CHECK-DAG:                      Return [<<Const14>>]
 
+  /// CHECK-START: int Main.IntAddition2() constant_folding (after)
+  /// CHECK-NOT:                      Add
+
   public static int IntAddition2() {
     int a, b, c;
     a = 1;
@@ -121,9 +125,30 @@
     return c;
   }
 
+  /// CHECK-START: long Main.LongAddition() constant_folding (before)
+  /// CHECK-DAG:     <<Const1:j\d+>>  LongConstant 1
+  /// CHECK-DAG:     <<Const2:j\d+>>  LongConstant 2
+  /// CHECK-DAG:     <<Add:j\d+>>     Add [<<Const1>>,<<Const2>>]
+  /// CHECK-DAG:                      Return [<<Add>>]
+
+  /// CHECK-START: long Main.LongAddition() constant_folding (after)
+  /// CHECK-DAG:     <<Const3:j\d+>>  LongConstant 3
+  /// CHECK-DAG:                      Return [<<Const3>>]
+
+  /// CHECK-START: long Main.LongAddition() constant_folding (after)
+  /// CHECK-NOT:                      Add
+
+  public static long LongAddition() {
+    long a, b, c;
+    a = 1L;
+    b = 2L;
+    c = a + b;
+    return c;
+  }
+
+
   /**
-   * Tiny three-register program exercising int constant folding
-   * on subtraction.
+   * Exercise constant folding on subtraction.
    */
 
   /// CHECK-START: int Main.IntSubtraction() constant_folding (before)
@@ -136,6 +161,9 @@
   /// CHECK-DAG:     <<Const4:i\d+>>  IntConstant 4
   /// CHECK-DAG:                      Return [<<Const4>>]
 
+  /// CHECK-START: int Main.IntSubtraction() constant_folding (after)
+  /// CHECK-NOT:                      Sub
+
   public static int IntSubtraction() {
     int a, b, c;
     a = 6;
@@ -144,34 +172,6 @@
     return c;
   }
 
-  /**
-   * Tiny three-register program exercising long constant folding
-   * on addition.
-   */
-
-  /// CHECK-START: long Main.LongAddition() constant_folding (before)
-  /// CHECK-DAG:     <<Const1:j\d+>>  LongConstant 1
-  /// CHECK-DAG:     <<Const2:j\d+>>  LongConstant 2
-  /// CHECK-DAG:     <<Add:j\d+>>     Add [<<Const1>>,<<Const2>>]
-  /// CHECK-DAG:                      Return [<<Add>>]
-
-  /// CHECK-START: long Main.LongAddition() constant_folding (after)
-  /// CHECK-DAG:     <<Const3:j\d+>>  LongConstant 3
-  /// CHECK-DAG:                      Return [<<Const3>>]
-
-  public static long LongAddition() {
-    long a, b, c;
-    a = 1L;
-    b = 2L;
-    c = a + b;
-    return c;
-  }
-
-  /**
-   * Tiny three-register program exercising long constant folding
-   * on subtraction.
-   */
-
   /// CHECK-START: long Main.LongSubtraction() constant_folding (before)
   /// CHECK-DAG:     <<Const6:j\d+>>  LongConstant 6
   /// CHECK-DAG:     <<Const2:j\d+>>  LongConstant 2
@@ -182,6 +182,9 @@
   /// CHECK-DAG:     <<Const4:j\d+>>  LongConstant 4
   /// CHECK-DAG:                      Return [<<Const4>>]
 
+  /// CHECK-START: long Main.LongSubtraction() constant_folding (after)
+  /// CHECK-NOT:                      Sub
+
   public static long LongSubtraction() {
     long a, b, c;
     a = 6L;
@@ -190,8 +193,158 @@
     return c;
   }
 
+
   /**
-   * Three-register program with a constant (static) condition.
+   * Exercise constant folding on multiplication.
+   */
+
+  /// CHECK-START: int Main.IntMultiplication() constant_folding (before)
+  /// CHECK-DAG:     <<Const7:i\d+>>  IntConstant 7
+  /// CHECK-DAG:     <<Const3:i\d+>>  IntConstant 3
+  /// CHECK-DAG:     <<Mul:i\d+>>     Mul [<<Const7>>,<<Const3>>]
+  /// CHECK-DAG:                      Return [<<Mul>>]
+
+  /// CHECK-START: int Main.IntMultiplication() constant_folding (after)
+  /// CHECK-DAG:     <<Const21:i\d+>> IntConstant 21
+  /// CHECK-DAG:                      Return [<<Const21>>]
+
+  /// CHECK-START: int Main.IntMultiplication() constant_folding (after)
+  /// CHECK-NOT:                      Mul
+
+  public static int IntMultiplication() {
+    int a, b, c;
+    a = 7;
+    b = 3;
+    c = a * b;
+    return c;
+  }
+
+  /// CHECK-START: long Main.LongMultiplication() constant_folding (before)
+  /// CHECK-DAG:     <<Const7:j\d+>>  LongConstant 7
+  /// CHECK-DAG:     <<Const3:j\d+>>  LongConstant 3
+  /// CHECK-DAG:     <<Mul:j\d+>>     Mul [<<Const7>>,<<Const3>>]
+  /// CHECK-DAG:                      Return [<<Mul>>]
+
+  /// CHECK-START: long Main.LongMultiplication() constant_folding (after)
+  /// CHECK-DAG:     <<Const21:j\d+>> LongConstant 21
+  /// CHECK-DAG:                      Return [<<Const21>>]
+
+  /// CHECK-START: long Main.LongMultiplication() constant_folding (after)
+  /// CHECK-NOT:                      Mul
+
+  public static long LongMultiplication() {
+    long a, b, c;
+    a = 7L;
+    b = 3L;
+    c = a * b;
+    return c;
+  }
+
+
+  /**
+   * Exercise constant folding on division.
+   */
+
+  /// CHECK-START: int Main.IntDivision() constant_folding (before)
+  /// CHECK-DAG:     <<Const8:i\d+>>   IntConstant 8
+  /// CHECK-DAG:     <<Const3:i\d+>>   IntConstant 3
+  /// CHECK-DAG:     <<Div0Chk:i\d+>>  DivZeroCheck [<<Const3>>]
+  /// CHECK-DAG:     <<Div:i\d+>>      Div [<<Const8>>,<<Div0Chk>>]
+  /// CHECK-DAG:                       Return [<<Div>>]
+
+  /// CHECK-START: int Main.IntDivision() constant_folding (after)
+  /// CHECK-DAG:     <<Const2:i\d+>>   IntConstant 2
+  /// CHECK-DAG:                       Return [<<Const2>>]
+
+  /// CHECK-START: int Main.IntDivision() constant_folding (after)
+  /// CHECK-NOT:                       DivZeroCheck
+  /// CHECK-NOT:                       Div
+
+  public static int IntDivision() {
+    int a, b, c;
+    a = 8;
+    b = 3;
+    c = a / b;
+    return c;
+  }
+
+  /// CHECK-START: long Main.LongDivision() constant_folding (before)
+  /// CHECK-DAG:     <<Const8:j\d+>>   LongConstant 8
+  /// CHECK-DAG:     <<Const3:j\d+>>   LongConstant 3
+  /// CHECK-DAG:     <<Div0Chk:j\d+>>  DivZeroCheck [<<Const3>>]
+  /// CHECK-DAG:     <<Div:j\d+>>      Div [<<Const8>>,<<Div0Chk>>]
+  /// CHECK-DAG:                       Return [<<Div>>]
+
+  /// CHECK-START: long Main.LongDivision() constant_folding (after)
+  /// CHECK-DAG:     <<Const2:j\d+>>   LongConstant 2
+  /// CHECK-DAG:                       Return [<<Const2>>]
+
+  /// CHECK-START: long Main.LongDivision() constant_folding (after)
+  /// CHECK-NOT:                       DivZeroCheck
+  /// CHECK-NOT:                       Div
+
+  public static long LongDivision() {
+    long a, b, c;
+    a = 8L;
+    b = 3L;
+    c = a / b;
+    return c;
+  }
+
+
+  /**
+   * Exercise constant folding on remainder.
+   */
+
+  /// CHECK-START: int Main.IntRemainder() constant_folding (before)
+  /// CHECK-DAG:     <<Const8:i\d+>>   IntConstant 8
+  /// CHECK-DAG:     <<Const3:i\d+>>   IntConstant 3
+  /// CHECK-DAG:     <<Div0Chk:i\d+>>  DivZeroCheck [<<Const3>>]
+  /// CHECK-DAG:     <<Rem:i\d+>>      Rem [<<Const8>>,<<Div0Chk>>]
+  /// CHECK-DAG:                       Return [<<Rem>>]
+
+  /// CHECK-START: int Main.IntRemainder() constant_folding (after)
+  /// CHECK-DAG:     <<Const2:i\d+>>   IntConstant 2
+  /// CHECK-DAG:                       Return [<<Const2>>]
+
+  /// CHECK-START: int Main.IntRemainder() constant_folding (after)
+  /// CHECK-NOT:                       DivZeroCheck
+  /// CHECK-NOT:                       Rem
+
+  public static int IntRemainder() {
+    int a, b, c;
+    a = 8;
+    b = 3;
+    c = a % b;
+    return c;
+  }
+
+  /// CHECK-START: long Main.LongRemainder() constant_folding (before)
+  /// CHECK-DAG:     <<Const8:j\d+>>   LongConstant 8
+  /// CHECK-DAG:     <<Const3:j\d+>>   LongConstant 3
+  /// CHECK-DAG:     <<Div0Chk:j\d+>>  DivZeroCheck [<<Const3>>]
+  /// CHECK-DAG:     <<Rem:j\d+>>      Rem [<<Const8>>,<<Div0Chk>>]
+  /// CHECK-DAG:                       Return [<<Rem>>]
+
+  /// CHECK-START: long Main.LongRemainder() constant_folding (after)
+  /// CHECK-DAG:     <<Const2:j\d+>>   LongConstant 2
+  /// CHECK-DAG:                       Return [<<Const2>>]
+
+  /// CHECK-START: long Main.LongRemainder() constant_folding (after)
+  /// CHECK-NOT:                       DivZeroCheck
+  /// CHECK-NOT:                       Rem
+
+  public static long LongRemainder() {
+    long a, b, c;
+    a = 8L;
+    b = 3L;
+    c = a % b;
+    return c;
+  }
+
+
+  /**
+   * Exercise constant folding on constant (static) condition.
    */
 
   /// CHECK-START: int Main.StaticCondition() constant_folding (before)
@@ -204,6 +357,9 @@
   /// CHECK-DAG:     <<Const1:i\d+>>  IntConstant 1
   /// CHECK-DAG:                      If [<<Const1>>]
 
+  /// CHECK-START: int Main.StaticCondition() constant_folding (after)
+  /// CHECK-NOT:                      GreaterThanOrEqual
+
   public static int StaticCondition() {
     int a, b, c;
     a = 7;
@@ -215,9 +371,10 @@
     return c;
   }
 
+
   /**
-   * Four-variable program with jumps leading to the creation of many
-   * blocks.
+   * Exercise constant folding on a program with condition
+   * (i.e. jumps) leading to the creation of many blocks.
    *
    * The intent of this test is to ensure that all constant expressions
    * are actually evaluated at compile-time, thanks to the reverse
@@ -238,6 +395,10 @@
   /// CHECK-DAG:     <<Phi:i\d+>>     Phi [<<Const7>>,<<Const3>>]
   /// CHECK-DAG:                      Return [<<Phi>>]
 
+  /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after)
+  /// CHECK-NOT:                      Add
+  /// CHECK-NOT:                      Sub
+
   public static int JumpsAndConditionals(boolean cond) {
     int a, b, c;
     a = 5;
@@ -249,6 +410,7 @@
     return c;
   }
 
+
   /**
    * Test optimizations of arithmetic identities yielding a constant result.
    */
@@ -262,9 +424,11 @@
   /// CHECK-START: int Main.And0(int) constant_folding (after)
   /// CHECK-DAG:     <<Arg:i\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
-  /// CHECK-NOT:                       And
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: int Main.And0(int) constant_folding (after)
+  /// CHECK-NOT:                       And
+
   public static int And0(int arg) {
     return arg & 0;
   }
@@ -278,9 +442,11 @@
   /// CHECK-START: long Main.Mul0(long) constant_folding (after)
   /// CHECK-DAG:     <<Arg:j\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:j\d+>>   LongConstant 0
-  /// CHECK-NOT:                       Mul
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: long Main.Mul0(long) constant_folding (after)
+  /// CHECK-NOT:                       Mul
+
   public static long Mul0(long arg) {
     return arg * 0;
   }
@@ -293,9 +459,11 @@
 
   /// CHECK-START: int Main.OrAllOnes(int) constant_folding (after)
   /// CHECK-DAG:     <<ConstF:i\d+>>   IntConstant -1
-  /// CHECK-NOT:                       Or
   /// CHECK-DAG:                       Return [<<ConstF>>]
 
+  /// CHECK-START: int Main.OrAllOnes(int) constant_folding (after)
+  /// CHECK-NOT:                       Or
+
   public static int OrAllOnes(int arg) {
     return arg | -1;
   }
@@ -309,9 +477,11 @@
 
   /// CHECK-START: long Main.Rem0(long) constant_folding (after)
   /// CHECK-DAG:     <<Const0:j\d+>>        LongConstant 0
-  /// CHECK-NOT:                            Rem
   /// CHECK-DAG:                            Return [<<Const0>>]
 
+  /// CHECK-START: long Main.Rem0(long) constant_folding (after)
+  /// CHECK-NOT:                            Rem
+
   public static long Rem0(long arg) {
     return 0 % arg;
   }
@@ -324,9 +494,11 @@
 
   /// CHECK-START: int Main.Rem1(int) constant_folding (after)
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
-  /// CHECK-NOT:                       Rem
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: int Main.Rem1(int) constant_folding (after)
+  /// CHECK-NOT:                       Rem
+
   public static int Rem1(int arg) {
     return arg % 1;
   }
@@ -340,9 +512,11 @@
 
   /// CHECK-START: long Main.RemN1(long) constant_folding (after)
   /// CHECK-DAG:     <<Const0:j\d+>>        LongConstant 0
-  /// CHECK-NOT:                            Rem
   /// CHECK-DAG:                            Return [<<Const0>>]
 
+  /// CHECK-START: long Main.RemN1(long) constant_folding (after)
+  /// CHECK-NOT:                            Rem
+
   public static long RemN1(long arg) {
     return arg % -1;
   }
@@ -356,9 +530,11 @@
   /// CHECK-START: int Main.Shl0(int) constant_folding (after)
   /// CHECK-DAG:     <<Arg:i\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
-  /// CHECK-NOT:                       Shl
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: int Main.Shl0(int) constant_folding (after)
+  /// CHECK-NOT:                       Shl
+
   public static int Shl0(int arg) {
     return 0 << arg;
   }
@@ -372,9 +548,11 @@
   /// CHECK-START: long Main.Shr0(int) constant_folding (after)
   /// CHECK-DAG:     <<Arg:i\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:j\d+>>   LongConstant 0
-  /// CHECK-NOT:                       Shr
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: long Main.Shr0(int) constant_folding (after)
+  /// CHECK-NOT:                       Shr
+
   public static long Shr0(int arg) {
     return (long)0 >> arg;
   }
@@ -387,9 +565,11 @@
   /// CHECK-START: long Main.SubSameLong(long) constant_folding (after)
   /// CHECK-DAG:     <<Arg:j\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:j\d+>>   LongConstant 0
-  /// CHECK-NOT:                       Sub
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: long Main.SubSameLong(long) constant_folding (after)
+  /// CHECK-NOT:                       Sub
+
   public static long SubSameLong(long arg) {
     return arg - arg;
   }
@@ -403,9 +583,11 @@
   /// CHECK-START: int Main.UShr0(int) constant_folding (after)
   /// CHECK-DAG:     <<Arg:i\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
-  /// CHECK-NOT:                       UShr
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: int Main.UShr0(int) constant_folding (after)
+  /// CHECK-NOT:                       UShr
+
   public static int UShr0(int arg) {
     return 0 >>> arg;
   }
@@ -418,9 +600,11 @@
   /// CHECK-START: int Main.XorSameInt(int) constant_folding (after)
   /// CHECK-DAG:     <<Arg:i\d+>>      ParameterValue
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
-  /// CHECK-NOT:                       Xor
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: int Main.XorSameInt(int) constant_folding (after)
+  /// CHECK-NOT:                       Xor
+
   public static int XorSameInt(int arg) {
     return arg ^ arg;
   }
@@ -473,6 +657,11 @@
     return arg < Double.NaN;
   }
 
+
+  /**
+   * Exercise constant folding on type conversions.
+   */
+
   /// CHECK-START: int Main.ReturnInt33() constant_folding (before)
   /// CHECK-DAG:     <<Const33:j\d+>>  LongConstant 33
   /// CHECK-DAG:     <<Convert:i\d+>>  TypeConversion [<<Const33>>]
@@ -482,6 +671,9 @@
   /// CHECK-DAG:     <<Const33:i\d+>>  IntConstant 33
   /// CHECK-DAG:                       Return [<<Const33>>]
 
+  /// CHECK-START: int Main.ReturnInt33() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static int ReturnInt33() {
     long imm = 33L;
     return (int) imm;
@@ -496,6 +688,9 @@
   /// CHECK-DAG:     <<ConstMax:i\d+>> IntConstant 2147483647
   /// CHECK-DAG:                       Return [<<ConstMax>>]
 
+  /// CHECK-START: int Main.ReturnIntMax() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static int ReturnIntMax() {
     float imm = 1.0e34f;
     return (int) imm;
@@ -510,6 +705,9 @@
   /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: int Main.ReturnInt0() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static int ReturnInt0() {
     double imm = Double.NaN;
     return (int) imm;
@@ -524,6 +722,9 @@
   /// CHECK-DAG:     <<Const33:j\d+>>  LongConstant 33
   /// CHECK-DAG:                       Return [<<Const33>>]
 
+  /// CHECK-START: long Main.ReturnLong33() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static long ReturnLong33() {
     int imm = 33;
     return (long) imm;
@@ -538,6 +739,9 @@
   /// CHECK-DAG:     <<Const34:j\d+>>  LongConstant 34
   /// CHECK-DAG:                       Return [<<Const34>>]
 
+  /// CHECK-START: long Main.ReturnLong34() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static long ReturnLong34() {
     float imm = 34.0f;
     return (long) imm;
@@ -552,6 +756,9 @@
   /// CHECK-DAG:     <<Const0:j\d+>>   LongConstant 0
   /// CHECK-DAG:                       Return [<<Const0>>]
 
+  /// CHECK-START: long Main.ReturnLong0() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static long ReturnLong0() {
     double imm = -Double.NaN;
     return (long) imm;
@@ -566,6 +773,9 @@
   /// CHECK-DAG:     <<Const33:f\d+>>  FloatConstant 33
   /// CHECK-DAG:                       Return [<<Const33>>]
 
+  /// CHECK-START: float Main.ReturnFloat33() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static float ReturnFloat33() {
     int imm = 33;
     return (float) imm;
@@ -580,6 +790,9 @@
   /// CHECK-DAG:     <<Const34:f\d+>>  FloatConstant 34
   /// CHECK-DAG:                       Return [<<Const34>>]
 
+  /// CHECK-START: float Main.ReturnFloat34() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static float ReturnFloat34() {
     long imm = 34L;
     return (float) imm;
@@ -594,6 +807,9 @@
   /// CHECK-DAG:     <<Const:f\d+>>    FloatConstant 99.25
   /// CHECK-DAG:                       Return [<<Const>>]
 
+  /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static float ReturnFloat99P25() {
     double imm = 99.25;
     return (float) imm;
@@ -622,6 +838,9 @@
   /// CHECK-DAG:     <<Const34:d\d+>>  DoubleConstant 34
   /// CHECK-DAG:                       Return [<<Const34>>]
 
+  /// CHECK-START: double Main.ReturnDouble34() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static double ReturnDouble34() {
     long imm = 34L;
     return (double) imm;
@@ -636,46 +855,70 @@
   /// CHECK-DAG:     <<Const:d\d+>>    DoubleConstant 99.25
   /// CHECK-DAG:                       Return [<<Const>>]
 
+  /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after)
+  /// CHECK-NOT:                       TypeConversion
+
   public static double ReturnDouble99P25() {
     float imm = 99.25f;
     return (double) imm;
   }
 
+
   public static void main(String[] args) {
-    assertIntEquals(IntNegation(), -42);
-    assertIntEquals(IntAddition1(), 3);
-    assertIntEquals(IntAddition2(), 14);
-    assertIntEquals(IntSubtraction(), 4);
-    assertLongEquals(LongAddition(), 3L);
-    assertLongEquals(LongSubtraction(), 4L);
-    assertIntEquals(StaticCondition(), 5);
-    assertIntEquals(JumpsAndConditionals(true), 7);
-    assertIntEquals(JumpsAndConditionals(false), 3);
+    assertIntEquals(-42, IntNegation());
+
+    assertIntEquals(3, IntAddition1());
+    assertIntEquals(14, IntAddition2());
+    assertLongEquals(3L, LongAddition());
+
+    assertIntEquals(4, IntSubtraction());
+    assertLongEquals(4L, LongSubtraction());
+
+    assertIntEquals(21, IntMultiplication());
+    assertLongEquals(21L, LongMultiplication());
+
+    assertIntEquals(2, IntDivision());
+    assertLongEquals(2L, LongDivision());
+
+    assertIntEquals(2, IntRemainder());
+    assertLongEquals(2L, LongRemainder());
+
+    assertIntEquals(5, StaticCondition());
+
+    assertIntEquals(7, JumpsAndConditionals(true));
+    assertIntEquals(3, JumpsAndConditionals(false));
+
     int arbitrary = 123456;  // Value chosen arbitrarily.
-    assertIntEquals(And0(arbitrary), 0);
-    assertLongEquals(Mul0(arbitrary), 0);
-    assertIntEquals(OrAllOnes(arbitrary), -1);
-    assertLongEquals(Rem0(arbitrary), 0);
-    assertIntEquals(Rem1(arbitrary), 0);
-    assertLongEquals(RemN1(arbitrary), 0);
-    assertIntEquals(Shl0(arbitrary), 0);
-    assertLongEquals(Shr0(arbitrary), 0);
-    assertLongEquals(SubSameLong(arbitrary), 0);
-    assertIntEquals(UShr0(arbitrary), 0);
-    assertIntEquals(XorSameInt(arbitrary), 0);
+
+    assertIntEquals(0, And0(arbitrary));
+    assertLongEquals(0, Mul0(arbitrary));
+    assertIntEquals(-1, OrAllOnes(arbitrary));
+    assertLongEquals(0, Rem0(arbitrary));
+    assertIntEquals(0, Rem1(arbitrary));
+    assertLongEquals(0, RemN1(arbitrary));
+    assertIntEquals(0, Shl0(arbitrary));
+    assertLongEquals(0, Shr0(arbitrary));
+    assertLongEquals(0, SubSameLong(arbitrary));
+    assertIntEquals(0, UShr0(arbitrary));
+    assertIntEquals(0, XorSameInt(arbitrary));
+
     assertFalse(CmpFloatGreaterThanNaN(arbitrary));
     assertFalse(CmpDoubleLessThanNaN(arbitrary));
-    assertIntEquals(ReturnInt33(), 33);
-    assertIntEquals(ReturnIntMax(), 2147483647);
-    assertIntEquals(ReturnInt0(), 0);
-    assertLongEquals(ReturnLong33(), 33);
-    assertLongEquals(ReturnLong34(), 34);
-    assertLongEquals(ReturnLong0(), 0);
-    assertFloatEquals(ReturnFloat33(), 33);
-    assertFloatEquals(ReturnFloat34(), 34);
-    assertFloatEquals(ReturnFloat99P25(), 99.25f);
-    assertDoubleEquals(ReturnDouble33(), 33);
-    assertDoubleEquals(ReturnDouble34(), 34);
-    assertDoubleEquals(ReturnDouble99P25(), 99.25);
+
+    assertIntEquals(33, ReturnInt33());
+    assertIntEquals(2147483647, ReturnIntMax());
+    assertIntEquals(0, ReturnInt0());
+
+    assertLongEquals(33, ReturnLong33());
+    assertLongEquals(34, ReturnLong34());
+    assertLongEquals(0, ReturnLong0());
+
+    assertFloatEquals(33, ReturnFloat33());
+    assertFloatEquals(34, ReturnFloat34());
+    assertFloatEquals(99.25f, ReturnFloat99P25());
+
+    assertDoubleEquals(33, ReturnDouble33());
+    assertDoubleEquals(34, ReturnDouble34());
+    assertDoubleEquals(99.25, ReturnDouble99P25());
   }
 }
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
index 42f9a11..6ee8a4d 100644
--- a/test/445-checker-licm/src/Main.java
+++ b/test/445-checker-licm/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java
index de00a09..e8168af 100644
--- a/test/446-checker-inliner2/src/Main.java
+++ b/test/446-checker-inliner2/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java
index e3fdffd..0b980d0 100644
--- a/test/447-checker-inliner3/src/Main.java
+++ b/test/447-checker-inliner3/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/449-checker-bce/expected.txt b/test/449-checker-bce/expected.txt
index e69de29..e114c50 100644
--- a/test/449-checker-bce/expected.txt
+++ b/test/449-checker-bce/expected.txt
@@ -0,0 +1 @@
+java.lang.ArrayIndexOutOfBoundsException: length=5; index=82
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index ed6fc1e..a746664 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
@@ -1101,6 +1101,28 @@
 
   }
 
+  public void testExceptionMessage() {
+    short[] B1 = new short[5];
+    int[] B2 = new int[5];
+    Exception err = null;
+    try {
+      testExceptionMessage1(B1, B2, null, -1, 6);
+    } catch (Exception e) {
+      err = e;
+    }
+    System.out.println(err);
+  }
+
+  void testExceptionMessage1(short[] a1, int[] a2, long a3[], int start, int finish) {
+    int j = finish + 77;
+    // Bug: 22665511
+    // A deoptimization will be triggered here right before the loop. Need to make
+    // sure the value of j is preserved for the interpreter.
+    for (int i = start; i <= finish; i++) {
+      a2[j - 1] = a1[i + 1];
+    }
+  }
+
   // Make sure this method is compiled with optimizing.
   /// CHECK-START: void Main.main(java.lang.String[]) register (after)
   /// CHECK: ParallelMove
@@ -1141,6 +1163,7 @@
     };
 
     testUnknownBounds();
+    new Main().testExceptionMessage();
   }
 
 }
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index 9070627..251a53e 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -1,19 +1,18 @@
 /*
-* 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.
-*/
-
+ * 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.
+ */
 
 interface Interface {
   void $noinline$f();
@@ -52,6 +51,15 @@
   }
 }
 
+class Generic<A> {
+  private A a = null;
+  public A get() {
+    return a;
+  }
+}
+
+final class Final {}
+
 public class Main {
 
   /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (before)
@@ -395,6 +403,104 @@
     ((SubclassA)a[0]).$noinline$g();
   }
 
+  private Generic<SubclassC> genericC = new Generic<SubclassC>();
+  private Generic<Final> genericFinal = new Generic<Final>();
+
+  private SubclassC get() {
+    return genericC.get();
+  }
+
+  private Final getFinal() {
+    return genericFinal.get();
+  }
+
+  /// CHECK-START: SubclassC Main.inlineGenerics() reference_type_propagation (after)
+  /// CHECK:      <<Invoke:l\d+>>    InvokeStaticOrDirect klass:SubclassC exact:false
+  /// CHECK-NEXT:                    Return [<<Invoke>>]
+
+  /// CHECK-START: SubclassC Main.inlineGenerics() reference_type_propagation_after_inlining (after)
+  /// CHECK:      <<BoundType:l\d+>> BoundType klass:SubclassC exact:false
+  /// CHECK:                         Return [<<BoundType>>]
+  private SubclassC inlineGenerics() {
+    SubclassC c = get();
+    return c;
+  }
+
+  /// CHECK-START: Final Main.inlineGenericsFinal() reference_type_propagation (after)
+  /// CHECK:      <<Invoke:l\d+>>    InvokeStaticOrDirect klass:Final exact:true
+  /// CHECK-NEXT:                    Return [<<Invoke>>]
+
+  /// CHECK-START: Final Main.inlineGenericsFinal() reference_type_propagation_after_inlining (after)
+  /// CHECK:      <<BoundType:l\d+>> BoundType klass:Final exact:true
+  /// CHECK:                         Return [<<BoundType>>]
+  private Final inlineGenericsFinal() {
+    Final f = getFinal();
+    return f;
+  }
+
+  /// CHECK-START: void Main.boundOnlyOnceIfNotNull(java.lang.Object) reference_type_propagation_after_inlining (after)
+  /// CHECK:      BoundType
+  /// CHECK-NOT:  BoundType
+  private void boundOnlyOnceIfNotNull(Object o) {
+    if (o != null) {
+      o.toString();
+    }
+  }
+
+  /// CHECK-START: void Main.boundOnlyOnceIfInstanceOf(java.lang.Object) reference_type_propagation_after_inlining (after)
+  /// CHECK:      BoundType
+  /// CHECK-NOT:  BoundType
+  private void boundOnlyOnceIfInstanceOf(Object o) {
+    if (o instanceof Main) {
+      o.toString();
+    }
+  }
+
+  /// CHECK-START: Final Main.boundOnlyOnceCheckCast(Generic) reference_type_propagation_after_inlining (after)
+  /// CHECK:      BoundType
+  /// CHECK-NOT:  BoundType
+  private Final boundOnlyOnceCheckCast(Generic<Final> o) {
+    Final f = o.get();
+    return f;
+  }
+
+  private Super getSuper() {
+    return new SubclassA();
+  }
+
+  /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) reference_type_propagation (after)
+  /// CHECK:      <<Phi:l\d+>> Phi klass:Super
+  /// CHECK:                   NullCheck [<<Phi>>] klass:Super
+
+  /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) reference_type_propagation_after_inlining (after)
+  /// CHECK:      <<Phi:l\d+>> Phi klass:SubclassA
+  /// CHECK:                   NullCheck [<<Phi>>] klass:SubclassA
+  private void updateNodesInTheSameBlockAsPhi(boolean cond) {
+    Super s = getSuper();
+    if (cond) {
+      s = new SubclassA();
+    }
+    s.$noinline$f();
+  }
+
+  /// CHECK-START: java.lang.String Main.checkcastPreserveNullCheck(java.lang.Object) reference_type_propagation_after_inlining (after)
+  /// CHECK:      <<This:l\d+>>     ParameterValue
+  /// CHECK:      <<Param:l\d+>>    ParameterValue
+  /// CHECK:      <<Clazz:l\d+>>    LoadClass
+  /// CHECK:                        CheckCast [<<Param>>,<<Clazz>>]
+  /// CHECK:                        BoundType [<<Param>>] can_be_null:true
+
+  /// CHECK-START: java.lang.String Main.checkcastPreserveNullCheck(java.lang.Object) instruction_simplifier_after_types (after)
+  /// CHECK:      <<This:l\d+>>     ParameterValue
+  /// CHECK:      <<Param:l\d+>>    ParameterValue
+  /// CHECK:      <<Clazz:l\d+>>    LoadClass
+  /// CHECK:                        CheckCast [<<Param>>,<<Clazz>>]
+  /// CHECK:      <<Bound:l\d+>>    BoundType [<<Param>>]
+  /// CHECK:                        NullCheck [<<Bound>>]
+  public String checkcastPreserveNullCheck(Object a) {
+    return ((SubclassA)a).toString();
+  }
+
   public static void main(String[] args) {
   }
 }
diff --git a/test/451-regression-add-float/src/Main.java b/test/451-regression-add-float/src/Main.java
index 0d4bf06..093c132 100644
--- a/test/451-regression-add-float/src/Main.java
+++ b/test/451-regression-add-float/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 
 public class Main {
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index aa4dda1..a14200e 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
index cee8e0f..171ade8 100644
--- a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
+++ b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class OtherDex {
   public static void emptyMethod() {
diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java
index 64979ca..1fe49a8 100644
--- a/test/462-checker-inlining-across-dex-files/src/Main.java
+++ b/test/462-checker-inlining-across-dex-files/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * 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.
+ */
 
 // Add a class that will be the first entry in the dex cache, to
 // avoid having the OtherDex and Main classes share the same cache index.
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index dd17e77..61510d8 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java
index 876496f..6dce96c 100644
--- a/test/464-checker-inline-sharpen-calls/src/Main.java
+++ b/test/464-checker-inline-sharpen-calls/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * 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 final class Main {
 
diff --git a/test/465-checker-clinit-gvn/src/Main.java b/test/465-checker-clinit-gvn/src/Main.java
index 704e9fe..9c77acc 100644
--- a/test/465-checker-clinit-gvn/src/Main.java
+++ b/test/465-checker-clinit-gvn/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * 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 OtherClass {
   static {
diff --git a/test/473-checker-inliner-constants/src/Main.java b/test/473-checker-inliner-constants/src/Main.java
index 85f6565..8638514 100644
--- a/test/473-checker-inliner-constants/src/Main.java
+++ b/test/473-checker-inliner-constants/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java
index 86d0f7c..a2b219d 100644
--- a/test/474-checker-boolean-input/src/Main.java
+++ b/test/474-checker-boolean-input/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/475-regression-inliner-ids/src/Main.java b/test/475-regression-inliner-ids/src/Main.java
index bf22062..423c3b5 100644
--- a/test/475-regression-inliner-ids/src/Main.java
+++ b/test/475-regression-inliner-ids/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 import java.lang.reflect.Method;
 
diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java
index e709ba0..41bec05 100644
--- a/test/476-checker-ctor-memory-barrier/src/Main.java
+++ b/test/476-checker-ctor-memory-barrier/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * 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.
+ */
 
 // TODO: Add more tests after we can inline functions with calls.
 
diff --git a/test/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java
index fe52e83..c873702 100644
--- a/test/477-checker-bound-type/src/Main.java
+++ b/test/477-checker-bound-type/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 
 public class Main {
diff --git a/test/478-checker-inliner-nested-loop/src/Main.java b/test/478-checker-inliner-nested-loop/src/Main.java
index aa02349..86c119f 100644
--- a/test/478-checker-inliner-nested-loop/src/Main.java
+++ b/test/478-checker-inliner-nested-loop/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 
 public class Main {
diff --git a/test/479-regression-implicit-null-check/src/Main.java b/test/479-regression-implicit-null-check/src/Main.java
index 6b6f2e4..005ba7f 100644
--- a/test/479-regression-implicit-null-check/src/Main.java
+++ b/test/479-regression-implicit-null-check/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 
 public class Main {
diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java
index 4cc1634..5adafaf 100644
--- a/test/480-checker-dead-blocks/src/Main.java
+++ b/test/480-checker-dead-blocks/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
 
diff --git a/test/481-regression-phi-cond/src/Main.java b/test/481-regression-phi-cond/src/Main.java
index bad9669..54982f7 100644
--- a/test/481-regression-phi-cond/src/Main.java
+++ b/test/481-regression-phi-cond/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 
 public class Main {
diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java
index 18125fa..2cfb04d 100644
--- a/test/482-checker-loop-back-edge-use/src/Main.java
+++ b/test/482-checker-loop-back-edge-use/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 
 public class Main {
diff --git a/test/484-checker-register-hints/src/Main.java b/test/484-checker-register-hints/src/Main.java
index 3715ca2..6e68f7c 100644
--- a/test/484-checker-register-hints/src/Main.java
+++ b/test/484-checker-register-hints/src/Main.java
@@ -16,6 +16,14 @@
 
 public class Main {
 
+  static class Foo {
+    int field0;
+    int field1;
+    int field2;
+    int field3;
+    int field4;
+  };
+
   /// CHECK-START: void Main.test1(boolean, int, int, int, int, int) register (after)
   /// CHECK:       name "B0"
   /// CHECK-NOT:     ParallelMove
@@ -25,7 +33,7 @@
   /// CHECK-NOT:     ParallelMove
   /// CHECK:       name "B3"
   /// CHECK-NOT:   end_block
-  /// CHECK:         ArraySet
+  /// CHECK:         InstanceFieldSet
   // We could check here that there is a parallel move, but it's only valid
   // for some architectures (for example x86), as other architectures may
   // not do move at all.
@@ -36,19 +44,19 @@
     int e = live1;
     int f = live2;
     int g = live3;
+    int j = live0;
     if (z) {
     } else {
       // Create enough live instructions to force spilling on x86.
       int h = live4;
       int i = live5;
-      array[2] = e + i + h;
-      array[3] = f + i + h;
-      array[4] = g + i + h;
-      array[0] = h;
-      array[1] = i + h;
-
+      foo.field2 = e + i + h;
+      foo.field3 = f + i + h;
+      foo.field4 = g + i + h;
+      foo.field0 = h;
+      foo.field1 = i + h;
     }
-    live1 = e + f + g;
+    live1 = e + f + g + j;
   }
 
   /// CHECK-START: void Main.test2(boolean, int, int, int, int, int) register (after)
@@ -60,7 +68,7 @@
   /// CHECK-NOT:     ParallelMove
   /// CHECK:       name "B3"
   /// CHECK-NOT:   end_block
-  /// CHECK:         ArraySet
+  /// CHECK:         InstanceFieldSet
   // We could check here that there is a parallel move, but it's only valid
   // for some architectures (for example x86), as other architectures may
   // not do move at all.
@@ -71,18 +79,19 @@
     int e = live1;
     int f = live2;
     int g = live3;
+    int j = live0;
     if (z) {
       if (y) {
         int h = live4;
         int i = live5;
-        array[2] = e + i + h;
-        array[3] = f + i + h;
-        array[4] = g + i + h;
-        array[0] = h;
-        array[1] = i + h;
+        foo.field2 = e + i + h;
+        foo.field3 = f + i + h;
+        foo.field4 = g + i + h;
+        foo.field0 = h;
+        foo.field1 = i + h;
       }
     }
-    live1 = e + f + g;
+    live1 = e + f + g + j;
   }
 
   /// CHECK-START: void Main.test3(boolean, int, int, int, int, int) register (after)
@@ -94,7 +103,7 @@
   /// CHECK-NOT:     ParallelMove
   /// CHECK:       name "B6"
   /// CHECK-NOT:   end_block
-  /// CHECK:         ArraySet
+  /// CHECK:         InstanceFieldSet
   // We could check here that there is a parallel move, but it's only valid
   // for some architectures (for example x86), as other architectures may
   // not do move at all.
@@ -107,6 +116,7 @@
     int e = live1;
     int f = live2;
     int g = live3;
+    int j = live0;
     if (z) {
       live1 = e;
     } else {
@@ -115,24 +125,25 @@
       } else {
         int h = live4;
         int i = live5;
-        array[2] = e + i + h;
-        array[3] = f + i + h;
-        array[4] = g + i + h;
-        array[0] = h;
-        array[1] = i + h;
+        foo.field2 = e + i + h;
+        foo.field3 = f + i + h;
+        foo.field4 = g + i + h;
+        foo.field0 = h;
+        foo.field1 = i + h;
       }
     }
-    live1 = e + f + g;
+    live1 = e + f + g + j;
   }
 
   public static void main(String[] args) {
   }
 
   static boolean y;
+  static int live0;
   static int live1;
   static int live2;
   static int live3;
   static int live4;
   static int live5;
-  static int[] array;
+  static Foo foo;
 }
diff --git a/test/491-current-method/src/Main.java b/test/491-current-method/src/Main.java
index 87ef052..51a41a6 100644
--- a/test/491-current-method/src/Main.java
+++ b/test/491-current-method/src/Main.java
@@ -16,7 +16,7 @@
 
 class Main {
 
-  // The code below is written in a way that will crash
+  // The code below is written in a way that would crash
   // the generated code at the time of submission of this test.
   // Therefore, changes to the register allocator may
   // affect the reproducibility of the crash.
@@ -25,8 +25,8 @@
     // to put the ART current method.
     c = c / 42;
     // We use the empty string for forcing the slow path.
-    // The slow path for charAt when it is intrinsified, will
-    // move the parameter to ECX, and therefore overwrite the ART
+    // The slow path for charAt, when it is intrinsified, will
+    // move the parameter to ECX and therefore overwrite the ART
     // current method.
     "".charAt(c);
 
diff --git a/test/508-checker-disassembly/src/Main.java b/test/508-checker-disassembly/src/Main.java
index 29c9374..0805731 100644
--- a/test/508-checker-disassembly/src/Main.java
+++ b/test/508-checker-disassembly/src/Main.java
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 public class Main {
   // A very simple check that disassembly information has been added to the
diff --git a/test/525-arrays-and-fields/expected.txt b/test/525-checker-arrays-and-fields/expected.txt
similarity index 100%
rename from test/525-arrays-and-fields/expected.txt
rename to test/525-checker-arrays-and-fields/expected.txt
diff --git a/test/525-arrays-and-fields/info.txt b/test/525-checker-arrays-and-fields/info.txt
similarity index 100%
rename from test/525-arrays-and-fields/info.txt
rename to test/525-checker-arrays-and-fields/info.txt
diff --git a/test/525-arrays-and-fields/src/Main.java b/test/525-checker-arrays-and-fields/src/Main.java
similarity index 62%
rename from test/525-arrays-and-fields/src/Main.java
rename to test/525-checker-arrays-and-fields/src/Main.java
index cb1e4af..a635a51 100644
--- a/test/525-arrays-and-fields/src/Main.java
+++ b/test/525-checker-arrays-and-fields/src/Main.java
@@ -80,56 +80,129 @@
 
   //
   // Loops on static arrays with invariant static field references.
+  // The checker is used to ensure hoisting occurred.
   //
 
+  /// CHECK-START: void Main.SInvLoopZ() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopZ() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopZ() {
     for (int i = 0; i < sArrZ.length; i++) {
       sArrZ[i] = sZ;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopB() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopB() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopB() {
     for (int i = 0; i < sArrB.length; i++) {
       sArrB[i] = sB;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopC() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopC() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopC() {
     for (int i = 0; i < sArrC.length; i++) {
       sArrC[i] = sC;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopS() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopS() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopS() {
     for (int i = 0; i < sArrS.length; i++) {
       sArrS[i] = sS;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopI() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopI() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopI() {
     for (int i = 0; i < sArrI.length; i++) {
       sArrI[i] = sI;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopJ() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopJ() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopJ() {
     for (int i = 0; i < sArrJ.length; i++) {
       sArrJ[i] = sJ;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopF() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopF() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopF() {
     for (int i = 0; i < sArrF.length; i++) {
       sArrF[i] = sF;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopD() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopD() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopD() {
     for (int i = 0; i < sArrD.length; i++) {
       sArrD[i] = sD;
     }
   }
 
+  /// CHECK-START: void Main.SInvLoopL() licm (before)
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SInvLoopL() licm (after)
+  /// CHECK-DAG: StaticFieldGet loop:none
+  /// CHECK-DAG: StaticFieldGet loop:none
+
   private static void SInvLoopL() {
     for (int i = 0; i < sArrL.length; i++) {
       sArrL[i] = sL;
@@ -138,6 +211,7 @@
 
   //
   // Loops on static arrays with variant static field references.
+  // Incorrect hoisting is detected by incorrect outcome.
   //
 
   private static void SVarLoopZ() {
@@ -214,56 +288,130 @@
 
   //
   // Loops on static arrays with a cross-over reference.
+  // Incorrect hoisting is detected by incorrect outcome.
+  // In addition, the checker is used to detect no hoisting.
   //
 
+  /// CHECK-START: void Main.SCrossOverLoopZ() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopZ() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopZ() {
     for (int i = 0; i < sArrZ.length; i++) {
       sArrZ[i] = !sArrZ[20];
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopB() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopB() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopB() {
     for (int i = 0; i < sArrB.length; i++) {
       sArrB[i] = (byte)(sArrB[20] + 2);
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopC() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopC() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopC() {
     for (int i = 0; i < sArrC.length; i++) {
       sArrC[i] = (char)(sArrC[20] + 2);
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopS() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopS() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopS() {
     for (int i = 0; i < sArrS.length; i++) {
       sArrS[i] = (short)(sArrS[20] + 2);
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopI() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopI() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopI() {
     for (int i = 0; i < sArrI.length; i++) {
       sArrI[i] = sArrI[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopJ() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopJ() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopJ() {
     for (int i = 0; i < sArrJ.length; i++) {
       sArrJ[i] = sArrJ[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopF() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopF() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopF() {
     for (int i = 0; i < sArrF.length; i++) {
       sArrF[i] = sArrF[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopD() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopD() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopD() {
     for (int i = 0; i < sArrD.length; i++) {
       sArrD[i] = sArrD[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.SCrossOverLoopL() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.SCrossOverLoopL() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private static void SCrossOverLoopL() {
     for (int i = 0; i < sArrL.length; i++) {
       sArrL[i] = (sArrL[20] == anObject) ? anotherObject : anObject;
@@ -272,56 +420,129 @@
 
   //
   // Loops on instance arrays with invariant instance field references.
+  // The checker is used to ensure hoisting occurred.
   //
 
+  /// CHECK-START: void Main.InvLoopZ() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopZ() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopZ() {
     for (int i = 0; i < mArrZ.length; i++) {
       mArrZ[i] = mZ;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopB() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopB() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopB() {
     for (int i = 0; i < mArrB.length; i++) {
       mArrB[i] = mB;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopC() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopC() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopC() {
     for (int i = 0; i < mArrC.length; i++) {
       mArrC[i] = mC;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopS() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopS() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopS() {
     for (int i = 0; i < mArrS.length; i++) {
       mArrS[i] = mS;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopI() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopI() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopI() {
     for (int i = 0; i < mArrI.length; i++) {
       mArrI[i] = mI;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopJ() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopJ() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopJ() {
     for (int i = 0; i < mArrJ.length; i++) {
       mArrJ[i] = mJ;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopF() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopF() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopF() {
     for (int i = 0; i < mArrF.length; i++) {
       mArrF[i] = mF;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopD() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopD() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopD() {
     for (int i = 0; i < mArrD.length; i++) {
       mArrD[i] = mD;
     }
   }
 
+  /// CHECK-START: void Main.InvLoopL() licm (before)
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+  /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.InvLoopL() licm (after)
+  /// CHECK-DAG: InstanceFieldGet loop:none
+  /// CHECK-DAG: InstanceFieldGet loop:none
+
   private void InvLoopL() {
     for (int i = 0; i < mArrL.length; i++) {
       mArrL[i] = mL;
@@ -330,6 +551,7 @@
 
   //
   // Loops on instance arrays with variant instance field references.
+  // Incorrect hoisting is detected by incorrect outcome.
   //
 
   private void VarLoopZ() {
@@ -406,56 +628,130 @@
 
   //
   // Loops on instance arrays with a cross-over reference.
+  // Incorrect hoisting is detected by incorrect outcome.
+  // In addition, the checker is used to detect no hoisting.
   //
 
+  /// CHECK-START: void Main.CrossOverLoopZ() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopZ() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopZ() {
     for (int i = 0; i < mArrZ.length; i++) {
       mArrZ[i] = !mArrZ[20];
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopB() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopB() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopB() {
     for (int i = 0; i < mArrB.length; i++) {
       mArrB[i] = (byte)(mArrB[20] + 2);
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopC() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopC() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopC() {
     for (int i = 0; i < mArrC.length; i++) {
       mArrC[i] = (char)(mArrC[20] + 2);
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopS() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopS() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopS() {
     for (int i = 0; i < mArrS.length; i++) {
       mArrS[i] = (short)(mArrS[20] + 2);
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopI() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopI() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopI() {
     for (int i = 0; i < mArrI.length; i++) {
       mArrI[i] = mArrI[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopJ() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopJ() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopJ() {
     for (int i = 0; i < mArrJ.length; i++) {
       mArrJ[i] = mArrJ[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopF() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopF() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopF() {
     for (int i = 0; i < mArrF.length; i++) {
       mArrF[i] = mArrF[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopD() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopD() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopD() {
     for (int i = 0; i < mArrD.length; i++) {
       mArrD[i] = mArrD[20] + 2;
     }
   }
 
+  /// CHECK-START: void Main.CrossOverLoopL() licm (before)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+  /// CHECK-START: void Main.CrossOverLoopL() licm (after)
+  /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+  /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
   private void CrossOverLoopL() {
     for (int i = 0; i < mArrL.length; i++) {
       mArrL[i] = (mArrL[20] == anObject) ? anotherObject : anObject;
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index fd9fcaf..728ccea 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -36,4 +36,5 @@
 b/22411633 (3)
 b/22411633 (4)
 b/22411633 (5)
+b/22777307
 Done!
diff --git a/test/800-smali/smali/b_22777307.smali b/test/800-smali/smali/b_22777307.smali
new file mode 100644
index 0000000..6de3c70
--- /dev/null
+++ b/test/800-smali/smali/b_22777307.smali
@@ -0,0 +1,18 @@
+.class public LB22777307;
+.super Ljava/lang/Object;
+
+# A static field. That way we can use the reference.
+.field private static sTest:Ljava/lang/Object;
+
+.method public static run()V
+.registers 2
+       # This is a broken new-instance. It needs to throw at runtime, though. This test is here to
+       # ensure we won't produce a VerifyError.
+       # Cloneable was chosen because it's an already existing interface.
+       new-instance v0, Ljava/lang/Cloneable;
+       invoke-direct {v0}, Ljava/lang/Cloneable;-><init>()V
+       sput-object v0, LB22777307;->sTest:Ljava/lang/Object;
+
+       return-void
+
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 8da2af4..438e214 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -119,6 +119,8 @@
                 new VerifyError(), null));
         testCases.add(new TestCase("b/22411633 (5)", "B22411633_5", "run", new Object[] { false },
                 null, null));
+        testCases.add(new TestCase("b/22777307", "B22777307", "run", null, new InstantiationError(),
+                null));
     }
 
     public void runTests() {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ed1339a..3698bc8 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -113,7 +113,7 @@
   COMPILER_TYPES += default
 endif
 ifeq ($(ART_TEST_INTERPRETER_ACCESS_CHECKS),true)
-  COMPILER_TYPES += interpreter-access-checks
+  COMPILER_TYPES += interp-ac
 endif
 ifeq ($(ART_TEST_INTERPRETER),true)
   COMPILER_TYPES += interpreter
@@ -277,9 +277,9 @@
   506-verify-aput \
   800-smali
 
-ifneq (,$(filter interpreter-access-checks,$(COMPILER_TYPES)))
+ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
-      interpreter-access-checks,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      interp-ac,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
       $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS), $(ALL_ADDRESS_SIZES))
 endif
 
@@ -288,10 +288,8 @@
 # Tests that are broken with GC stress.
 # 137-cfi needs to unwind a second forked process. We're using a primitive sleep to wait till we
 # hope the second process got into the expected state. The slowness of gcstress makes this bad.
-# 955-lambda-smali temporarily disabled b/22665783
 TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
-  137-cfi \
-  955-lambda-smali
+  137-cfi
 
 ifneq (,$(filter gcstress,$(GC_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -631,7 +629,7 @@
 
 # Create a rule to build and run a tests following the form:
 # test-art-{1: host or target}-run-test-{2: debug ndebug}-{3: prebuild no-prebuild no-dex2oat}-
-#    {4: interpreter default optimizing jit interpreter-access-checks}-
+#    {4: interpreter default optimizing jit interp-ac}-
 #    {5: relocate nrelocate relocate-npatchoat}-
 #    {6: trace or ntrace}-{7: gcstress gcverify cms}-{8: forcecopy checkjni jni}-
 #    {9: no-image image picimage}-{10: pictest npictest}-
@@ -702,7 +700,7 @@
     ifeq ($(4),interpreter)
       test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_RULES
       run_test_options += --interpreter
-    else ifeq ($(4),interpreter-access-checks)
+    else ifeq ($(4),interp-ac)
       test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_ACCESS_CHECKS_RULES
       run_test_options += --interpreter --verify-soft-fail
     else
diff --git a/test/run-test b/test/run-test
index 56ae480..934329f 100755
--- a/test/run-test
+++ b/test/run-test
@@ -264,7 +264,7 @@
         shift
     elif [ "x$1" = "x--verify-soft-fail" ]; then
         run_args="${run_args} --verify-soft-fail"
-        image_suffix="-interpreter-access-checks"
+        image_suffix="-interp-ac"
         shift
     elif [ "x$1" = "x--no-optimize" ]; then
         run_args="${run_args} --no-optimize"
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 992a8a6..d58f034 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -150,5 +150,12 @@
   result: EXEC_FAILED,
   modes: [device],
   names: ["org.apache.harmony.tests.java.lang.ClassTest#test_forNameLjava_lang_String"]
+},
+{
+  description: "TimeZoneTest.testAllDisplayNames times out, needs investigation",
+  result: EXEC_FAILED,
+  modes: [device],
+  names: ["libcore.java.util.TimeZoneTest.testAllDisplayNames"],
+  bug: 22786792
 }
 ]