Merge "Revert "Interpreter: Add support for direct handle invokes on methods.""
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 140c7f0..663cbaf 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -162,17 +162,17 @@
                                          /*out*/bool* needs_taken_test) {
   bool is_last_value = false;
   int64_t stride_value = 0;
-  return GenerateCode(context,
-                      instruction,
-                      is_last_value,
-                      nullptr,
-                      nullptr,
-                      nullptr,
-                      nullptr,
-                      nullptr,  // nothing generated yet
-                      &stride_value,
-                      needs_finite_test,
-                      needs_taken_test)
+  return GenerateRangeOrLastValue(context,
+                                  instruction,
+                                  is_last_value,
+                                  nullptr,
+                                  nullptr,
+                                  nullptr,
+                                  nullptr,
+                                  nullptr,  // nothing generated yet
+                                  &stride_value,
+                                  needs_finite_test,
+                                  needs_taken_test)
       && (stride_value == -1 ||
           stride_value == 0 ||
           stride_value == 1);  // avoid wrap-around anomalies.
@@ -187,17 +187,17 @@
   bool is_last_value = false;
   int64_t stride_value = 0;
   bool b1, b2;  // unused
-  if (!GenerateCode(context,
-                    instruction,
-                    is_last_value,
-                    graph,
-                    block,
-                    lower,
-                    upper,
-                    nullptr,
-                    &stride_value,
-                    &b1,
-                    &b2)) {
+  if (!GenerateRangeOrLastValue(context,
+                                instruction,
+                                is_last_value,
+                                graph,
+                                block,
+                                lower,
+                                upper,
+                                nullptr,
+                                &stride_value,
+                                &b1,
+                                &b2)) {
     LOG(FATAL) << "Failed precondition: CanGenerateRange()";
   }
 }
@@ -209,17 +209,17 @@
   bool is_last_value = false;
   int64_t stride_value = 0;
   bool b1, b2;  // unused
-  if (!GenerateCode(context,
-                    context,
-                    is_last_value,
-                    graph,
-                    block,
-                    nullptr,
-                    nullptr,
-                    &taken_test,
-                    &stride_value,
-                    &b1,
-                    &b2)) {
+  if (!GenerateRangeOrLastValue(context,
+                                context,
+                                is_last_value,
+                                graph,
+                                block,
+                                nullptr,
+                                nullptr,
+                                &taken_test,
+                                &stride_value,
+                                &b1,
+                                &b2)) {
     LOG(FATAL) << "Failed precondition: CanGenerateRange()";
   }
   return taken_test;
@@ -230,17 +230,17 @@
   int64_t stride_value = 0;
   bool needs_finite_test = false;
   bool needs_taken_test = false;
-  return GenerateCode(instruction,
-                      instruction,
-                      is_last_value,
-                      nullptr,
-                      nullptr,
-                      nullptr,
-                      nullptr,
-                      nullptr,  // nothing generated yet
-                      &stride_value,
-                      &needs_finite_test,
-                      &needs_taken_test)
+  return GenerateRangeOrLastValue(instruction,
+                                  instruction,
+                                  is_last_value,
+                                  nullptr,
+                                  nullptr,
+                                  nullptr,
+                                  nullptr,
+                                  nullptr,  // nothing generated yet
+                                  &stride_value,
+                                  &needs_finite_test,
+                                  &needs_taken_test)
       && !needs_finite_test && !needs_taken_test;
 }
 
@@ -251,17 +251,17 @@
   bool is_last_value = true;
   int64_t stride_value = 0;
   bool b1, b2;  // unused
-  if (!GenerateCode(instruction,
-                    instruction,
-                    is_last_value,
-                    graph,
-                    block,
-                    &last_value,
-                    &last_value,
-                    nullptr,
-                    &stride_value,
-                    &b1,
-                    &b2)) {
+  if (!GenerateRangeOrLastValue(instruction,
+                                instruction,
+                                is_last_value,
+                                graph,
+                                block,
+                                &last_value,
+                                &last_value,
+                                nullptr,
+                                &stride_value,
+                                &b1,
+                                &b2)) {
     LOG(FATAL) << "Failed precondition: CanGenerateLastValue()";
   }
   return last_value;
@@ -280,6 +280,12 @@
   }
 }
 
+bool InductionVarRange::IsFinite(HLoopInformation* loop) const {
+  HInductionVarAnalysis::InductionInfo *trip =
+      induction_analysis_->LookupInfo(loop, GetLoopControl(loop));
+  return trip != nullptr && !IsUnsafeTripCount(trip);
+}
+
 //
 // Private class methods.
 //
@@ -732,17 +738,17 @@
   return Value();
 }
 
-bool InductionVarRange::GenerateCode(HInstruction* context,
-                                     HInstruction* instruction,
-                                     bool is_last_value,
-                                     HGraph* graph,
-                                     HBasicBlock* block,
-                                     /*out*/HInstruction** lower,
-                                     /*out*/HInstruction** upper,
-                                     /*out*/HInstruction** taken_test,
-                                     /*out*/int64_t* stride_value,
-                                     /*out*/bool* needs_finite_test,
-                                     /*out*/bool* needs_taken_test) const {
+bool InductionVarRange::GenerateRangeOrLastValue(HInstruction* context,
+                                                 HInstruction* instruction,
+                                                 bool is_last_value,
+                                                 HGraph* graph,
+                                                 HBasicBlock* block,
+                                                 /*out*/HInstruction** lower,
+                                                 /*out*/HInstruction** upper,
+                                                 /*out*/HInstruction** taken_test,
+                                                 /*out*/int64_t* stride_value,
+                                                 /*out*/bool* needs_finite_test,
+                                                 /*out*/bool* needs_taken_test) const {
   HLoopInformation* loop = nullptr;
   HInductionVarAnalysis::InductionInfo* info = nullptr;
   HInductionVarAnalysis::InductionInfo* trip = nullptr;
@@ -760,12 +766,17 @@
   *needs_taken_test = IsBodyTripCount(trip);
   // Handle last value request.
   if (is_last_value) {
-    if (info->induction_class != HInductionVarAnalysis::kLinear) {
-      return false;
-    } else if (*stride_value > 0) {
-      lower = nullptr;
+    if (info->induction_class == HInductionVarAnalysis::kLinear) {
+      if (*stride_value > 0) {
+        lower = nullptr;
+      } else {
+        upper = nullptr;
+      }
+    } else if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
+      DCHECK(!in_body);
+      return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test);
     } else {
-      upper = nullptr;
+      return false;
     }
   }
   // Code generation for taken test: generate the code when requested or otherwise analyze
@@ -787,6 +798,56 @@
       GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
 }
 
+bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info,
+                                                  HInductionVarAnalysis::InductionInfo* trip,
+                                                  HGraph* graph,
+                                                  HBasicBlock* block,
+                                                  /*out*/HInstruction** result,
+                                                  /*out*/bool* needs_taken_test) const {
+  DCHECK(info->induction_class == HInductionVarAnalysis::kPeriodic);
+  // Count period.
+  int32_t period = 1;
+  for (HInductionVarAnalysis::InductionInfo* p = info;
+       p->induction_class == HInductionVarAnalysis::kPeriodic;
+       p = p->op_b, ++period) {}
+  // Handle periodic(x, y) case for restricted types.
+  if (period != 2 ||
+      trip->op_a->type != Primitive::kPrimInt ||
+      (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) {
+    return false;  // TODO: easy to generalize
+  }
+  HInstruction* x_instr = nullptr;
+  HInstruction* y_instr = nullptr;
+  HInstruction* trip_expr = nullptr;
+  if (GenerateCode(info->op_a, nullptr, graph, block, graph ? &x_instr   : nullptr, false, false) &&
+      GenerateCode(info->op_b, nullptr, graph, block, graph ? &y_instr   : nullptr, false, false) &&
+      GenerateCode(trip->op_a, nullptr, graph, block, graph ? &trip_expr : nullptr, false, false)) {
+    // During actual code generation (graph != nullptr),
+    // generate is_even ? x : y select instruction.
+    if (graph != nullptr) {
+      HInstruction* is_even = Insert(block, new (graph->GetArena()) HEqual(
+          Insert(block, new (graph->GetArena()) HAnd(
+              Primitive::kPrimInt, trip_expr, graph->GetIntConstant(1))),
+          graph->GetIntConstant(0), kNoDexPc));
+      *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x_instr, y_instr, kNoDexPc));
+    }
+    // Guard select with taken test if needed.
+    if (*needs_taken_test) {
+      HInstruction* taken_test = nullptr;
+      if (!GenerateCode(
+          trip->op_b, nullptr, graph, block, graph ? &taken_test : nullptr, false, false)) {
+        return false;
+      } else if (graph != nullptr) {
+         *result = Insert(block,
+                          new (graph->GetArena()) HSelect(taken_test, *result, x_instr, kNoDexPc));
+      }
+      *needs_taken_test = false;  // taken care of
+    }
+    return true;
+  }
+  return false;
+}
+
 bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
                                      HInductionVarAnalysis::InductionInfo* trip,
                                      HGraph* graph,  // when set, code is generated
@@ -812,6 +873,7 @@
         // Invariants.
         switch (info->operation) {
           case HInductionVarAnalysis::kAdd:
+          case HInductionVarAnalysis::kXor:
           case HInductionVarAnalysis::kLT:
           case HInductionVarAnalysis::kLE:
           case HInductionVarAnalysis::kGT:
@@ -823,6 +885,8 @@
                 switch (info->operation) {
                   case HInductionVarAnalysis::kAdd:
                     operation = new (graph->GetArena()) HAdd(type, opa, opb); break;
+                  case HInductionVarAnalysis::kXor:
+                    operation = new (graph->GetArena()) HXor(type, opa, opb); break;
                   case HInductionVarAnalysis::kLT:
                     operation = new (graph->GetArena()) HLessThan(opa, opb); break;
                   case HInductionVarAnalysis::kLE:
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 8951300..2f70046 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -139,6 +139,11 @@
     induction_analysis_->VisitLoop(loop);
   }
 
+  /**
+   * Checks if header logic of a loop terminates.
+   */
+  bool IsFinite(HLoopInformation* loop) const;
+
  private:
   /*
    * Enum used in IsConstant() request.
@@ -218,17 +223,24 @@
    * success. With values nullptr, the method can be used to determine if code generation
    * would be successful without generating actual code yet.
    */
-  bool GenerateCode(HInstruction* context,
-                    HInstruction* instruction,
-                    bool is_last_val,
-                    HGraph* graph,
-                    HBasicBlock* block,
-                    /*out*/ HInstruction** lower,
-                    /*out*/ HInstruction** upper,
-                    /*out*/ HInstruction** taken_test,
-                    /*out*/ int64_t* stride_value,
-                    /*out*/ bool* needs_finite_test,
-                    /*out*/ bool* needs_taken_test) const;
+  bool GenerateRangeOrLastValue(HInstruction* context,
+                                HInstruction* instruction,
+                                bool is_last_val,
+                                HGraph* graph,
+                                HBasicBlock* block,
+                                /*out*/ HInstruction** lower,
+                                /*out*/ HInstruction** upper,
+                                /*out*/ HInstruction** taken_test,
+                                /*out*/ int64_t* stride_value,
+                                /*out*/ bool* needs_finite_test,
+                                /*out*/ bool* needs_taken_test) const;
+
+  bool GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info,
+                                 HInductionVarAnalysis::InductionInfo* trip,
+                                 HGraph* graph,
+                                 HBasicBlock* block,
+                                 /*out*/HInstruction** result,
+                                 /*out*/ bool* needs_taken_test) const;
 
   bool GenerateCode(HInductionVarAnalysis::InductionInfo* info,
                     HInductionVarAnalysis::InductionInfo* trip,
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 7347686..820fa29 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -168,7 +168,9 @@
   const int16_t declaring_class_def_index_;  // declaring class's def's dex index.
   bool value_killed_by_loop_side_effects_;   // value of this location may be killed by loop
                                              // side effects because this location is stored
-                                             // into inside a loop.
+                                             // into inside a loop. This gives
+                                             // better info on whether a singleton's location
+                                             // value may be killed by loop side effects.
 
   DISALLOW_COPY_AND_ASSIGN(HeapLocation);
 };
@@ -420,8 +422,26 @@
   void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE {
     HeapLocation* location = VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
     has_heap_stores_ = true;
-    if (instruction->GetBlock()->GetLoopInformation() != nullptr) {
-      location->SetValueKilledByLoopSideEffects(true);
+    if (location->GetReferenceInfo()->IsSingleton()) {
+      // A singleton's location value may be killed by loop side effects if it's
+      // defined before that loop, and it's stored into inside that loop.
+      HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
+      if (loop_info != nullptr) {
+        HInstruction* ref = location->GetReferenceInfo()->GetReference();
+        DCHECK(ref->IsNewInstance());
+        if (loop_info->IsDefinedOutOfTheLoop(ref)) {
+          // ref's location value may be killed by this loop's side effects.
+          location->SetValueKilledByLoopSideEffects(true);
+        } else {
+          // ref is defined inside this loop so this loop's side effects cannot
+          // kill its location value at the loop header since ref/its location doesn't
+          // exist yet at the loop header.
+        }
+      }
+    } else {
+      // For non-singletons, value_killed_by_loop_side_effects_ is inited to
+      // true.
+      DCHECK_EQ(location->IsValueKilledByLoopSideEffects(), true);
     }
   }
 
@@ -810,9 +830,6 @@
         if (loop_info != nullptr) {
           // instruction is a store in the loop so the loop must does write.
           DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
-          // If it's a singleton, IsValueKilledByLoopSideEffects() must be true.
-          DCHECK(!ref_info->IsSingleton() ||
-                 heap_location_collector_.GetHeapLocation(idx)->IsValueKilledByLoopSideEffects());
 
           if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
             DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 33fa87d..703a104 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -20,17 +20,37 @@
 
 namespace art {
 
-// TODO: Generalize to cycles, as found by induction analysis?
+// Detects a potential induction cycle. Note that the actual induction
+// information is queried later if its last value is really needed.
 static bool IsPhiInduction(HPhi* phi, ArenaSet<HInstruction*>* iset) {
   DCHECK(iset->empty());
   HInputsRef inputs = phi->GetInputs();
-  if (inputs.size() == 2 && (inputs[1]->IsAdd() || inputs[1]->IsSub())) {
-    HInstruction* addsub = inputs[1];
-    if (addsub->InputAt(0) == phi || addsub->InputAt(1) == phi) {
-      if (addsub->GetUses().HasExactlyOneElement()) {
-        iset->insert(phi);
-        iset->insert(addsub);
-        return true;
+  if (inputs.size() == 2) {
+    HLoopInformation* loop_info = phi->GetBlock()->GetLoopInformation();
+    HInstruction* op = inputs[1];
+    if (op->GetBlock()->GetLoopInformation() == loop_info) {
+      // Chase a simple chain back to phi.
+      while (!op->IsPhi()) {
+        // Binary operation with single use in same loop.
+        if (!op->IsBinaryOperation() || !op->GetUses().HasExactlyOneElement()) {
+          return false;
+        }
+        // Chase back either through left or right operand.
+        iset->insert(op);
+        HInstruction* a = op->InputAt(0);
+        HInstruction* b = op->InputAt(1);
+        if (a->GetBlock()->GetLoopInformation() == loop_info && b != phi) {
+          op = a;
+        } else if (b->GetBlock()->GetLoopInformation() == loop_info) {
+          op = b;
+        } else {
+          return false;
+        }
+      }
+      // Closed the cycle?
+      if (op == phi) {
+       iset->insert(phi);
+       return true;
       }
     }
   }
@@ -62,16 +82,23 @@
   return false;
 }
 
+// Does the loop-body consist of induction cycle and direct control flow only?
 static bool IsEmptyBody(HBasicBlock* block, ArenaSet<HInstruction*>* iset) {
-  HInstruction* phi = block->GetFirstPhi();
-  HInstruction* i = block->GetFirstInstruction();
-  return phi == nullptr && iset->find(i) != iset->end() &&
-      i->GetNext() != nullptr && i->GetNext()->IsGoto();
+  if (block->GetFirstPhi() == nullptr) {
+    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+      HInstruction* instruction = it.Current();
+      if (!instruction->IsGoto() && iset->find(instruction) == iset->end()) {
+        return false;
+      }
+    }
+    return true;
+  }
+  return false;
 }
 
+// Remove the instruction from the graph. A bit more elaborate than the usual
+// instruction removal, since there may be a cycle in the use structure.
 static void RemoveFromCycle(HInstruction* instruction) {
-  // A bit more elaborate than the usual instruction removal,
-  // since there may be a cycle in the use structure.
   instruction->RemoveAsUserOfAllInputs();
   instruction->RemoveEnvironmentUsers();
   instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
@@ -196,7 +223,9 @@
     }
     SimplifyInduction(node);
     SimplifyBlocks(node);
-    RemoveIfEmptyLoop(node);
+    if (node->inner == nullptr) {
+      RemoveIfEmptyInnerLoop(node);
+    }
   }
 }
 
@@ -233,7 +262,7 @@
         block->RemoveInstruction(instruction);
       }
     }
-    // Remove trivial control flow blocks from the loop body, again usually resulting
+    // Remove trivial control flow blocks from the loop-body, again usually resulting
     // from eliminating induction cycles.
     if (block->GetPredecessors().size() == 1 &&
         block->GetSuccessors().size() == 1 &&
@@ -252,9 +281,13 @@
   }
 }
 
-void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) {
+void HLoopOptimization::RemoveIfEmptyInnerLoop(LoopNode* node) {
   HBasicBlock* header = node->loop_info->GetHeader();
   HBasicBlock* preheader = node->loop_info->GetPreHeader();
+  // Ensure loop header logic is finite.
+  if (!induction_range_.IsFinite(node->loop_info)) {
+    return;
+  }
   // Ensure there is only a single loop-body (besides the header).
   HBasicBlock* body = nullptr;
   for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 9c4b462..4113357 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -62,7 +62,7 @@
 
   void SimplifyInduction(LoopNode* node);
   void SimplifyBlocks(LoopNode* node);
-  void RemoveIfEmptyLoop(LoopNode* node);
+  void RemoveIfEmptyInnerLoop(LoopNode* node);
 
   bool IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
                            HInstruction* instruction,
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 54e52e5..c3321e1 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2117,7 +2117,7 @@
     mov     %r8d, %ecx
     cmovg   %r9d, %ecx
     /* Going into loop to compare each character */
-    jecxz   .Lstring_compareto_keep_length      // check loop counter (if 0 then stop)
+    jecxz   .Lstring_compareto_keep_length1     // check loop counter (if 0 then stop)
 .Lstring_compareto_loop_comparison_this_compressed:
     movzbl  (%edi), %r8d                        // move *(this_cur_char) byte to long
     movzwl  (%esi), %r9d                        // move *(that_cur_char) word to long
@@ -2126,6 +2126,7 @@
     subl    %r9d, %r8d
     loope   .Lstring_compareto_loop_comparison_this_compressed
     cmovne  %r8d, %eax                          // return eax = *(this_cur_char) - *(that_cur_char)
+.Lstring_compareto_keep_length1:
     ret
 .Lstring_compareto_that_is_compressed:
     andl    LITERAL(0x7FFFFFFF), %r9d
@@ -2134,7 +2135,7 @@
     mov     %r8d, %ecx
     cmovg   %r9d, %ecx
     /* Comparison this (8-bit) and that (16-bit) */
-    jecxz   .Lstring_compareto_keep_length      // check loop counter (if 0, don't compare)
+    jecxz   .Lstring_compareto_keep_length2     // check loop counter (if 0, don't compare)
 .Lstring_compareto_loop_comparison_that_compressed:
     movzwl  (%edi), %r8d                        // move *(this_cur_char) word to long
     movzbl  (%esi), %r9d                        // move *(that_cur_chat) byte to long
@@ -2143,6 +2144,7 @@
     subl    %r9d, %r8d
     loope   .Lstring_compareto_loop_comparison_that_compressed
     cmovne  %r8d, %eax                          // return eax = *(this_cur_char) - *(that_cur_char)
+.Lstring_compareto_keep_length2:
     ret
 .Lstring_compareto_both_compressed:
     andl    LITERAL(0x7FFFFFFF), %r9d
@@ -2151,9 +2153,9 @@
     movl    %r8d, %eax
     subl    %r9d, %eax
     cmovg   %r9d, %ecx
-    jecxz   .Lstring_compareto_keep_length
+    jecxz   .Lstring_compareto_keep_length3
     repe    cmpsb
-    je      .Lstring_compareto_keep_length
+    je      .Lstring_compareto_keep_length3
     movzbl  -1(%edi), %eax        // get last compared char from this string (8-bit)
     movzbl  -1(%esi), %ecx        // get last compared char from comp string (8-bit)
     jmp     .Lstring_compareto_count_difference
@@ -2171,14 +2173,14 @@
      *   esi: pointer to comp string data
      *   edi: pointer to this string data
      */
-    jecxz .Lstring_compareto_keep_length
+    jecxz .Lstring_compareto_keep_length3
     repe  cmpsw                   // find nonmatching chars in [%esi] and [%edi], up to length %ecx
-    je    .Lstring_compareto_keep_length
+    je    .Lstring_compareto_keep_length3
     movzwl  -2(%edi), %eax        // get last compared char from this string (16-bit)
     movzwl  -2(%esi), %ecx        // get last compared char from comp string (16-bit)
 .Lstring_compareto_count_difference:
     subl  %ecx, %eax              // return the difference
-.Lstring_compareto_keep_length:
+.Lstring_compareto_keep_length3:
     ret
 END_FUNCTION art_quick_string_compareto
 
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index dba9b8f..126187f 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -36,7 +36,8 @@
   return FindClass(self, descriptor, ScopedNullHandle<mirror::ClassLoader>());
 }
 
-inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, mirror::Class** element_class) {
+inline mirror::Class* ClassLinker::FindArrayClass(Thread* self,
+                                                  ObjPtr<mirror::Class>* element_class) {
   for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
     // Read the cached array class once to avoid races with other threads setting it.
     mirror::Class* array_class = find_array_class_cache_[i].Read();
@@ -49,7 +50,7 @@
   descriptor += (*element_class)->GetDescriptor(&temp);
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle((*element_class)->GetClassLoader()));
-  HandleWrapper<mirror::Class> h_element_class(hs.NewHandleWrapper(element_class));
+  HandleWrapperObjPtr<mirror::Class> h_element_class(hs.NewHandleWrapper(element_class));
   mirror::Class* array_class = FindClass(self, descriptor.c_str(), class_loader);
   if (array_class != nullptr) {
     // Benign races in storing array class and incrementing index.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d07aa89..63de76c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1962,7 +1962,7 @@
         // 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();
+      ObjPtr<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));
@@ -2294,7 +2294,7 @@
                                              const char* descriptor,
                                              size_t hash,
                                              Handle<mirror::ClassLoader> class_loader,
-                                             mirror::Class** result) {
+                                             ObjPtr<mirror::Class>* result) {
   // Termination case: boot class-loader.
   if (IsBootClassLoader(soa, class_loader.Get())) {
     // The boot class loader, search the boot class path.
@@ -2452,12 +2452,12 @@
     }
   } else {
     ScopedObjectAccessUnchecked soa(self);
-    mirror::Class* cp_klass;
+    ObjPtr<mirror::Class> cp_klass;
     if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) {
       // The chain was understood. So the value in cp_klass is either the class we were looking
       // for, or not found.
       if (cp_klass != nullptr) {
-        return cp_klass;
+        return cp_klass.Ptr();
       }
       // TODO: We handle the boot classpath loader in FindClassInPathClassLoader. Try to unify this
       //       and the branch above. TODO: throw the right exception here.
@@ -7798,7 +7798,7 @@
   // other than by looking at the shorty ?
   const size_t num_method_args = strlen(dex_file.StringDataByIdx(proto_id.shorty_idx_)) - 1;
 
-  mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
   mirror::Class* array_of_class = FindArrayClass(self, &class_type);
   Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
       mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_method_args)));
@@ -8153,12 +8153,12 @@
   }
 }
 
-void ClassLinker::InsertDexFileInToClassLoader(mirror::Object* dex_file,
-                                               mirror::ClassLoader* class_loader) {
+void ClassLinker::InsertDexFileInToClassLoader(ObjPtr<mirror::Object> dex_file,
+                                               ObjPtr<mirror::ClassLoader> class_loader) {
   DCHECK(dex_file != nullptr);
   Thread* const self = Thread::Current();
   WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
-  ClassTable* const table = ClassTableForClassLoader(class_loader);
+  ClassTable* const table = ClassTableForClassLoader(class_loader.Ptr());
   DCHECK(table != nullptr);
   if (table->InsertStrongRoot(dex_file) && class_loader != nullptr) {
     // It was not already inserted, perform the write barrier to let the GC know the class loader's
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 70cc768..6437010 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -181,7 +181,7 @@
                                   const char* descriptor,
                                   size_t hash,
                                   Handle<mirror::ClassLoader> class_loader,
-                                  mirror::Class** result)
+                                  ObjPtr<mirror::Class>* result)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!dex_lock_);
 
@@ -192,7 +192,7 @@
       REQUIRES(!dex_lock_);
 
   // Finds the array class given for the element class.
-  mirror::Class* FindArrayClass(Thread* self, mirror::Class** element_class)
+  mirror::Class* FindArrayClass(Thread* self, ObjPtr<mirror::Class>* element_class)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!dex_lock_);
 
@@ -606,7 +606,8 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // May be called with null class_loader due to legacy code. b/27954959
-  void InsertDexFileInToClassLoader(mirror::Object* dex_file, mirror::ClassLoader* class_loader)
+  void InsertDexFileInToClassLoader(ObjPtr<mirror::Object> dex_file,
+                                    ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 6279717..4fac830 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -216,7 +216,7 @@
     EXPECT_STREQ(direct_interface0->GetDescriptor(&temp), "Ljava/lang/Cloneable;");
     ObjPtr<mirror::Class> direct_interface1 = mirror::Class::GetDirectInterface(self, array, 1);
     EXPECT_STREQ(direct_interface1->GetDescriptor(&temp), "Ljava/io/Serializable;");
-    mirror::Class* array_ptr = array->GetComponentType();
+    ObjPtr<mirror::Class> array_ptr = array->GetComponentType();
     EXPECT_OBJ_PTR_EQ(class_linker_->FindArrayClass(self, &array_ptr), array.Get());
 
     PointerSize pointer_size = class_linker_->GetImagePointerSize();
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 2ae7e8c..d435050 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -156,7 +156,7 @@
   return ComputeModifiedUtf8Hash(descriptor);
 }
 
-bool ClassTable::InsertStrongRoot(mirror::Object* obj) {
+bool ClassTable::InsertStrongRoot(ObjPtr<mirror::Object> obj) {
   WriterMutexLock mu(Thread::Current(), lock_);
   DCHECK(obj != nullptr);
   for (GcRoot<mirror::Object>& root : strong_roots_) {
@@ -167,7 +167,7 @@
   strong_roots_.push_back(GcRoot<mirror::Object>(obj));
   // If `obj` is a dex cache associated with a new oat file with GC roots, add it to oat_files_.
   if (obj->IsDexCache()) {
-    const DexFile* dex_file = down_cast<mirror::DexCache*>(obj)->GetDexFile();
+    const DexFile* dex_file = ObjPtr<mirror::DexCache>::DownCast(obj)->GetDexFile();
     if (dex_file != nullptr && dex_file->GetOatDexFile() != nullptr) {
       const OatFile* oat_file = dex_file->GetOatDexFile()->GetOatFile();
       if (!oat_file->GetBssGcRoots().empty() && !ContainsElement(oat_files_, oat_file)) {
diff --git a/runtime/class_table.h b/runtime/class_table.h
index acb15c7..20e434d 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -27,6 +27,7 @@
 #include "base/mutex.h"
 #include "dex_file.h"
 #include "gc_root.h"
+#include "obj_ptr.h"
 #include "object_callbacks.h"
 #include "runtime.h"
 
@@ -136,7 +137,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Return true if we inserted the strong root, false if it already exists.
-  bool InsertStrongRoot(mirror::Object* obj)
+  bool InsertStrongRoot(ObjPtr<mirror::Object> obj)
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 576c4aa..e538334 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -254,7 +254,7 @@
     return nullptr;
   }
 
-  mirror::Class* annotation_member_class =
+  ObjPtr<mirror::Class> annotation_member_class =
       soa.Decode<mirror::Class>(WellKnownClasses::libcore_reflect_AnnotationMember).Ptr();
   mirror::Class* annotation_member_array_class =
       class_linker->FindArrayClass(self, &annotation_member_class);
@@ -731,7 +731,7 @@
   if (annotation_item == nullptr) {
     return nullptr;
   }
-  mirror::Class* string_class = mirror::String::GetJavaLangString();
+  ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
   Handle<mirror::Class> string_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
   if (string_array_class.Get() == nullptr) {
@@ -757,7 +757,7 @@
   if (annotation_item == nullptr) {
     return nullptr;
   }
-  mirror::Class* class_class = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
   Handle<mirror::Class> class_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class)));
   if (class_array_class.Get() == nullptr) {
@@ -839,8 +839,8 @@
   Thread* self = Thread::Current();
   ScopedObjectAccessUnchecked soa(self);
   StackHandleScope<1> hs(self);
-  mirror::Class* annotation_array_class =
-      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array).Ptr();
+  ObjPtr<mirror::Class> annotation_array_class =
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
   mirror::Class* annotation_array_array_class =
       Runtime::Current()->GetClassLinker()->FindArrayClass(self, &annotation_array_class);
   if (annotation_array_array_class == nullptr) {
@@ -1049,7 +1049,7 @@
   StackHandleScope<5> hs(Thread::Current());
 
   // Extract the parameters' names String[].
-  mirror::Class* string_class = mirror::String::GetJavaLangString();
+  ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
   Handle<mirror::Class> string_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
   if (UNLIKELY(string_array_class.Get() == nullptr)) {
@@ -1139,7 +1139,7 @@
     return nullptr;
   }
   StackHandleScope<1> hs(Thread::Current());
-  mirror::Class* class_class = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
   Handle<mirror::Class> class_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(hs.Self(), &class_class)));
   if (class_array_class.Get() == nullptr) {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 45bd87b..f4a3aea 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1246,9 +1246,6 @@
         << " total=" << seen_backtrace_count_.LoadRelaxed() +
             unique_backtrace_count_.LoadRelaxed();
   }
-  // Delete any still registered allocation listener.
-  AllocationListener* l = GetAndOverwriteAllocationListener(&alloc_listener_, nullptr);
-  delete l;
 
   VLOG(heap) << "Finished ~Heap()";
 }
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 0030326..2d5d7cb 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -155,11 +155,12 @@
   large_objects_.Put(obj, LargeObject {mem_map, false /* not zygote */});
   const size_t allocation_size = mem_map->BaseSize();
   DCHECK(bytes_allocated != nullptr);
-  begin_ = std::min(begin_, reinterpret_cast<uint8_t*>(obj));
-  uint8_t* obj_end = reinterpret_cast<uint8_t*>(obj) + allocation_size;
-  if (end_ == nullptr || obj_end > end_) {
-    end_ = obj_end;
+
+  if (begin_ == nullptr || begin_ > reinterpret_cast<uint8_t*>(obj)) {
+    begin_ = reinterpret_cast<uint8_t*>(obj);
   }
+  end_ = std::max(end_, reinterpret_cast<uint8_t*>(obj) + allocation_size);
+
   *bytes_allocated = allocation_size;
   if (usable_size != nullptr) {
     *usable_size = allocation_size;
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 6109ec6..130c10d 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -58,17 +58,15 @@
   }
 }
 
-IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
-                                               size_t maxCount, IndirectRefKind desiredKind,
+IndirectReferenceTable::IndirectReferenceTable(size_t max_count,
+                                               IndirectRefKind desired_kind,
                                                bool abort_on_error)
-    : kind_(desiredKind),
-      max_entries_(maxCount) {
-  CHECK_GT(initialCount, 0U);
-  CHECK_LE(initialCount, maxCount);
-  CHECK_NE(desiredKind, kHandleScopeOrInvalid);
+    : kind_(desired_kind),
+      max_entries_(max_count) {
+  CHECK_NE(desired_kind, kHandleScopeOrInvalid);
 
   std::string error_str;
-  const size_t table_bytes = maxCount * sizeof(IrtEntry);
+  const size_t table_bytes = max_count * sizeof(IrtEntry);
   table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
                                             PROT_READ | PROT_WRITE, false, false, &error_str));
   if (abort_on_error) {
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 64de7a8..1762b10 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -259,8 +259,7 @@
  public:
   // WARNING: When using with abort_on_error = false, the object may be in a partially
   //          initialized state. Use IsValid() to check.
-  IndirectReferenceTable(size_t initialCount, size_t maxCount, IndirectRefKind kind,
-                         bool abort_on_error = true);
+  IndirectReferenceTable(size_t max_count, IndirectRefKind kind, bool abort_on_error = true);
 
   ~IndirectReferenceTable();
 
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 1699110..7b28f0b 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -48,9 +48,8 @@
   ScopedLogSeverity sls(LogSeverity::FATAL);
 
   ScopedObjectAccess soa(Thread::Current());
-  static const size_t kTableInitial = 10;
   static const size_t kTableMax = 20;
-  IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal);
+  IndirectReferenceTable irt(kTableMax, kGlobal);
 
   mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
   StackHandleScope<4> hs(soa.Self());
@@ -230,6 +229,7 @@
 
   // Test table resizing.
   // These ones fit...
+  static const size_t kTableInitial = kTableMax / 2;
   IndirectRef manyRefs[kTableInitial];
   for (size_t i = 0; i < kTableInitial; i++) {
     manyRefs[i] = irt.Add(cookie, obj0.Get());
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index a73970b..36e8608 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -647,7 +647,15 @@
   } else {
     MutexLock mu(self, *Locks::runtime_shutdown_lock_);
     SetQuickAllocEntryPointsInstrumented(instrumented);
-    ResetQuickAllocEntryPoints();
+
+    // Note: ResetQuickAllocEntryPoints only works when the runtime is started. Manually run the
+    //       update for just this thread.
+    // Note: self may be null. One of those paths is setting instrumentation in the Heap
+    //       constructor for gcstress mode.
+    if (self != nullptr) {
+      ResetQuickAllocEntryPointsForThread(self, nullptr);
+    }
+
     alloc_entrypoints_instrumented_ = instrumented;
   }
 }
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4a3654b..4347c37 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1472,13 +1472,17 @@
     uint32_t* args, JValue* result) {
   int32_t length = args[1];
   DCHECK_GE(length, 0);
-  mirror::Class* element_class = reinterpret_cast<mirror::Object*>(args[0])->AsClass();
+  ObjPtr<mirror::Class> element_class = reinterpret_cast<mirror::Object*>(args[0])->AsClass();
   Runtime* runtime = Runtime::Current();
-  mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(self, &element_class);
+  ObjPtr<mirror::Class> array_class =
+      runtime->GetClassLinker()->FindArrayClass(self, &element_class);
   DCHECK(array_class != nullptr);
   gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
-  result->SetL(mirror::Array::Alloc<true, true>(self, array_class, length,
-                                                array_class->GetComponentSizeShift(), allocator));
+  result->SetL(mirror::Array::Alloc<true, true>(self,
+                                                array_class,
+                                                length,
+                                                array_class->GetComponentSizeShift(),
+                                                allocator));
 }
 
 void UnstartedRuntime::UnstartedJNIVMStackGetCallingClassLoader(
@@ -1601,10 +1605,10 @@
     ThrowNegativeArraySizeException(length);
     return;
   }
-  mirror::Class* element_class = reinterpret_cast<mirror::Class*>(args[0])->AsClass();
+  ObjPtr<mirror::Class> element_class = reinterpret_cast<mirror::Class*>(args[0])->AsClass();
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
-  mirror::Class* array_class = class_linker->FindArrayClass(self, &element_class);
+  ObjPtr<mirror::Class> array_class = class_linker->FindArrayClass(self, &element_class);
   if (UNLIKELY(array_class == nullptr)) {
     CHECK(self->IsExceptionPending());
     return;
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 6a4add3..b190c81 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -81,20 +81,21 @@
 
   static mirror::ObjectArray<mirror::Object>* CreateObjectArray(
       Thread* self,
-      mirror::Class* component_type,
+      ObjPtr<mirror::Class> component_type,
       const StackHandleScope<3>& data)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     Runtime* runtime = Runtime::Current();
-    mirror::Class* array_type = runtime->GetClassLinker()->FindArrayClass(self, &component_type);
+    ObjPtr<mirror::Class> array_type =
+        runtime->GetClassLinker()->FindArrayClass(self, &component_type);
     CHECK(array_type != nullptr);
-    mirror::ObjectArray<mirror::Object>* result =
+    ObjPtr<mirror::ObjectArray<mirror::Object>> result =
         mirror::ObjectArray<mirror::Object>::Alloc(self, array_type, 3);
     CHECK(result != nullptr);
     for (size_t i = 0; i < 3; ++i) {
       result->Set(static_cast<int32_t>(i), data.GetReference(i));
       CHECK(!self->IsExceptionPending());
     }
-    return result;
+    return result.Ptr();
   }
 
   static void CheckObjectArray(mirror::ObjectArray<mirror::Object>* array,
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index f2bda05..99edb7c 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -42,11 +42,9 @@
 
 namespace art {
 
-static size_t gGlobalsInitial = 512;  // Arbitrary.
-static size_t gGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
+static constexpr size_t kGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
 
-static const size_t kWeakGlobalsInitial = 16;  // Arbitrary.
-static const size_t kWeakGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
+static constexpr size_t kWeakGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
 
 bool JavaVMExt::IsBadJniVersion(int version) {
   // We don't support JNI_VERSION_1_1. These are the only other valid versions.
@@ -422,10 +420,10 @@
       tracing_enabled_(runtime_options.Exists(RuntimeArgumentMap::JniTrace)
                        || VLOG_IS_ON(third_party_jni)),
       trace_(runtime_options.GetOrDefault(RuntimeArgumentMap::JniTrace)),
-      globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
+      globals_(kGlobalsMax, kGlobal),
       libraries_(new Libraries),
       unchecked_functions_(&gJniInvokeInterface),
-      weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
+      weak_globals_(kWeakGlobalsMax, kWeakGlobal),
       allow_accessing_weak_globals_(true),
       weak_globals_add_condition_("weak globals add condition",
                                   (CHECK(Locks::jni_weak_globals_lock_ != nullptr),
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 3c749d0..39f4d97 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -33,8 +33,6 @@
 static constexpr size_t kMonitorsInitial = 32;  // Arbitrary.
 static constexpr size_t kMonitorsMax = 4096;  // Arbitrary sanity check.
 
-static constexpr size_t kLocalsInitial = 64;  // Arbitrary.
-
 // Checking "locals" requires the mutator lock, but at creation time we're really only interested
 // in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged
 // with NO_THREAD_SAFETY_ANALYSIS.
@@ -71,7 +69,7 @@
     : self(self_in),
       vm(vm_in),
       local_ref_cookie(IRT_FIRST_SEGMENT),
-      locals(kLocalsInitial, kLocalsMax, kLocal, false),
+      locals(kLocalsInitial, kLocal, false),
       check_jni(false),
       runtime_deleted(false),
       critical(0),
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 121f848..549f8c5 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -29,9 +29,9 @@
 
 class JavaVMExt;
 
-// Maximum number of local references in the indirect reference table. The value is arbitrary but
+// Number of local references in the indirect reference table. The value is arbitrary but
 // low enough that it forces sanity checks.
-static constexpr size_t kLocalsMax = 512;
+static constexpr size_t kLocalsInitial = 512;
 
 struct JNIEnvExt : public JNIEnv {
   static JNIEnvExt* Create(Thread* self, JavaVMExt* vm);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 8eebe56..db2453d 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -1904,11 +1904,12 @@
 
     // Compute the array class corresponding to the given element class.
     ScopedObjectAccess soa(env);
-    mirror::Class* array_class;
+    ObjPtr<mirror::Class> array_class;
     {
-      mirror::Class* element_class = soa.Decode<mirror::Class>(element_jclass).Ptr();
+      ObjPtr<mirror::Class> element_class = soa.Decode<mirror::Class>(element_jclass).Ptr();
       if (UNLIKELY(element_class->IsPrimitive())) {
-        soa.Vm()->JniAbortF("NewObjectArray", "not an object type: %s",
+        soa.Vm()->JniAbortF("NewObjectArray",
+                            "not an object type: %s",
                             PrettyDescriptor(element_class).c_str());
         return nullptr;
       }
@@ -2394,13 +2395,13 @@
                                           const char* caller)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // TODO: we should try to expand the table if necessary.
-    if (desired_capacity < 0 || desired_capacity > static_cast<jint>(kLocalsMax)) {
+    if (desired_capacity < 0 || desired_capacity > static_cast<jint>(kLocalsInitial)) {
       LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity;
       return JNI_ERR;
     }
     // TODO: this isn't quite right, since "capacity" includes holes.
     const size_t capacity = soa.Env()->locals.Capacity();
-    bool okay = (static_cast<jint>(kLocalsMax - capacity) >= desired_capacity);
+    bool okay = (static_cast<jint>(kLocalsInitial - capacity) >= desired_capacity);
     if (!okay) {
       soa.Self()->ThrowOutOfMemoryError(caller);
     }
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index fbd670c..9622a8e 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -865,6 +865,11 @@
   GetStaticMethodIdBadArgumentTest(true);
 }
 
+static size_t GetLocalsCapacity(JNIEnv* env) {
+  ScopedObjectAccess soa(Thread::Current());
+  return reinterpret_cast<JNIEnvExt*>(env)->locals.Capacity();
+}
+
 TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
   jclass jlrField = env_->FindClass("java/lang/reflect/Field");
   jclass c = env_->FindClass("java/lang/String");
@@ -873,11 +878,15 @@
   ASSERT_NE(fid, nullptr);
   // Turn the fid into a java.lang.reflect.Field...
   jobject field = env_->ToReflectedField(c, fid, JNI_FALSE);
-  for (size_t i = 0; i <= kLocalsMax; ++i) {
+  size_t capacity_before = GetLocalsCapacity(env_);
+  for (size_t i = 0; i <= 10; ++i) {
     // Regression test for b/18396311, ToReflectedField leaking local refs causing a local
     // reference table overflows with 512 references to ArtField
     env_->DeleteLocalRef(env_->ToReflectedField(c, fid, JNI_FALSE));
   }
+  size_t capacity_after = GetLocalsCapacity(env_);
+  ASSERT_EQ(capacity_before, capacity_after);
+
   ASSERT_NE(c, nullptr);
   ASSERT_TRUE(env_->IsInstanceOf(field, jlrField));
   // ...and back again.
@@ -911,11 +920,14 @@
   ASSERT_NE(mid, nullptr);
   // Turn the mid into a java.lang.reflect.Constructor...
   jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE);
-  for (size_t i = 0; i <= kLocalsMax; ++i) {
+  size_t capacity_before = GetLocalsCapacity(env_);
+  for (size_t i = 0; i <= 10; ++i) {
     // Regression test for b/18396311, ToReflectedMethod leaking local refs causing a local
     // reference table overflows with 512 references to ArtMethod
     env_->DeleteLocalRef(env_->ToReflectedMethod(c, mid, JNI_FALSE));
   }
+  size_t capacity_after = GetLocalsCapacity(env_);
+  ASSERT_EQ(capacity_before, capacity_after);
   ASSERT_NE(method, nullptr);
   ASSERT_TRUE(env_->IsInstanceOf(method, jlrConstructor));
   // ...and back again.
@@ -2295,7 +2307,7 @@
   // The segment_state_ field is private, and we want to avoid friend declaration. So we'll check
   // by modifying memory.
   // The parameters don't really matter here.
-  IndirectReferenceTable irt(5, 5, IndirectRefKind::kGlobal, true);
+  IndirectReferenceTable irt(5, IndirectRefKind::kGlobal, true);
   uint32_t old_state = irt.GetSegmentState();
 
   // Write some new state directly. We invert parts of old_state to ensure a new value.
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 1aa38dd..8afa4aa 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -60,7 +60,7 @@
     for (int32_t i = 0; i < array_length; i++) {
       StackHandleScope<1> hs2(self);
       Handle<mirror::Class> h_component_type(hs2.NewHandle(array_class->GetComponentType()));
-      Array* sub_array = RecursiveCreateMultiArray(self, h_component_type,
+      ObjPtr<Array> sub_array = RecursiveCreateMultiArray(self, h_component_type,
                                                    current_dimension + 1, dimensions);
       if (UNLIKELY(sub_array == nullptr)) {
         CHECK(self->IsExceptionPending());
@@ -93,7 +93,7 @@
 
   // Find/generate the array class.
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  mirror::Class* element_class_ptr = element_class.Get();
+  ObjPtr<mirror::Class>  element_class_ptr = element_class.Get();
   StackHandleScope<1> hs(self);
   MutableHandle<mirror::Class> array_class(
       hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr)));
@@ -102,7 +102,7 @@
     return nullptr;
   }
   for (int32_t i = 1; i < dimensions->GetLength(); ++i) {
-    mirror::Class* array_class_ptr = array_class.Get();
+    ObjPtr<mirror::Class> array_class_ptr = array_class.Get();
     array_class.Assign(class_linker->FindArrayClass(self, &array_class_ptr));
     if (UNLIKELY(array_class.Get() == nullptr)) {
       CHECK(self->IsExceptionPending());
@@ -110,11 +110,11 @@
     }
   }
   // Create the array.
-  Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions);
+  ObjPtr<Array> new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions);
   if (UNLIKELY(new_array == nullptr)) {
     CHECK(self->IsExceptionPending());
   }
-  return new_array;
+  return new_array.Ptr();
 }
 
 void Array::ThrowArrayIndexOutOfBoundsException(int32_t index) {
@@ -136,12 +136,13 @@
       heap->GetCurrentNonMovingAllocator();
   const auto component_size = GetClass()->GetComponentSize();
   const auto component_shift = GetClass()->GetComponentSizeShift();
-  Array* new_array = Alloc<true>(self, GetClass(), new_length, component_shift, allocator_type);
+  ObjPtr<Array> new_array = Alloc<true>(self, GetClass(), new_length, component_shift, allocator_type);
   if (LIKELY(new_array != nullptr)) {
-    memcpy(new_array->GetRawData(component_size, 0), h_this->GetRawData(component_size, 0),
+    memcpy(new_array->GetRawData(component_size, 0),
+           h_this->GetRawData(component_size, 0),
            std::min(h_this->GetLength(), new_length) << component_shift);
   }
-  return new_array;
+  return new_array.Ptr();
 }
 
 
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 86b5e7a..be849a3 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -44,8 +44,8 @@
   return StringDexCachePair::Lookup(GetStrings(), string_idx, NumStrings()).Read();
 }
 
-inline void DexCache::SetResolvedString(uint32_t string_idx, mirror::String* resolved) {
-  StringDexCachePair::Assign(GetStrings(), string_idx, resolved, NumStrings());
+inline void DexCache::SetResolvedString(uint32_t string_idx, ObjPtr<mirror::String> resolved) {
+  StringDexCachePair::Assign(GetStrings(), string_idx, resolved.Ptr(), NumStrings());
   Runtime* const runtime = Runtime::Current();
   if (UNLIKELY(runtime->IsActiveTransaction())) {
     DCHECK(runtime->IsAotCompiler());
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index c3c7ab4..d728f90 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -215,7 +215,7 @@
   mirror::String* GetResolvedString(uint32_t string_idx) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetResolvedString(uint32_t string_idx, mirror::String* resolved) ALWAYS_INLINE
+  void SetResolvedString(uint32_t string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Clear a string for a string_idx, used to undo string intern transactions to make sure
diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc
index a968bff..03ab930 100644
--- a/runtime/mirror/method_type_test.cc
+++ b/runtime/mirror/method_type_test.cc
@@ -52,7 +52,7 @@
           soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader));
   CHECK(return_clazz.Get() != nullptr);
 
-  mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
   mirror::Class* class_array_type = class_linker->FindArrayClass(self, &class_type);
   Handle<mirror::ObjectArray<mirror::Class>> param_classes = hs.NewHandle(
       mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, param_types.size()));
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index ed1103f..d60274f 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -292,38 +292,41 @@
   if (lhs == rhs) {
     return 0;
   }
-  // TODO: is this still true?
-  // The annoying part here is that 0x00e9 - 0xffff != 0x00ea,
-  // because the interpreter converts the characters to 32-bit integers
-  // *without* sign extension before it subtracts them (which makes some
-  // sense since "char" is unsigned).  So what we get is the result of
-  // 0x000000e9 - 0x0000ffff, which is 0xffff00ea.
-  int32_t lhsCount = lhs->GetLength();
-  int32_t rhsCount = rhs->GetLength();
-  int32_t countDiff = lhsCount - rhsCount;
-  int32_t minCount = (countDiff < 0) ? lhsCount : rhsCount;
+  int32_t lhs_count = lhs->GetLength();
+  int32_t rhs_count = rhs->GetLength();
+  int32_t count_diff = lhs_count - rhs_count;
+  int32_t min_count = (count_diff < 0) ? lhs_count : rhs_count;
   if (lhs->IsCompressed() && rhs->IsCompressed()) {
-    int32_t comparison = memcmp(lhs->GetValueCompressed(),
-                                rhs->GetValueCompressed(),
-                                minCount * sizeof(uint8_t));
-    if (comparison != 0) {
-      return comparison;
+    const uint8_t* lhs_chars = lhs->GetValueCompressed();
+    const uint8_t* rhs_chars = rhs->GetValueCompressed();
+    for (int32_t i = 0; i < min_count; ++i) {
+      int32_t char_diff = static_cast<int32_t>(lhs_chars[i]) - static_cast<int32_t>(rhs_chars[i]);
+      if (char_diff != 0) {
+        return char_diff;
+      }
     }
   } else if (lhs->IsCompressed() || rhs->IsCompressed()) {
-    for (int32_t i = 0; i < minCount; ++i) {
-      if (lhs->CharAt(i) != rhs->CharAt(i)) {
-        return static_cast<int32_t>(lhs->CharAt(i)) - static_cast<int32_t>(rhs->CharAt(i));
+    const uint8_t* compressed_chars =
+        lhs->IsCompressed() ? lhs->GetValueCompressed() : rhs->GetValueCompressed();
+    const uint16_t* uncompressed_chars = lhs->IsCompressed() ? rhs->GetValue() : lhs->GetValue();
+    for (int32_t i = 0; i < min_count; ++i) {
+      int32_t char_diff =
+          static_cast<int32_t>(compressed_chars[i]) - static_cast<int32_t>(uncompressed_chars[i]);
+      if (char_diff != 0) {
+        return lhs->IsCompressed() ? char_diff : -char_diff;
       }
     }
   } else {
-    const uint16_t* lhsChars = lhs->GetValue();
-    const uint16_t* rhsChars = rhs->GetValue();
-    int32_t otherRes = MemCmp16(lhsChars, rhsChars, minCount);
-    if (otherRes != 0) {
-      return otherRes;
+    const uint16_t* lhs_chars = lhs->GetValue();
+    const uint16_t* rhs_chars = rhs->GetValue();
+    // FIXME: The MemCmp16() name is misleading. It returns the char difference on mismatch
+    // where memcmp() only guarantees that the returned value has the same sign.
+    int32_t char_diff = MemCmp16(lhs_chars, rhs_chars, min_count);
+    if (char_diff != 0) {
+      return char_diff;
     }
   }
-  return countDiff;
+  return count_diff;
 }
 
 void String::VisitRoots(RootVisitor* visitor) {
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 086da60..df0849a 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -218,7 +218,7 @@
   {
     ScopedObjectAccess soa(env);
     ObjPtr<mirror::Object> dex_files_object = soa.Decode<mirror::Object>(cookie);
-    mirror::LongArray* long_dex_files = dex_files_object->AsLongArray();
+    ObjPtr<mirror::LongArray> long_dex_files = dex_files_object->AsLongArray();
     // Delete dex files associated with this dalvik.system.DexFile since there should not be running
     // code using it. dex_files is a vector due to multidex.
     ClassLinker* const class_linker = runtime->GetClassLinker();
@@ -279,15 +279,15 @@
       Handle<mirror::ClassLoader> class_loader(
           hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
       class_linker->RegisterDexFile(*dex_file, class_loader.Get());
-      mirror::Class* result = class_linker->DefineClass(soa.Self(),
-                                                        descriptor.c_str(),
-                                                        hash,
-                                                        class_loader,
-                                                        *dex_file,
-                                                        *dex_class_def);
+      ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
+                                                               descriptor.c_str(),
+                                                               hash,
+                                                               class_loader,
+                                                               *dex_file,
+                                                               *dex_class_def);
       // Add the used dex file. This only required for the DexFile.loadClass API since normal
       // class loaders already keep their dex files live.
-      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile).Ptr(),
+      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
                                                  class_loader.Get());
       if (result != nullptr) {
         VLOG(class_linker) << "DexFile_defineClassNative returning " << result
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
index e32545b..db245aa 100644
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
@@ -150,14 +150,18 @@
     Handle<mirror::ClassLoader> class_loader(
         handle_scope.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));
     class_linker->RegisterDexFile(*dex_file, class_loader.Get());
-    mirror::Class* result = class_linker->DefineClass(
-        soa.Self(), class_descriptor, hash, class_loader, *dex_file, *dex_class_def);
+    ObjPtr<mirror::Class> result = class_linker->DefineClass(
+        soa.Self(),
+        class_descriptor,
+        hash, class_loader,
+        *dex_file,
+        *dex_class_def);
     if (result != nullptr) {
       // Ensure the class table has a strong reference to the
       // InMemoryClassLoader/DexData instance now that a class has
       // been loaded.
-      class_linker->InsertDexFileInToClassLoader(
-          soa.Decode<mirror::Object>(dexData).Ptr(), class_loader.Get());
+      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexData),
+                                                 class_loader.Get());
       return soa.AddLocalReference<jclass>(result);
     }
   }
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index e458e2d..888fddb 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -74,21 +74,23 @@
     ThrowNegativeArraySizeException(length);
     return nullptr;
   }
-  mirror::Class* element_class = soa.Decode<mirror::Class>(javaElementClass).Ptr();
+  ObjPtr<mirror::Class> element_class = soa.Decode<mirror::Class>(javaElementClass);
   if (UNLIKELY(element_class == nullptr)) {
     ThrowNullPointerException("element class == null");
     return nullptr;
   }
   Runtime* runtime = Runtime::Current();
-  mirror::Class* array_class =
+  ObjPtr<mirror::Class> array_class =
       runtime->GetClassLinker()->FindArrayClass(soa.Self(), &element_class);
   if (UNLIKELY(array_class == nullptr)) {
     return nullptr;
   }
   gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator();
-  mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
-                                                     array_class->GetComponentSizeShift(),
-                                                     allocator);
+  ObjPtr<mirror::Array> result = mirror::Array::Alloc<true>(soa.Self(),
+                                                            array_class,
+                                                            length,
+                                                            array_class->GetComponentSizeShift(),
+                                                            allocator);
   return soa.AddLocalReference<jobject>(result);
 }
 
@@ -99,21 +101,24 @@
     ThrowNegativeArraySizeException(length);
     return nullptr;
   }
-  mirror::Class* element_class = soa.Decode<mirror::Class>(javaElementClass).Ptr();
+  ObjPtr<mirror::Class> element_class = soa.Decode<mirror::Class>(javaElementClass);
   if (UNLIKELY(element_class == nullptr)) {
     ThrowNullPointerException("element class == null");
     return nullptr;
   }
   Runtime* runtime = Runtime::Current();
-  mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(),
-                                                                         &element_class);
+  ObjPtr<mirror::Class> array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(),
+                                                                                &element_class);
   if (UNLIKELY(array_class == nullptr)) {
     return nullptr;
   }
   gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
-  mirror::Array* result = mirror::Array::Alloc<true, true>(soa.Self(), array_class, length,
-                                                           array_class->GetComponentSizeShift(),
-                                                           allocator);
+  ObjPtr<mirror::Array> result = mirror::Array::Alloc<true, true>(
+      soa.Self(),
+      array_class,
+      length,
+      array_class->GetComponentSizeShift(),
+      allocator);
   return soa.AddLocalReference<jobject>(result);
 }
 
@@ -127,7 +132,7 @@
     ThrowIllegalArgumentException("not an array");
     return 0;
   }
-  if (Runtime::Current()->GetHeap()->IsMovableObject(array.Ptr())) {
+  if (Runtime::Current()->GetHeap()->IsMovableObject(array)) {
     ThrowRuntimeException("Trying to get address of movable array object");
     return 0;
   }
@@ -263,7 +268,7 @@
   Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env));
 }
 
-typedef std::map<std::string, mirror::String*> StringTable;
+typedef std::map<std::string, ObjPtr<mirror::String>> StringTable;
 
 class PreloadDexCachesStringsVisitor : public SingleRootVisitor {
  public:
@@ -271,7 +276,7 @@
 
   void VisitRoot(mirror::Object* root, const RootInfo& info ATTRIBUTE_UNUSED)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::String* string = root->AsString();
+    ObjPtr<mirror::String> string = root->AsString();
     table_->operator[](string->ToModifiedUtf8()) = string;
   }
 
@@ -283,7 +288,7 @@
 static void PreloadDexCachesResolveString(
     Handle<mirror::DexCache> dex_cache, uint32_t string_idx, StringTable& strings)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::String* string = dex_cache->GetResolvedString(string_idx);
+  ObjPtr<mirror::String>  string = dex_cache->GetResolvedString(string_idx);
   if (string != nullptr) {
     return;
   }
@@ -298,10 +303,11 @@
 }
 
 // Based on ClassLinker::ResolveType.
-static void PreloadDexCachesResolveType(
-    Thread* self, mirror::DexCache* dex_cache, uint32_t type_idx)
+static void PreloadDexCachesResolveType(Thread* self,
+                                        ObjPtr<mirror::DexCache> dex_cache,
+                                        uint32_t type_idx)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
+  ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(type_idx);
   if (klass != nullptr) {
     return;
   }
@@ -364,7 +370,7 @@
   }
   const DexFile* dex_file = dex_cache->GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx);
-  mirror::Class* klass = dex_cache->GetResolvedType(method_id.class_idx_);
+  ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(method_id.class_idx_);
   if (klass == nullptr) {
     return;
   }
@@ -439,25 +445,25 @@
   Thread* const self = Thread::Current();
   for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
     CHECK(dex_file != nullptr);
-    mirror::DexCache* const dex_cache = class_linker->FindDexCache(self, *dex_file, true);
+    ObjPtr<mirror::DexCache> const dex_cache = class_linker->FindDexCache(self, *dex_file, true);
     // If dex cache was deallocated, just continue.
     if (dex_cache == nullptr) {
       continue;
     }
     for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
-      mirror::String* string = dex_cache->GetResolvedString(j);
+      ObjPtr<mirror::String> string = dex_cache->GetResolvedString(j);
       if (string != nullptr) {
         filled->num_strings++;
       }
     }
     for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) {
-      mirror::Class* klass = dex_cache->GetResolvedType(j);
+      ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(j);
       if (klass != nullptr) {
         filled->num_types++;
       }
     }
     for (size_t j = 0; j < dex_cache->NumResolvedFields(); j++) {
-      ArtField* field = class_linker->GetResolvedField(j, dex_cache);
+      ArtField* field = class_linker->GetResolvedField(j, dex_cache.Ptr());
       if (field != nullptr) {
         filled->num_fields++;
       }
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 0dd8cdd..36825cb 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -87,10 +87,10 @@
 
     bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
       DCHECK(class_loader == nullptr);
-      mirror::Class* c = GetMethod()->GetDeclaringClass();
+      ObjPtr<mirror::Class> c = GetMethod()->GetDeclaringClass();
       // c is null for runtime methods.
       if (c != nullptr) {
-        mirror::Object* cl = c->GetClassLoader();
+        ObjPtr<mirror::Object> cl = c->GetClassLoader();
         if (cl != nullptr) {
           class_loader = cl;
           return false;
@@ -99,7 +99,7 @@
       return true;
     }
 
-    mirror::Object* class_loader;
+    ObjPtr<mirror::Object> class_loader;
   };
   ScopedFastNativeObjectAccess soa(env);
   ClosestUserClassLoaderVisitor visitor(soa.Self());
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index ceb37c4..ac5dbda 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -497,13 +497,13 @@
       // Pending exception from GetDeclaredClasses.
       return nullptr;
     }
-    mirror::Class* class_class = mirror::Class::GetJavaLangClass();
-    mirror::Class* class_array_class =
+    ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
+    ObjPtr<mirror::Class> class_array_class =
         Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class);
     if (class_array_class == nullptr) {
       return nullptr;
     }
-    mirror::ObjectArray<mirror::Class>* empty_array =
+    ObjPtr<mirror::ObjectArray<mirror::Class>> empty_array =
         mirror::ObjectArray<mirror::Class>::Alloc(soa.Self(), class_array_class, 0);
     return soa.AddLocalReference<jobjectArray>(empty_array);
   }
@@ -527,7 +527,7 @@
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
-  mirror::Object* method = annotations::GetEnclosingMethod(klass);
+  ObjPtr<mirror::Object> method = annotations::GetEnclosingMethod(klass);
   if (method != nullptr) {
     if (soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Constructor) ==
         method->GetClass()) {
@@ -544,7 +544,7 @@
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
-  mirror::Object* method = annotations::GetEnclosingMethod(klass);
+  ObjPtr<mirror::Object> method = annotations::GetEnclosingMethod(klass);
   if (method != nullptr) {
     if (soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Method) ==
         method->GetClass()) {
@@ -660,7 +660,7 @@
   // Invoke the string allocator to return an empty string for the string class.
   if (klass->IsStringClass()) {
     gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-    mirror::Object* obj = mirror::String::AllocEmptyString<true>(soa.Self(), allocator_type);
+    ObjPtr<mirror::Object> obj = mirror::String::AllocEmptyString<true>(soa.Self(), allocator_type);
     if (UNLIKELY(soa.Self()->IsExceptionPending())) {
       return nullptr;
     } else {
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index 1fd7ed1..71379a5 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -68,7 +68,7 @@
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
-  dex_cache->SetResolvedType(type_index, soa.Decode<mirror::Class>(type).Ptr());
+  dex_cache->SetResolvedType(type_index, soa.Decode<mirror::Class>(type));
 }
 
 static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index,
@@ -76,7 +76,7 @@
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   CHECK_LT(static_cast<size_t>(string_index), dex_cache->GetDexFile()->NumStringIds());
-  dex_cache->SetResolvedString(string_index, soa.Decode<mirror::String>(string).Ptr());
+  dex_cache->SetResolvedString(string_index, soa.Decode<mirror::String>(string));
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 5a49c20..ea266d1 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -57,7 +57,8 @@
   int32_t length_this = string_this->GetLength();
   int32_t length_arg = string_arg->GetLength();
   if (length_arg > 0 && length_this > 0) {
-    mirror::String* result = mirror::String::AllocFromStrings(soa.Self(), string_this, string_arg);
+    ObjPtr<mirror::String> result =
+        mirror::String::AllocFromStrings(soa.Self(), string_this, string_arg);
     return soa.AddLocalReference<jstring>(result);
   }
   jobject string_original = (length_this == 0) ? java_string_arg : java_this;
@@ -76,8 +77,11 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::String> string_this(hs.NewHandle(soa.Decode<mirror::String>(java_this)));
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-  mirror::String* result = mirror::String::AllocFromString<true>(soa.Self(), length, string_this,
-                                                                 start, allocator_type);
+  ObjPtr<mirror::String> result = mirror::String::AllocFromString<true>(soa.Self(),
+                                                                        length,
+                                                                        string_this,
+                                                                        start,
+                                                                        allocator_type);
   return soa.AddLocalReference<jstring>(result);
 }
 
diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc
index 119f2b8..e0738a4 100644
--- a/runtime/native/java_lang_StringFactory.cc
+++ b/runtime/native/java_lang_StringFactory.cc
@@ -44,9 +44,12 @@
     return nullptr;
   }
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-  mirror::String* result = mirror::String::AllocFromByteArray<true>(soa.Self(), byte_count,
-                                                                    byte_array, offset, high,
-                                                                    allocator_type);
+  ObjPtr<mirror::String> result = mirror::String::AllocFromByteArray<true>(soa.Self(),
+                                                                           byte_count,
+                                                                           byte_array,
+                                                                           offset,
+                                                                           high,
+                                                                           allocator_type);
   return soa.AddLocalReference<jstring>(result);
 }
 
@@ -58,9 +61,11 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray>(java_data)));
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-  mirror::String* result = mirror::String::AllocFromCharArray<true>(soa.Self(), char_count,
-                                                                    char_array, offset,
-                                                                    allocator_type);
+  ObjPtr<mirror::String> result = mirror::String::AllocFromCharArray<true>(soa.Self(),
+                                                                           char_count,
+                                                                           char_array,
+                                                                           offset,
+                                                                           allocator_type);
   return soa.AddLocalReference<jstring>(result);
 }
 
@@ -73,8 +78,11 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(to_copy)));
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-  mirror::String* result = mirror::String::AllocFromString<true>(soa.Self(), string->GetLength(),
-                                                                 string, 0, allocator_type);
+  ObjPtr<mirror::String> result = mirror::String::AllocFromString<true>(soa.Self(),
+                                                                        string->GetLength(),
+                                                                        string,
+                                                                        0,
+                                                                        allocator_type);
   return soa.AddLocalReference<jstring>(result);
 }
 
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 3f5fa73..eaf2d65 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -71,8 +71,8 @@
     ThrowArrayStoreException_NotAnArray("destination", dstObject);
     return;
   }
-  mirror::Array* srcArray = srcObject->AsArray();
-  mirror::Array* dstArray = dstObject->AsArray();
+  ObjPtr<mirror::Array> srcArray = srcObject->AsArray();
+  ObjPtr<mirror::Array> dstArray = dstObject->AsArray();
 
   // Bounds checking.
   if (UNLIKELY(srcPos < 0) || UNLIKELY(dstPos < 0) || UNLIKELY(count < 0) ||
@@ -85,8 +85,8 @@
     return;
   }
 
-  mirror::Class* dstComponentType = dstArray->GetClass()->GetComponentType();
-  mirror::Class* srcComponentType = srcArray->GetClass()->GetComponentType();
+  ObjPtr<mirror::Class> dstComponentType = dstArray->GetClass()->GetComponentType();
+  ObjPtr<mirror::Class> srcComponentType = srcArray->GetClass()->GetComponentType();
   Primitive::Type dstComponentPrimitiveType = dstComponentType->GetPrimitiveType();
 
   if (LIKELY(srcComponentType == dstComponentType)) {
@@ -143,8 +143,10 @@
     return;
   }
   // Arrays hold distinct types and so therefore can't alias - use memcpy instead of memmove.
-  mirror::ObjectArray<mirror::Object>* dstObjArray = dstArray->AsObjectArray<mirror::Object>();
-  mirror::ObjectArray<mirror::Object>* srcObjArray = srcArray->AsObjectArray<mirror::Object>();
+  ObjPtr<mirror::ObjectArray<mirror::Object>> dstObjArray =
+      dstArray->AsObjectArray<mirror::Object>();
+  ObjPtr<mirror::ObjectArray<mirror::Object>> srcObjArray =
+      srcArray->AsObjectArray<mirror::Object>();
   // If we're assigning into say Object[] then we don't need per element checks.
   if (dstComponentType->IsAssignableFrom(srcComponentType)) {
     dstObjArray->AssignableMemcpy(dstPos, srcObjArray, srcPos, count);
@@ -157,8 +159,9 @@
 
 // Template to convert general array to that of its specific primitive type.
 template <typename T>
-inline T* AsPrimitiveArray(mirror::Array* array) {
-  return down_cast<T*>(array);
+inline ObjPtr<T> AsPrimitiveArray(ObjPtr<mirror::Array> array)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return ObjPtr<T>::DownCast(array);
 }
 
 template <typename T, Primitive::Type kPrimType>
@@ -168,8 +171,8 @@
   ObjPtr<mirror::Object> srcObject = soa.Decode<mirror::Object>(javaSrc);
   ObjPtr<mirror::Object> dstObject = soa.Decode<mirror::Object>(javaDst);
   DCHECK(dstObject != nullptr);
-  mirror::Array* srcArray = srcObject->AsArray();
-  mirror::Array* dstArray = dstObject->AsArray();
+  ObjPtr<mirror::Array> srcArray = srcObject->AsArray();
+  ObjPtr<mirror::Array> dstArray = dstObject->AsArray();
   DCHECK_GE(count, 0);
   DCHECK_EQ(srcArray->GetClass(), dstArray->GetClass());
   DCHECK_EQ(srcArray->GetClass()->GetComponentType()->GetPrimitiveType(), kPrimType);
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 73d12f1..1fe89bf 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -37,24 +37,24 @@
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   std::string descriptor(DotToDescriptor(name.c_str()));
   const size_t descriptor_hash = ComputeModifiedUtf8Hash(descriptor.c_str());
-  mirror::Class* c = cl->LookupClass(soa.Self(),
-                                     descriptor.c_str(),
-                                     descriptor_hash,
-                                     loader.Ptr());
+  ObjPtr<mirror::Class>  c = cl->LookupClass(soa.Self(),
+                                             descriptor.c_str(),
+                                             descriptor_hash,
+                                             loader.Ptr());
   if (c != nullptr && c->IsResolved()) {
     return soa.AddLocalReference<jclass>(c);
   }
   // If class is erroneous, throw the earlier failure, wrapped in certain cases. See b/28787733.
   if (c != nullptr && c->IsErroneous()) {
-    cl->ThrowEarlierClassFailure(c);
+    cl->ThrowEarlierClassFailure(c.Ptr());
     Thread* self = soa.Self();
-    mirror::Class* eiie_class =
+    ObjPtr<mirror::Class> eiie_class =
         self->DecodeJObject(WellKnownClasses::java_lang_ExceptionInInitializerError)->AsClass();
-    mirror::Class* iae_class =
+    ObjPtr<mirror::Class> iae_class =
         self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
-    mirror::Class* ncdfe_class =
+    ObjPtr<mirror::Class> ncdfe_class =
         self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
-    mirror::Class* exception = self->GetException()->GetClass();
+    ObjPtr<mirror::Class> exception = self->GetException()->GetClass();
     if (exception == eiie_class || exception == iae_class || exception == ncdfe_class) {
       self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
                                      PrettyDescriptor(c).c_str());
diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc
index 1f03c7c..c7d06f4 100644
--- a/runtime/native/java_lang_ref_FinalizerReference.cc
+++ b/runtime/native/java_lang_ref_FinalizerReference.cc
@@ -28,8 +28,7 @@
 static jboolean FinalizerReference_makeCircularListIfUnenqueued(JNIEnv* env, jobject javaThis) {
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::FinalizerReference> ref = soa.Decode<mirror::FinalizerReference>(javaThis);
-  return Runtime::Current()->GetHeap()->GetReferenceProcessor()->MakeCircularListIfUnenqueued(
-      ref.Ptr());
+  return Runtime::Current()->GetHeap()->GetReferenceProcessor()->MakeCircularListIfUnenqueued(ref);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 6f2da33..d827f81 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -40,8 +40,9 @@
   DCHECK_EQ(dimensions_obj->GetClass()->GetComponentType()->GetPrimitiveType(),
             Primitive::kPrimInt);
   Handle<mirror::IntArray> dimensions_array(
-      hs.NewHandle(down_cast<mirror::IntArray*>(dimensions_obj.Ptr())));
-  mirror::Array* new_array = mirror::Array::CreateMultiArray(soa.Self(), element_class,
+      hs.NewHandle(ObjPtr<mirror::IntArray>::DownCast(dimensions_obj)));
+  mirror::Array* new_array = mirror::Array::CreateMultiArray(soa.Self(),
+                                                             element_class,
                                                              dimensions_array);
   return soa.AddLocalReference<jobject>(new_array);
 }
@@ -53,17 +54,20 @@
     ThrowNegativeArraySizeException(length);
     return nullptr;
   }
-  mirror::Class* element_class = soa.Decode<mirror::Class>(javaElementClass).Ptr();
+  ObjPtr<mirror::Class> element_class = soa.Decode<mirror::Class>(javaElementClass);
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
-  mirror::Class* array_class = class_linker->FindArrayClass(soa.Self(), &element_class);
+  ObjPtr<mirror::Class> array_class = class_linker->FindArrayClass(soa.Self(), &element_class);
   if (UNLIKELY(array_class == nullptr)) {
     CHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
   DCHECK(array_class->IsObjectArrayClass());
-  mirror::Array* new_array = mirror::ObjectArray<mirror::Object*>::Alloc(
-      soa.Self(), array_class, length, runtime->GetHeap()->GetCurrentAllocator());
+  ObjPtr<mirror::Array> new_array = mirror::ObjectArray<mirror::Object*>::Alloc(
+      soa.Self(),
+      array_class,
+      length,
+      runtime->GetHeap()->GetCurrentAllocator());
   return soa.AddLocalReference<jobject>(new_array);
 }
 
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 505f85d..a81ba7d 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -39,13 +39,13 @@
       annotations::GetExceptionTypesForMethod(method);
   if (result_array == nullptr) {
     // Return an empty array instead of a null pointer.
-    mirror::Class* class_class = mirror::Class::GetJavaLangClass();
-    mirror::Class* class_array_class =
+    ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
+    ObjPtr<mirror::Class> class_array_class =
         Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class);
     if (class_array_class == nullptr) {
       return nullptr;
     }
-    mirror::ObjectArray<mirror::Class>* empty_array =
+    ObjPtr<mirror::ObjectArray<mirror::Class>> empty_array =
         mirror::ObjectArray<mirror::Class>::Alloc(soa.Self(), class_array_class, 0);
     return soa.AddLocalReference<jobjectArray>(empty_array);
   } else {
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index b5f2f7c..a6589bc 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -44,7 +44,7 @@
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
   if (method->GetDeclaringClass()->IsProxyClass()) {
-    mirror::Class* klass = method->GetDeclaringClass();
+    ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
     int throws_index = -1;
     size_t i = 0;
     for (const auto& m : klass->GetDeclaredVirtualMethods(kRuntimePointerSize)) {
@@ -62,8 +62,8 @@
         annotations::GetExceptionTypesForMethod(method);
     if (result_array == nullptr) {
       // Return an empty array instead of a null pointer
-      mirror::Class* class_class = mirror::Class::GetJavaLangClass();
-      mirror::Class* class_array_class =
+      ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
+      ObjPtr<mirror::Class> class_array_class =
           Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class);
       if (class_array_class == nullptr) {
         return nullptr;
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 670c4ac..cdf4b14 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -305,7 +305,8 @@
 }
 
 template<typename T>
-static void copyToArray(jlong srcAddr, mirror::PrimitiveArray<T>* array,
+static void copyToArray(jlong srcAddr,
+                        ObjPtr<mirror::PrimitiveArray<T>> array,
                         size_t array_offset,
                         size_t size)
         REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -318,7 +319,8 @@
 }
 
 template<typename T>
-static void copyFromArray(jlong dstAddr, mirror::PrimitiveArray<T>* array,
+static void copyFromArray(jlong dstAddr,
+                          ObjPtr<mirror::PrimitiveArray<T>> array,
                           size_t array_offset,
                           size_t size)
         REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -347,15 +349,15 @@
   size_t sz = (size_t)size;
   size_t dst_offset = (size_t)dstOffset;
   ObjPtr<mirror::Object> dst = soa.Decode<mirror::Object>(dstObj);
-  mirror::Class* component_type = dst->GetClass()->GetComponentType();
+  ObjPtr<mirror::Class> component_type = dst->GetClass()->GetComponentType();
   if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
-    copyToArray(srcAddr, dst->AsByteSizedArray(), dst_offset, sz);
+    copyToArray(srcAddr, MakeObjPtr(dst->AsByteSizedArray()), dst_offset, sz);
   } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
-    copyToArray(srcAddr, dst->AsShortSizedArray(), dst_offset, sz);
+    copyToArray(srcAddr, MakeObjPtr(dst->AsShortSizedArray()), dst_offset, sz);
   } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
-    copyToArray(srcAddr, dst->AsIntArray(), dst_offset, sz);
+    copyToArray(srcAddr, MakeObjPtr(dst->AsIntArray()), dst_offset, sz);
   } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
-    copyToArray(srcAddr, dst->AsLongArray(), dst_offset, sz);
+    copyToArray(srcAddr, MakeObjPtr(dst->AsLongArray()), dst_offset, sz);
   } else {
     ThrowIllegalAccessException("not a primitive array");
   }
@@ -378,15 +380,15 @@
   size_t sz = (size_t)size;
   size_t src_offset = (size_t)srcOffset;
   ObjPtr<mirror::Object> src = soa.Decode<mirror::Object>(srcObj);
-  mirror::Class* component_type = src->GetClass()->GetComponentType();
+  ObjPtr<mirror::Class> component_type = src->GetClass()->GetComponentType();
   if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
-    copyFromArray(dstAddr, src->AsByteSizedArray(), src_offset, sz);
+    copyFromArray(dstAddr, MakeObjPtr(src->AsByteSizedArray()), src_offset, sz);
   } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
-    copyFromArray(dstAddr, src->AsShortSizedArray(), src_offset, sz);
+    copyFromArray(dstAddr, MakeObjPtr(src->AsShortSizedArray()), src_offset, sz);
   } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
-    copyFromArray(dstAddr, src->AsIntArray(), src_offset, sz);
+    copyFromArray(dstAddr, MakeObjPtr(src->AsIntArray()), src_offset, sz);
   } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
-    copyFromArray(dstAddr, src->AsLongArray(), src_offset, sz);
+    copyFromArray(dstAddr, MakeObjPtr(src->AsLongArray()), src_offset, sz);
   } else {
     ThrowIllegalAccessException("not a primitive array");
   }
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 450f85e..59e01ea 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -40,6 +40,8 @@
 #include "mirror/object.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
 
 namespace openjdkjvmti {
 
@@ -172,6 +174,10 @@
 };
 
 static void SetupObjectAllocationTracking(art::gc::AllocationListener* listener, bool enable) {
+  // We must not hold the mutator lock here, but if we're in FastJNI, for example, we might. For
+  // now, do a workaround: (possibly) acquire and release.
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ScopedThreadSuspension sts(soa.Self(), art::ThreadState::kSuspended);
   if (enable) {
     art::Runtime::Current()->GetHeap()->SetAllocationListener(listener);
   } else {
@@ -213,6 +219,8 @@
     return ERR(INVALID_EVENT_TYPE);
   }
 
+  bool old_state = global_mask.Test(event);
+
   if (mode == JVMTI_ENABLE) {
     env->event_masks.EnableEvent(thread, event);
     global_mask.Set(event);
@@ -233,8 +241,12 @@
     global_mask.Set(event, union_value);
   }
 
+  bool new_state = global_mask.Test(event);
+
   // Handle any special work required for the event type.
-  HandleEventType(event, mode == JVMTI_ENABLE);
+  if (new_state != old_state) {
+    HandleEventType(event, mode == JVMTI_ENABLE);
+  }
 
   return ERR(NONE);
 }
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index a84668b..626d9cf 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -748,7 +748,7 @@
       DCHECK(result->IsObjectClass());
       return result;
     }
-    mirror::Class* common_elem = ClassJoin(s_ct, t_ct);
+    ObjPtr<mirror::Class> common_elem = ClassJoin(s_ct, t_ct);
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), &common_elem);
     DCHECK(array_class != nullptr);
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index d1ea0b1..a848fba 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -89,5 +89,424 @@
         Method fromUTF8ByteArray = Strings.getDeclaredMethod("fromUTF8ByteArray", byte[].class);
         String result = (String) fromUTF8ByteArray.invoke(null, new byte[] {'O', 'K'});
         System.out.println(result);
+
+        testCompareToAndEquals();
+        testIndexOf();
     }
+
+    public static void testCompareToAndEquals() {
+        String[] strings = {
+                // Special: empty string.
+                "",
+                // Category 0, ASCII strings:
+                //     "0123456789abcdef".substring(0, index + 1)
+                "0",
+                "01",
+                "012",
+                "0123",
+                "01234",
+                "012345",
+                "0123456",
+                "01234567",
+                "012345678",
+                "0123456789",
+                "0123456789a",
+                "0123456789ab",
+                "0123456789abc",
+                "0123456789abcd",
+                "0123456789abcde",
+                "0123456789abcdef",
+                // Category 1, ASCII strings:
+                //     "0123456789abcdef".substring(0, index) + "x"
+                "x",
+                "0x",
+                "01x",
+                "012x",
+                "0123x",
+                "01234x",
+                "012345x",
+                "0123456x",
+                "01234567x",
+                "012345678x",
+                "0123456789x",
+                "0123456789ax",
+                "0123456789abx",
+                "0123456789abcx",
+                "0123456789abcdx",
+                "0123456789abcdex",
+                // Category 2, ASCII strings,
+                //     "0123456789abcdef".substring(0, index) + "x" +
+                //     "0123456789abcdef".substring(index + 1)
+                "x123456789abcdef",
+                "0x23456789abcdef",
+                "01x3456789abcdef",
+                "012x456789abcdef",
+                "0123x56789abcdef",
+                "01234x6789abcdef",
+                "012345x789abcdef",
+                "0123456x89abcdef",
+                "01234567x9abcdef",
+                "012345678xabcdef",
+                "0123456789xbcdef",
+                "0123456789axcdef",
+                "0123456789abxdef",
+                "0123456789abcxef",
+                "0123456789abcdxf",
+                "0123456789abcdex",
+                // Category 3, ASCII strings:
+                //     "z" + "0123456789abcdef".substring(1, index + 1)
+                "z",
+                "z1",
+                "z12",
+                "z123",
+                "z1234",
+                "z12345",
+                "z123456",
+                "z1234567",
+                "z12345678",
+                "z123456789",
+                "z123456789a",
+                "z123456789ab",
+                "z123456789abc",
+                "z123456789abcd",
+                "z123456789abcde",
+                "z123456789abcdef",
+                // Category 4, non-ASCII strings:
+                //     "0123456789abcdef".substring(0, index) + "\u0440"
+                "\u0440",
+                "0\u0440",
+                "01\u0440",
+                "012\u0440",
+                "0123\u0440",
+                "01234\u0440",
+                "012345\u0440",
+                "0123456\u0440",
+                "01234567\u0440",
+                "012345678\u0440",
+                "0123456789\u0440",
+                "0123456789a\u0440",
+                "0123456789ab\u0440",
+                "0123456789abc\u0440",
+                "0123456789abcd\u0440",
+                "0123456789abcde\u0440",
+                // Category 5, non-ASCII strings:
+                //     "0123456789abcdef".substring(0, index) + "\u0440" +
+                //     "0123456789abcdef".substring(index + 1)
+                "\u0440123456789abcdef",
+                "0\u044023456789abcdef",
+                "01\u04403456789abcdef",
+                "012\u0440456789abcdef",
+                "0123\u044056789abcdef",
+                "01234\u04406789abcdef",
+                "012345\u0440789abcdef",
+                "0123456\u044089abcdef",
+                "01234567\u04409abcdef",
+                "012345678\u0440abcdef",
+                "0123456789\u0440bcdef",
+                "0123456789a\u0440cdef",
+                "0123456789ab\u0440def",
+                "0123456789abc\u0440ef",
+                "0123456789abcd\u0440f",
+                "0123456789abcde\u0440",
+                // Category 6, ASCII strings:
+                //     "\u0443" + "0123456789abcdef".substring(1, index + 1)
+                "\u0443",
+                "\u04431",
+                "\u044312",
+                "\u0443123",
+                "\u04431234",
+                "\u044312345",
+                "\u0443123456",
+                "\u04431234567",
+                "\u044312345678",
+                "\u0443123456789",
+                "\u0443123456789a",
+                "\u0443123456789ab",
+                "\u0443123456789abc",
+                "\u0443123456789abcd",
+                "\u0443123456789abcde",
+                "\u0443123456789abcdef",
+                // Category 7, non-ASCII strings:
+                //     "0123456789abcdef".substring(0, index) + "\u0482"
+                "\u0482",
+                "0\u0482",
+                "01\u0482",
+                "012\u0482",
+                "0123\u0482",
+                "01234\u0482",
+                "012345\u0482",
+                "0123456\u0482",
+                "01234567\u0482",
+                "012345678\u0482",
+                "0123456789\u0482",
+                "0123456789a\u0482",
+                "0123456789ab\u0482",
+                "0123456789abc\u0482",
+                "0123456789abcd\u0482",
+                "0123456789abcde\u0482",
+                // Category 8, non-ASCII strings:
+                //     "0123456789abcdef".substring(0, index) + "\u0482" +
+                //     "0123456789abcdef".substring(index + 1)
+                "\u0482123456789abcdef",
+                "0\u048223456789abcdef",
+                "01\u04823456789abcdef",
+                "012\u0482456789abcdef",
+                "0123\u048256789abcdef",
+                "01234\u04826789abcdef",
+                "012345\u0482789abcdef",
+                "0123456\u048289abcdef",
+                "01234567\u04829abcdef",
+                "012345678\u0482abcdef",
+                "0123456789\u0482bcdef",
+                "0123456789a\u0482cdef",
+                "0123456789ab\u0482def",
+                "0123456789abc\u0482ef",
+                "0123456789abcd\u0482f",
+                "0123456789abcde\u0482",
+                // Category 9, ASCII strings:
+                //     "\u0489" + "0123456789abcdef".substring(1, index + 1)
+                "\u0489",
+                "\u04891",
+                "\u048912",
+                "\u0489123",
+                "\u04891234",
+                "\u048912345",
+                "\u0489123456",
+                "\u04891234567",
+                "\u048912345678",
+                "\u0489123456789",
+                "\u0489123456789a",
+                "\u0489123456789ab",
+                "\u0489123456789abc",
+                "\u0489123456789abcd",
+                "\u0489123456789abcde",
+                "\u0489123456789abcdef",
+        };
+        int length = strings.length;
+        Assert.assertEquals(1 + 16 * 10, length);
+        for (int i = 0; i != length; ++i) {
+            String lhs = strings[i];
+            for (int j = 0; j != length; ++j) {
+                String rhs = strings[j];
+                int result = $noinline$compareTo(lhs, rhs);
+                final int expected;
+                if (i == 0 || j == 0 || i == j) {
+                    // One of the strings is empty or the strings are the same.
+                    expected = lhs.length() - rhs.length();
+                } else {
+                    int i_category = (i - 1) / 16;
+                    int i_index = (i - 1) % 16;
+                    int j_category = (j - 1) / 16;
+                    int j_index = (j - 1) % 16;
+                    int min_ij_index = (i_index < j_index) ? i_index : j_index;
+                    if (i_category == j_category) {
+                        switch (i_category) {
+                            case 0: case 3: case 6: case 9:
+                                // Differs in length.
+                                expected = lhs.length() - rhs.length();
+                                break;
+                            case 1: case 2: case 4: case 5: case 7: case 8:
+                                // Differs in charAt(min_ij_index).
+                                expected = lhs.charAt(min_ij_index) - rhs.charAt(min_ij_index);
+                                break;
+                            default: throw new Error("Unexpected category.");
+                      }
+                    } else if (i_category == 3 || i_category == 6 || i_category == 9 ||
+                               j_category == 3 || j_category == 6 || j_category == 9) {
+                        // In these categories, charAt(0) differs from other categories' strings.
+                        expected = lhs.charAt(0) - rhs.charAt(0);
+                    } else if (// Category 0 string is a prefix to any longer string in
+                               // remaining categories.
+                               (i_category == 0 && i_index < j_index) ||
+                               (j_category == 0 && j_index < i_index) ||
+                               // Category 2 string is a prefix to category 3 string at the same
+                               // index. Similar for categories 4 and 5 and also 7 and 8.
+                               // This includes matching last strings of these pairs of categories.
+                               (i_index == j_index &&
+                                   ((i_category == 1 && j_category == 2) ||
+                                    (i_category == 2 && j_category == 1) ||
+                                    (i_category == 4 && j_category == 5) ||
+                                    (i_category == 5 && j_category == 4) ||
+                                    (i_category == 7 && j_category == 8) ||
+                                    (i_category == 8 && j_category == 7)))) {
+                        // Differs in length.
+                        expected = lhs.length() - rhs.length();
+                    } else {
+                        // The remaining cases differ in charAt(min_ij_index), the characters
+                        // before that are "0123456789abcdef".substring(0, min_ij_index).
+                        for (int k = 0; k < min_ij_index; ++k) {
+                          Assert.assertEquals("0123456789abcdef".charAt(k), lhs.charAt(k));
+                          Assert.assertEquals("0123456789abcdef".charAt(k), rhs.charAt(k));
+                        }
+                        expected = lhs.charAt(min_ij_index) - rhs.charAt(min_ij_index);
+                        Assert.assertFalse(expected == 0);
+                    }
+                }
+                if (expected != result) {
+                  throw new Error(
+                      "Mismatch at i=" + i + ", j=" + j + ", expected=" + expected +
+                      ", result=" + result);
+                }
+                boolean equalsExpected =
+                    (i == j) ||
+                    // Last string in categories 1 and 2.
+                    (i == 32 && j == 48) || (i == 48 && j == 32) ||
+                    // Last string in categories 4 and 5.
+                    (i == 80 && j == 96) || (i == 96 && j == 80) ||
+                    // Last string in categories 7 and 8.
+                    (i == 128 && j == 144) || (i == 144 && j == 128);
+                Assert.assertEquals(equalsExpected, $noinline$equals(lhs, rhs));
+            }
+        }
+
+        try {
+            $noinline$compareTo("", null);
+            Assert.fail();
+        } catch (NullPointerException expected) {
+        }
+        try {
+            $noinline$compareTo(null, "");
+            Assert.fail();
+        } catch (NullPointerException expected) {
+        }
+
+        Assert.assertFalse($noinline$equals("", null));
+        try {
+            $noinline$equals(null, "");
+            Assert.fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public static void testIndexOf() {
+        String[] prefixes = {
+                "",
+                "0",
+                "01",
+                "012",
+                "0123",
+                "01234",
+                "012345",
+                "0123456",
+                "01234567",
+                "012345678",
+                "0123456789",
+                "0123456789a",
+                "0123456789ab",
+                "0123456789abc",
+                "0123456789abcd",
+                "0123456789abcdef",
+        };
+        String[] cores = {
+                "",
+                "x",
+                "xx",
+                "xxx",
+                "xxxx",
+                "xxxxx",
+                "xxxxxx",
+                "xxxxxxx",
+                "xxxxxxxx",
+                "xzx",
+                "xxzx",
+                "xxxzx",
+                "xxxxzx",
+                "xxxxxzx",
+                "xxxxxxzx",
+                "xxxxxxxzx",
+                "xxxxxxxxzx",
+                "\u0440",
+                "\u0440\u0440",
+                "\u0440\u0440\u0440",
+                "\u0440\u0440\u0440\u0440",
+                "\u0440\u0440\u0440\u0440\u0440",
+                "\u0440\u0440\u0440\u0440\u0440\u0440",
+                "\u0440\u0440\u0440\u0440\u0440\u0440\u0440",
+                "\u0440\u0440\u0440\u0440\u0440\u0440\u0440\u0440",
+                "\u0440z\u0440",
+                "\u0440\u0440z\u0440",
+                "\u0440\u0440\u0440z\u0440",
+                "\u0440\u0440\u0440\u0440z\u0440",
+                "\u0440\u0440\u0440\u0440\u0440z\u0440",
+                "\u0440\u0440\u0440\u0440\u0440\u0440z\u0440",
+                "\u0440\u0440\u0440\u0440\u0440\u0440\u0440z\u0440",
+                "\u0440\u0440\u0440\u0440\u0440\u0440\u0440\u0440z\u0440",
+        };
+        String[] suffixes = {
+                "",
+                "y",
+                "yy",
+                "yyy",
+                "yyyy",
+                "yyyyy",
+                "yyyyyy",
+                "yyyyyyy",
+                "yyyyyyyy",
+                "\u0441",
+                "y\u0441",
+                "yy\u0441",
+                "yyy\u0441",
+                "yyyy\u0441",
+                "yyyyy\u0441",
+                "yyyyyy\u0441",
+                "yyyyyyy\u0441",
+                "yyyyyyyy\u0441",
+        };
+        for (String p : prefixes) {
+            for (String c : cores) {
+                for (String s : suffixes) {
+                    String full = p + c + s;
+                    int expX = (c.isEmpty() || c.charAt(0) != 'x') ? -1 : p.length();
+                    int exp0440 = (c.isEmpty() || c.charAt(0) != '\u0440') ? -1 : p.length();
+                    Assert.assertEquals(expX, $noinline$indexOf(full, 'x'));
+                    Assert.assertEquals(exp0440, $noinline$indexOf(full, '\u0440'));
+                    Assert.assertEquals(expX, $noinline$indexOf(full, 'x', -1));
+                    Assert.assertEquals(exp0440, $noinline$indexOf(full, '\u0440', -1));
+                    Assert.assertEquals(-1, $noinline$indexOf(full, 'x', full.length() + 1));
+                    Assert.assertEquals(-1, $noinline$indexOf(full, '\u0440', full.length() + 1));
+                    for (int from = 0; from != full.length(); ++from) {
+                        final int eX;
+                        final int e0440;
+                        if (from <= p.length()) {
+                            eX = expX;
+                            e0440 = exp0440;
+                        } else if (from >= p.length() + c.length()) {
+                            eX = -1;
+                            e0440 = -1;
+                        } else if (full.charAt(from) == 'z') {
+                            eX = (full.charAt(from + 1) != 'x') ? -1 : from + 1;
+                            e0440 = (full.charAt(from + 1) != '\u0440') ? -1 : from + 1;
+                        } else {
+                            eX = (full.charAt(from) != 'x') ? -1 : from;
+                            e0440 = (full.charAt(from) != '\u0440') ? -1 : from;
+                        }
+                        Assert.assertEquals(eX, $noinline$indexOf(full, 'x', from));
+                        Assert.assertEquals(e0440, $noinline$indexOf(full, '\u0440', from));
+                    }
+                }
+            }
+        }
+    }
+
+    public static int $noinline$compareTo(String lhs, String rhs) {
+        if (doThrow) { throw new Error(); }
+        return lhs.compareTo(rhs);
+    }
+
+    public static boolean $noinline$equals(String lhs, String rhs) {
+        if (doThrow) { throw new Error(); }
+        return lhs.equals(rhs);
+    }
+
+    public static int $noinline$indexOf(String lhs, int ch) {
+        if (doThrow) { throw new Error(); }
+        return lhs.indexOf(ch);
+    }
+
+    public static int $noinline$indexOf(String lhs, int ch, int fromIndex) {
+        if (doThrow) { throw new Error(); }
+        return lhs.indexOf(ch, fromIndex);
+    }
+
+    public static boolean doThrow = false;
 }
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 89875d7..6b0dedf 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -717,6 +717,33 @@
     return sumWithFilter(array, filter);
   }
 
+  private static int mI = 0;
+  private static float mF = 0f;
+
+  /// CHECK-START: float Main.testAllocationEliminationWithLoops() load_store_elimination (before)
+  /// CHECK: NewInstance
+  /// CHECK: NewInstance
+  /// CHECK: NewInstance
+
+  /// CHECK-START: float Main.testAllocationEliminationWithLoops() load_store_elimination (after)
+  /// CHECK-NOT: NewInstance
+
+  private static float testAllocationEliminationWithLoops() {
+    for (int i0 = 0; i0 < 5; i0++) {
+      for (int i1 = 0; i1 < 5; i1++) {
+        for (int i2 = 0; i2 < 5; i2++) {
+          int lI0 = ((int) new Integer(((int) new Integer(mI))));
+          if (((boolean) new Boolean(false))) {
+            for (int i3 = 576 - 1; i3 >= 0; i3--) {
+              mF -= 976981405.0f;
+            }
+          }
+        }
+      }
+    }
+    return 1.0f;
+  }
+
   static void assertIntEquals(int result, int expected) {
     if (expected != result) {
       throw new Error("Expected: " + expected + ", found: " + result);
@@ -779,6 +806,8 @@
     assertIntEquals($noinline$testHSelect(true), 0xdead);
     int[] array = {2, 5, 9, -1, -3, 10, 8, 4};
     assertIntEquals(sumWithinRange(array, 1, 5), 11);
+    assertFloatEquals(testAllocationEliminationWithLoops(), 1.0f);
+    assertFloatEquals(mF, 0f);
   }
 
   static boolean sFlag;
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 5c789cd..b5606bd 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -31,6 +31,26 @@
     }
   }
 
+  /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+  //
+  /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
+  /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none
+  static void deadSingleLoopN(int n) {
+    for (int i = 0; i < n; i++) {
+    }
+  }
+
+  /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+  //
+  /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (after)
+  /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+  static void potentialInfiniteLoop(int n) {
+    for (int i = 0; i <= n; i++) {  // loops forever when n = MAX_INT
+    }
+  }
+
   /// CHECK-START: void Main.deadNestedLoops() loop_optimization (before)
   /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: Phi loop:{{B\d+}}      outer_loop:<<Loop>>
@@ -160,6 +180,10 @@
   /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (after)
   /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
   /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.closedFormInductionUp() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 12395
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   static int closedFormInductionUp() {
     int closed = 12345;
     for (int i = 0; i < 10; i++) {
@@ -194,6 +218,10 @@
   /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
   /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:loop{{B\d+}}
   /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.closedFormNested() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 100
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   static int closedFormNested() {
     int closed = 0;
     for (int i = 0; i < 10; i++) {
@@ -215,6 +243,10 @@
   /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
   /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:loop{{B\d+}}
   /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.closedFormNestedAlt() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 15082
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   static int closedFormNestedAlt() {
     int closed = 12345;
     for (int i = 0; i < 17; i++) {
@@ -293,14 +325,29 @@
   /// CHECK-START: int Main.mainIndexReturned() loop_optimization (after)
   /// CHECK-NOT:              Phi    loop:{{B\d+}} outer_loop:none
   /// CHECK-DAG:              Return loop:none
+  //
+  /// CHECK-START: int Main.mainIndexReturned() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 10
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   static int mainIndexReturned() {
     int i;
     for (i = 0; i < 10; i++);
     return i;
   }
 
-  // If ever replaced by closed form, last value should be correct!
-  static int periodicReturned() {
+  /// CHECK-START: int Main.periodicReturned9() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: int Main.periodicReturned9() loop_optimization (after)
+  /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
+  /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.periodicReturned9() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 1
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
+  static int periodicReturned9() {
     int k = 0;
     for (int i = 0; i < 9; i++) {
       k = 1 - k;
@@ -308,6 +355,26 @@
     return k;
   }
 
+  /// CHECK-START: int Main.periodicReturned10() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: int Main.periodicReturned10() loop_optimization (after)
+  /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
+  /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.periodicReturned10() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 0
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
+  static int periodicReturned10() {
+    int k = 0;
+    for (int i = 0; i < 10; i++) {
+      k = 1 - k;
+    }
+    return k;
+  }
+
   // If ever replaced by closed form, last value should be correct!
   private static int getSum21() {
     int k = 0;
@@ -326,7 +393,14 @@
     return i;
   }
 
-  // If ever replaced by closed form, last value should be correct!
+  /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after)
+  /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
+  /// CHECK-DAG:               Return loop:none
   static int periodicReturnedN(int n) {
     int k = 0;
     for (int i = 0; i < n; i++) {
@@ -371,6 +445,10 @@
   /// CHECK-START: int Main.closedFeed() loop_optimization (after)
   /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
   /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.closedFeed() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 20
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   private static int closedFeed() {
     int closed = 0;
     for (int i = 0; i < 10; i++) {
@@ -392,6 +470,10 @@
   /// CHECK-START: int Main.closedLargeUp() loop_optimization (after)
   /// CHECK-NOT:               Phi    loop:B\d+ outer_loop:none
   /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.closedLargeUp() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant -10
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   private static int closedLargeUp() {
     int closed = 0;
     for (int i = 0; i < 10; i++) {
@@ -408,6 +490,10 @@
   /// CHECK-START: int Main.closedLargeDown() loop_optimization (after)
   /// CHECK-NOT:               Phi    loop:B\d+ outer_loop:none
   /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.closedLargeDown() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 10
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   private static int closedLargeDown() {
     int closed = 0;
     for (int i = 0; i < 10; i++) {
@@ -427,6 +513,10 @@
   /// CHECK-START: int Main.waterFall() loop_optimization (after)
   /// CHECK-NOT:               Phi    loop:B\d+ outer_loop:none
   /// CHECK-DAG:               Return loop:none
+  //
+  /// CHECK-START: int Main.waterFall() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 50
+  /// CHECK-DAG:               Return [<<Int>>] loop:none
   private static int waterFall() {
     int i = 0;
     for (; i < 10; i++);
@@ -469,6 +559,8 @@
 
   public static void main(String[] args) {
     deadSingleLoop();
+    deadSingleLoopN(4);
+    potentialInfiniteLoop(4);
     deadNestedLoops();
     deadNestedAndFollowingLoops();
 
@@ -512,7 +604,8 @@
     }
 
     expectEquals(10, mainIndexReturned());
-    expectEquals(1, periodicReturned());
+    expectEquals(1, periodicReturned9());
+    expectEquals(0, periodicReturned10());
     expectEquals(21, getSum21());
     for (int n = -4; n < 4; n++) {
       int tc = (n <= 0) ? 0 : n;
diff --git a/test/904-object-allocation/src/Main.java b/test/904-object-allocation/src/Main.java
index 9a089bd..fc8a112 100644
--- a/test/904-object-allocation/src/Main.java
+++ b/test/904-object-allocation/src/Main.java
@@ -40,7 +40,11 @@
   }
 
   public static void doTest(ArrayList<Object> l) throws Exception {
-    setupObjectAllocCallback();
+    // Disable the global registration from OnLoad, to get into a known state.
+    enableAllocationTracking(null, false);
+
+    // Enable actual logging callback.
+    setupObjectAllocCallback(true);
 
     enableAllocationTracking(null, true);
 
@@ -74,6 +78,11 @@
     testThread(l, false, true);
 
     l.add(new Byte((byte)0));
+
+    // Disable actual logging callback and re-enable tracking, so we can keep the event enabled and
+    // check that shutdown works correctly.
+    setupObjectAllocCallback(false);
+    enableAllocationTracking(null, true);
   }
 
   private static void testThread(final ArrayList<Object> l, final boolean sameThread,
@@ -82,6 +91,8 @@
     final SimpleBarrier trackBarrier = new SimpleBarrier(1);
     final SimpleBarrier disableBarrier = new SimpleBarrier(1);
 
+    final Thread thisThread = Thread.currentThread();
+
     Thread t = new Thread() {
       public void run() {
         try {
@@ -95,7 +106,7 @@
         l.add(new Double(0.0));
 
         if (disableTracking) {
-          enableAllocationTracking(sameThread ? this : Thread.currentThread(), false);
+          enableAllocationTracking(sameThread ? this : thisThread, false);
         }
       }
     };
@@ -127,6 +138,6 @@
     }
   }
 
-  private static native void setupObjectAllocCallback();
+  private static native void setupObjectAllocCallback(boolean enable);
   private static native void enableAllocationTracking(Thread thread, boolean enable);
 }
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index b22fc6c..57bfed5 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -58,10 +58,10 @@
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectAllocCallback(
-    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
   jvmtiEventCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
-  callbacks.VMObjectAlloc = ObjectAllocated;
+  callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr;
 
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
   if (ret != JVMTI_ERROR_NONE) {
@@ -94,6 +94,7 @@
     printf("Unable to get jvmti env!\n");
     return 1;
   }
+  jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, nullptr);
   return 0;
 }
 
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 34f8838..3bf7e4b 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -491,6 +491,7 @@
 # between those runs to be able to have precise checks.
 TEST_ART_BROKEN_JIT_RUN_TESTS := \
   137-cfi \
+  904-object-allocation \
   906-iterate-heap \
 
 ifneq (,$(filter jit,$(COMPILER_TYPES)))
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index 8604ff0..dbc1102 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -74,7 +74,9 @@
  * Instance.isRoot and Instance.getRootTypes.
 
 Release History:
- 0.8 Pending
+ 0.9 Pending
+
+ 0.8 Oct 18, 2016
    Show sample path from GC root with field names in place of dominator path.
 
  0.7 Aug 16, 2016
diff --git a/tools/ahat/src/manifest.txt b/tools/ahat/src/manifest.txt
index cac53c5..1993910 100644
--- a/tools/ahat/src/manifest.txt
+++ b/tools/ahat/src/manifest.txt
@@ -1,4 +1,4 @@
 Name: ahat/
 Implementation-Title: ahat
-Implementation-Version: 0.7
+Implementation-Version: 0.8
 Main-Class: com.android.ahat.Main
diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt
deleted file mode 100644
index 0e289a6..0000000
--- a/tools/libcore_failures_concurrent_collector.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file contains expectations for ART's buildbot's concurrent collector
- * configurations. The purpose of this file is to temporary and quickly list
- * failing tests and not break the bots on the CC configurations, until they
- * are fixed or until the libcore expectation files get properly updated. The
- * script that uses this file is art/tools/run-libcore-tests.sh.
- *
- * It is also used to enable AOSP experiments, and not mess up with CTS's
- * expectations.
- */
-
-[
-]
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 01c7f20..41faa69 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -43,10 +43,6 @@
 done
 
 expectations="--expectations art/tools/libcore_failures.txt"
-if [ "x$ART_USE_READ_BARRIER" = xtrue ]; then
-  # Tolerate some more failures on the concurrent collector configurations.
-  expectations="$expectations --expectations art/tools/libcore_failures_concurrent_collector.txt"
-fi
 
 emulator="no"
 if [ "$ANDROID_SERIAL" = "emulator-5554" ]; then