Merge "Refactor the alloc entry point generation code."
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index a443487..b507124 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -295,18 +295,22 @@
   art_cflags += -DIMT_SIZE=64
 endif
 
-ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
-  art_cflags += -DART_USE_OPTIMIZING_COMPILER=1
-endif
-
 ifeq ($(ART_HEAP_POISONING),true)
   art_cflags += -DART_HEAP_POISONING=1
   art_asflags += -DART_HEAP_POISONING=1
 endif
 
+#
+# Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
+# The default is BAKER.
+#
+ART_READ_BARRIER_TYPE ?= BAKER
+
 ifeq ($(ART_USE_READ_BARRIER),true)
   art_cflags += -DART_USE_READ_BARRIER=1
+  art_cflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
   art_asflags += -DART_USE_READ_BARRIER=1
+  art_asflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
 endif
 
 ifeq ($(ART_USE_TLAB),true)
@@ -396,7 +400,6 @@
 art_non_debug_cflags :=
 art_host_non_debug_cflags :=
 art_target_non_debug_cflags :=
-art_default_gc_type :=
 art_default_gc_type_cflags :=
 
 ART_HOST_LDLIBS :=
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 71a55bb..9775f6a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -351,6 +351,7 @@
 
 COMPILER_GTEST_HOST_SRC_FILES_mips64 := \
   $(COMPILER_GTEST_COMMON_SRC_FILES_mips64) \
+  compiler/utils/mips64/assembler_mips64_test.cc \
 
 COMPILER_GTEST_HOST_SRC_FILES_x86 := \
   $(COMPILER_GTEST_COMMON_SRC_FILES_x86) \
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 6e17ed3..592843e 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -52,12 +52,6 @@
   core_pic_infix :=
   core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
 
-  # With the optimizing compiler, we want to rerun dex2oat whenever there is
-  # a dex2oat change to catch regressions early.
-  ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
-    core_dex2oat_dependency := $(DEX2OAT)
-  endif
-
   ifeq ($(1),default)
     core_compile_options += --compiler-backend=Quick
   endif
@@ -178,17 +172,13 @@
   core_pic_infix :=
   core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
 
-  # With the optimizing compiler, we want to rerun dex2oat whenever there is
-  # a dex2oat change to catch regressions early.
-  ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
-    core_dex2oat_dependency := $(DEX2OAT)
-  endif
-
   ifeq ($(1),default)
     core_compile_options += --compiler-backend=Quick
   endif
   ifeq ($(1),optimizing)
     core_compile_options += --compiler-backend=Optimizing
+    # With the optimizing compiler, we want to rerun dex2oat whenever there is
+    # a dex2oat change to catch regressions early.
     core_dex2oat_dependency := $(DEX2OAT)
     core_infix := -optimizing
   endif
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index dc2bc5c..67b4428 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -92,7 +92,7 @@
 
   void UnreserveImageSpace();
 
-  Compiler::Kind compiler_kind_ = kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick;
+  Compiler::Kind compiler_kind_ = Compiler::kOptimizing;
   std::unique_ptr<CompilerOptions> compiler_options_;
   std::unique_ptr<VerificationResults> verification_results_;
   std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_;
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 1f114cf..3c5c2fe 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -148,7 +148,7 @@
   if (arg1.wide == 0) {
     LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1));
   } else {
-    RegStorage r_tmp = TargetReg(cu_->instruction_set == kMips ? kArg2 : kArg1, kWide);
+    RegStorage r_tmp = TargetReg(kArg2, kWide);
     LoadValueDirectWideFixed(arg1, r_tmp);
   }
   LoadConstant(TargetReg(kArg0, kNotWide), arg0);
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 939bf40..6ed666b 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -535,37 +535,76 @@
   if (block->visited || block->hidden) {
     return;
   }
-  block->visited = true;
 
-  /* Process this block */
-  DoSSAConversion(block);
+  typedef struct {
+    BasicBlock* bb;
+    int32_t* ssa_map;
+  } BasicBlockInfo;
+  BasicBlockInfo temp;
 
-  /* Save SSA map snapshot */
   ScopedArenaAllocator allocator(&cu_->arena_stack);
-  uint32_t num_vregs = GetNumOfCodeAndTempVRs();
-  int32_t* saved_ssa_map = allocator.AllocArray<int32_t>(num_vregs, kArenaAllocDalvikToSSAMap);
-  size_t map_size = sizeof(saved_ssa_map[0]) * num_vregs;
-  memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
+  ScopedArenaVector<BasicBlockInfo> bi_stack(allocator.Adapter());
+  ScopedArenaVector<BasicBlock*> succ_stack(allocator.Adapter());
 
-  if (block->fall_through != NullBasicBlockId) {
-    DoDFSPreOrderSSARename(GetBasicBlock(block->fall_through));
-    /* Restore SSA map snapshot */
-    memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
-  }
-  if (block->taken != NullBasicBlockId) {
-    DoDFSPreOrderSSARename(GetBasicBlock(block->taken));
-    /* Restore SSA map snapshot */
-    memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
-  }
-  if (block->successor_block_list_type != kNotUsed) {
-    for (SuccessorBlockInfo* successor_block_info : block->successor_blocks) {
-      BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
-      DoDFSPreOrderSSARename(succ_bb);
-      /* Restore SSA map snapshot */
-      memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
+  uint32_t num_vregs = GetNumOfCodeAndTempVRs();
+  size_t map_size = sizeof(int32_t) * num_vregs;
+  temp.bb = block;
+  temp.ssa_map = vreg_to_ssa_map_;
+  bi_stack.push_back(temp);
+
+  while (!bi_stack.empty()) {
+    temp = bi_stack.back();
+    bi_stack.pop_back();
+    BasicBlock* b = temp.bb;
+
+    if (b->visited || b->hidden) {
+      continue;
+    }
+    b->visited = true;
+
+    /* Restore SSA map snapshot, except for the first block */
+    if (b != block) {
+      memcpy(vreg_to_ssa_map_, temp.ssa_map, map_size);
+    }
+
+    /* Process this block */
+    DoSSAConversion(b);
+
+    /* If there are no successor, taken, and fall through blocks, continue */
+    if (b->successor_block_list_type == kNotUsed &&
+        b->taken == NullBasicBlockId &&
+        b->fall_through == NullBasicBlockId) {
+      continue;
+    }
+
+    /* Save SSA map snapshot */
+    int32_t* saved_ssa_map =
+      allocator.AllocArray<int32_t>(num_vregs, kArenaAllocDalvikToSSAMap);
+    memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
+
+    if (b->successor_block_list_type != kNotUsed) {
+      for (SuccessorBlockInfo* successor_block_info : b->successor_blocks) {
+        BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
+        succ_stack.push_back(succ_bb);
+      }
+      while (!succ_stack.empty()) {
+        temp.bb = succ_stack.back();
+        succ_stack.pop_back();
+        temp.ssa_map = saved_ssa_map;
+        bi_stack.push_back(temp);
+      }
+    }
+    if (b->taken != NullBasicBlockId) {
+      temp.bb = GetBasicBlock(b->taken);
+      temp.ssa_map = saved_ssa_map;
+      bi_stack.push_back(temp);
+    }
+    if (b->fall_through != NullBasicBlockId) {
+      temp.bb = GetBasicBlock(b->fall_through);
+      temp.ssa_map = saved_ssa_map;
+      bi_stack.push_back(temp);
     }
   }
-  return;
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 960f4d9..2c7c127 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -965,7 +965,8 @@
             suspend_check->GetEnvironment(), header);
       }
 
-      HArrayLength* new_array_length = new (graph->GetArena()) HArrayLength(array);
+      HArrayLength* new_array_length
+          = new (graph->GetArena()) HArrayLength(array, array->GetDexPc());
       deopt_block->InsertInstructionBefore(new_array_length, deopt_block->GetLastInstruction());
 
       if (loop_entry_test_block_added) {
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 08e1e36..ce6dc75 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -91,7 +91,7 @@
   HBasicBlock* block2 = new (&allocator_) HBasicBlock(graph_);
   graph_->AddBlock(block2);
   HNullCheck* null_check = new (&allocator_) HNullCheck(parameter1, 0);
-  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check2 = new (&allocator_)
       HBoundsCheck(parameter2, array_length, 0);
   HArraySet* array_set = new (&allocator_) HArraySet(
@@ -104,7 +104,7 @@
   HBasicBlock* block3 = new (&allocator_) HBasicBlock(graph_);
   graph_->AddBlock(block3);
   null_check = new (&allocator_) HNullCheck(parameter1, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   cmp = new (&allocator_) HLessThan(parameter2, array_length);
   if_inst = new (&allocator_) HIf(cmp);
   block3->AddInstruction(null_check);
@@ -115,7 +115,7 @@
   HBasicBlock* block4 = new (&allocator_) HBasicBlock(graph_);
   graph_->AddBlock(block4);
   null_check = new (&allocator_) HNullCheck(parameter1, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check4 = new (&allocator_)
       HBoundsCheck(parameter2, array_length, 0);
   array_set = new (&allocator_) HArraySet(
@@ -128,7 +128,7 @@
   HBasicBlock* block5 = new (&allocator_) HBasicBlock(graph_);
   graph_->AddBlock(block5);
   null_check = new (&allocator_) HNullCheck(parameter1, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check5 = new (&allocator_)
       HBoundsCheck(parameter2, array_length, 0);
   array_set = new (&allocator_) HArraySet(
@@ -190,7 +190,7 @@
   graph_->AddBlock(block2);
   HInstruction* add = new (&allocator_) HAdd(Primitive::kPrimInt, parameter2, constant_max_int);
   HNullCheck* null_check = new (&allocator_) HNullCheck(parameter1, 0);
-  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
   HInstruction* cmp2 = new (&allocator_) HGreaterThanOrEqual(add, array_length);
   if_inst = new (&allocator_) HIf(cmp2);
   block2->AddInstruction(add);
@@ -245,7 +245,7 @@
   HBasicBlock* block1 = new (&allocator_) HBasicBlock(graph_);
   graph_->AddBlock(block1);
   HNullCheck* null_check = new (&allocator_) HNullCheck(parameter1, 0);
-  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
   HInstruction* cmp = new (&allocator_) HGreaterThanOrEqual(parameter2, array_length);
   HIf* if_inst = new (&allocator_) HIf(cmp);
   block1->AddInstruction(null_check);
@@ -308,7 +308,7 @@
   entry->AddSuccessor(block);
 
   HNullCheck* null_check = new (&allocator_) HNullCheck(parameter, 0);
-  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check6 = new (&allocator_)
       HBoundsCheck(constant_6, array_length, 0);
   HInstruction* array_set = new (&allocator_) HArraySet(
@@ -319,7 +319,7 @@
   block->AddInstruction(array_set);
 
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check5 = new (&allocator_)
       HBoundsCheck(constant_5, array_length, 0);
   array_set = new (&allocator_) HArraySet(
@@ -330,7 +330,7 @@
   block->AddInstruction(array_set);
 
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check4 = new (&allocator_)
       HBoundsCheck(constant_4, array_length, 0);
   array_set = new (&allocator_) HArraySet(
@@ -389,7 +389,7 @@
 
   HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
   HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
-  HInstruction* array_length = new (allocator) HArrayLength(null_check);
+  HInstruction* array_length = new (allocator) HArrayLength(null_check, 0);
   HInstruction* cmp = nullptr;
   if (cond == kCondGE) {
     cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
@@ -406,7 +406,7 @@
   phi->AddInput(constant_initial);
 
   null_check = new (allocator) HNullCheck(parameter, 0);
-  array_length = new (allocator) HArrayLength(null_check);
+  array_length = new (allocator) HArrayLength(null_check, 0);
   HInstruction* bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
   HInstruction* array_set = new (allocator) HArraySet(
       null_check, bounds_check, constant_10, Primitive::kPrimInt, 0);
@@ -489,7 +489,7 @@
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
-  HInstruction* array_length = new (allocator) HArrayLength(null_check);
+  HInstruction* array_length = new (allocator) HArrayLength(null_check, 0);
   block->AddInstruction(null_check);
   block->AddInstruction(array_length);
   block->AddInstruction(new (allocator) HGoto());
@@ -522,7 +522,7 @@
 
   HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_minus_1);
   null_check = new (allocator) HNullCheck(parameter, 0);
-  array_length = new (allocator) HArrayLength(null_check);
+  array_length = new (allocator) HArrayLength(null_check, 0);
   HInstruction* bounds_check = new (allocator) HBoundsCheck(add, array_length, 0);
   HInstruction* array_set = new (allocator) HArraySet(
       null_check, bounds_check, constant_10, Primitive::kPrimInt, 0);
@@ -631,7 +631,7 @@
   phi->AddInput(constant_initial);
 
   HNullCheck* null_check = new (allocator) HNullCheck(new_array, 0);
-  HArrayLength* array_length = new (allocator) HArrayLength(null_check);
+  HArrayLength* array_length = new (allocator) HArrayLength(null_check, 0);
   HInstruction* bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
   HInstruction* array_set = new (allocator) HArraySet(
       null_check, bounds_check, constant_10, Primitive::kPrimInt, 0);
@@ -716,7 +716,7 @@
 
   HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
   HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
-  HInstruction* array_length = new (allocator) HArrayLength(null_check);
+  HInstruction* array_length = new (allocator) HArrayLength(null_check, 0);
   HInstruction* cmp = nullptr;
   if (cond == kCondGE) {
     cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
@@ -732,7 +732,7 @@
   phi->AddInput(constant_initial);
 
   null_check = new (allocator) HNullCheck(parameter, 0);
-  array_length = new (allocator) HArrayLength(null_check);
+  array_length = new (allocator) HArrayLength(null_check, 0);
   HInstruction* sub = new (allocator) HSub(Primitive::kPrimInt, array_length, phi);
   HInstruction* add_minus_1 = new (allocator)
       HAdd(Primitive::kPrimInt, sub, constant_minus_1);
@@ -811,7 +811,7 @@
   graph_->AddBlock(outer_header);
   HPhi* phi_i = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
   HNullCheck* null_check = new (&allocator_) HNullCheck(parameter, 0);
-  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+  HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
   HAdd* add = new (&allocator_) HAdd(Primitive::kPrimInt, array_length, constant_minus_1);
   HInstruction* cmp = new (&allocator_) HGreaterThanOrEqual(phi_i, add);
   HIf* if_inst = new (&allocator_) HIf(cmp);
@@ -827,7 +827,7 @@
   graph_->AddBlock(inner_header);
   HPhi* phi_j = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HSub* sub = new (&allocator_) HSub(Primitive::kPrimInt, array_length, phi_i);
   add = new (&allocator_) HAdd(Primitive::kPrimInt, sub, constant_minus_1);
   cmp = new (&allocator_) HGreaterThanOrEqual(phi_j, add);
@@ -844,20 +844,20 @@
   HBasicBlock* inner_body_compare = new (&allocator_) HBasicBlock(graph_);
   graph_->AddBlock(inner_body_compare);
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check1 = new (&allocator_) HBoundsCheck(phi_j, array_length, 0);
   HArrayGet* array_get_j = new (&allocator_)
-      HArrayGet(null_check, bounds_check1, Primitive::kPrimInt);
+      HArrayGet(null_check, bounds_check1, Primitive::kPrimInt, 0);
   inner_body_compare->AddInstruction(null_check);
   inner_body_compare->AddInstruction(array_length);
   inner_body_compare->AddInstruction(bounds_check1);
   inner_body_compare->AddInstruction(array_get_j);
   HInstruction* j_plus_1 = new (&allocator_) HAdd(Primitive::kPrimInt, phi_j, constant_1);
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HBoundsCheck* bounds_check2 = new (&allocator_) HBoundsCheck(j_plus_1, array_length, 0);
   HArrayGet* array_get_j_plus_1 = new (&allocator_)
-      HArrayGet(null_check, bounds_check2, Primitive::kPrimInt);
+      HArrayGet(null_check, bounds_check2, Primitive::kPrimInt, 0);
   cmp = new (&allocator_) HGreaterThanOrEqual(array_get_j, array_get_j_plus_1);
   if_inst = new (&allocator_) HIf(cmp);
   inner_body_compare->AddInstruction(j_plus_1);
@@ -873,10 +873,10 @@
   j_plus_1 = new (&allocator_) HAdd(Primitive::kPrimInt, phi_j, constant_1);
   // temp = array[j+1]
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HInstruction* bounds_check3 = new (&allocator_) HBoundsCheck(j_plus_1, array_length, 0);
   array_get_j_plus_1 = new (&allocator_)
-      HArrayGet(null_check, bounds_check3, Primitive::kPrimInt);
+      HArrayGet(null_check, bounds_check3, Primitive::kPrimInt, 0);
   inner_body_swap->AddInstruction(j_plus_1);
   inner_body_swap->AddInstruction(null_check);
   inner_body_swap->AddInstruction(array_length);
@@ -884,16 +884,16 @@
   inner_body_swap->AddInstruction(array_get_j_plus_1);
   // array[j+1] = array[j]
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HInstruction* bounds_check4 = new (&allocator_) HBoundsCheck(phi_j, array_length, 0);
   array_get_j = new (&allocator_)
-      HArrayGet(null_check, bounds_check4, Primitive::kPrimInt);
+      HArrayGet(null_check, bounds_check4, Primitive::kPrimInt, 0);
   inner_body_swap->AddInstruction(null_check);
   inner_body_swap->AddInstruction(array_length);
   inner_body_swap->AddInstruction(bounds_check4);
   inner_body_swap->AddInstruction(array_get_j);
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HInstruction* bounds_check5 = new (&allocator_) HBoundsCheck(j_plus_1, array_length, 0);
   HArraySet* array_set_j_plus_1 = new (&allocator_)
       HArraySet(null_check, bounds_check5, array_get_j, Primitive::kPrimInt, 0);
@@ -903,7 +903,7 @@
   inner_body_swap->AddInstruction(array_set_j_plus_1);
   // array[j] = temp
   null_check = new (&allocator_) HNullCheck(parameter, 0);
-  array_length = new (&allocator_) HArrayLength(null_check);
+  array_length = new (&allocator_) HArrayLength(null_check, 0);
   HInstruction* bounds_check6 = new (&allocator_) HBoundsCheck(phi_j, array_length, 0);
   HArraySet* array_set_j = new (&allocator_)
       HArraySet(null_check, bounds_check6, array_get_j_plus_1, Primitive::kPrimInt, 0);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index cb36f62..5acc5fd 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -940,7 +940,8 @@
           storage_index,
           *dex_compilation_unit_->GetDexFile(),
           is_outer_class,
-          dex_pc);
+          dex_pc,
+          /*needs_access_check*/ false);
       current_block_->AddInstruction(load_class);
       clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
       current_block_->AddInstruction(clinit_check);
@@ -1186,6 +1187,12 @@
   }
 }
 
+static Primitive::Type GetFieldAccessType(const DexFile& dex_file, uint16_t field_index) {
+  const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+  const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+  return Primitive::GetType(type[0]);
+}
+
 bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
                                              uint32_t dex_pc,
                                              bool is_put) {
@@ -1205,44 +1212,61 @@
   ArtField* resolved_field =
       compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa);
 
-  if (resolved_field == nullptr) {
-    MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
-    return false;
-  }
-
-  Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
 
   HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot, dex_pc);
-  current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_pc));
+  HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc);
+  current_block_->AddInstruction(null_check);
+
+  Primitive::Type field_type = (resolved_field == nullptr)
+      ? GetFieldAccessType(*dex_file_, field_index)
+      : resolved_field->GetTypeAsPrimitiveType();
   if (is_put) {
     Temporaries temps(graph_);
-    HInstruction* null_check = current_block_->GetLastInstruction();
     // We need one temporary for the null check.
     temps.Add(null_check);
     HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
-    current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
-        null_check,
-        value,
-        field_type,
-        resolved_field->GetOffset(),
-        resolved_field->IsVolatile(),
-        field_index,
-        *dex_file_,
-        dex_compilation_unit_->GetDexCache(),
-        dex_pc));
+    HInstruction* field_set = nullptr;
+    if (resolved_field == nullptr) {
+      MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+      field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check,
+                                                           value,
+                                                           field_type,
+                                                           field_index,
+                                                           dex_pc);
+    } else {
+      field_set = new (arena_) HInstanceFieldSet(null_check,
+                                                 value,
+                                                 field_type,
+                                                 resolved_field->GetOffset(),
+                                                 resolved_field->IsVolatile(),
+                                                 field_index,
+                                                 *dex_file_,
+                                                 dex_compilation_unit_->GetDexCache(),
+                                                 dex_pc);
+    }
+    current_block_->AddInstruction(field_set);
   } else {
-    current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
-        current_block_->GetLastInstruction(),
-        field_type,
-        resolved_field->GetOffset(),
-        resolved_field->IsVolatile(),
-        field_index,
-        *dex_file_,
-        dex_compilation_unit_->GetDexCache(),
-        dex_pc));
-
-    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
+    HInstruction* field_get = nullptr;
+    if (resolved_field == nullptr) {
+      MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+      field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check,
+                                                           field_type,
+                                                           field_index,
+                                                           dex_pc);
+    } else {
+      field_get = new (arena_) HInstanceFieldGet(null_check,
+                                                 field_type,
+                                                 resolved_field->GetOffset(),
+                                                 resolved_field->IsVolatile(),
+                                                 field_index,
+                                                 *dex_file_,
+                                                 dex_compilation_unit_->GetDexCache(),
+                                                 dex_pc);
+    }
+    current_block_->AddInstruction(field_get);
+    UpdateLocal(source_or_dest_reg, field_get, dex_pc);
   }
+
   return true;
 }
 
@@ -1282,6 +1306,23 @@
   return outer_class.Get() == cls.Get();
 }
 
+void HGraphBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+                                                     uint32_t dex_pc,
+                                                     bool is_put,
+                                                     Primitive::Type field_type) {
+  uint32_t source_or_dest_reg = instruction.VRegA_21c();
+  uint16_t field_index = instruction.VRegB_21c();
+
+  if (is_put) {
+    HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
+    current_block_->AddInstruction(
+        new (arena_) HUnresolvedStaticFieldSet(value, field_type, field_index, dex_pc));
+  } else {
+    current_block_->AddInstruction(
+        new (arena_) HUnresolvedStaticFieldGet(field_type, field_index, dex_pc));
+    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
+  }
+}
 bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
                                            uint32_t dex_pc,
                                            bool is_put) {
@@ -1299,10 +1340,13 @@
       soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
 
   if (resolved_field == nullptr) {
-    MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
-    return false;
+    MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+    Primitive::Type field_type = GetFieldAccessType(*dex_file_, field_index);
+    BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+    return true;
   }
 
+  Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
   const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
   Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
       outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
@@ -1317,6 +1361,7 @@
     // The compiler driver cannot currently understand multiple dex caches involved. Just bailout.
     return false;
   } else {
+    // TODO: This is rather expensive. Perf it and cache the results if needed.
     std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
         outer_dex_cache.Get(),
         GetCompilingClass(),
@@ -1325,7 +1370,9 @@
         &storage_index);
     bool can_easily_access = is_put ? pair.second : pair.first;
     if (!can_easily_access) {
-      return false;
+      MaybeRecordStat(MethodCompilationStat::kUnresolvedFieldNotAFastAccess);
+      BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+      return true;
     }
   }
 
@@ -1338,7 +1385,8 @@
                                                  storage_index,
                                                  *dex_compilation_unit_->GetDexFile(),
                                                  is_outer_class,
-                                                 dex_pc);
+                                                 dex_pc,
+                                                 /*needs_access_check*/ false);
   current_block_->AddInstruction(constant);
 
   HInstruction* cls = constant;
@@ -1346,8 +1394,6 @@
     cls = new (arena_) HClinitCheck(constant, dex_pc);
     current_block_->AddInstruction(cls);
   }
-
-  Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
   if (is_put) {
     // We need to keep the class alive before loading the value.
     Temporaries temps(graph_);
@@ -1571,7 +1617,9 @@
 
 static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
     SHARED_REQUIRES(Locks::mutator_lock_) {
-  if (cls->IsInterface()) {
+  if (cls.Get() == nullptr) {
+    return TypeCheckKind::kUnresolvedCheck;
+  } else if (cls->IsInterface()) {
     return TypeCheckKind::kInterfaceCheck;
   } else if (cls->IsArrayClass()) {
     if (cls->GetComponentType()->IsObjectClass()) {
@@ -1590,11 +1638,20 @@
   }
 }
 
-bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction,
+void HGraphBuilder::BuildTypeCheck(const Instruction& instruction,
                                    uint8_t destination,
                                    uint8_t reference,
                                    uint16_t type_index,
                                    uint32_t dex_pc) {
+  bool type_known_final, type_known_abstract, use_declaring_class;
+  bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+      dex_compilation_unit_->GetDexMethodIndex(),
+      *dex_compilation_unit_->GetDexFile(),
+      type_index,
+      &type_known_final,
+      &type_known_abstract,
+      &use_declaring_class);
+
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(
@@ -1602,22 +1659,14 @@
           soa.Self(), *dex_compilation_unit_->GetDexFile())));
   Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
 
-  if ((resolved_class.Get() == nullptr) ||
-       // TODO: Remove this check once the compiler actually knows which
-       // ArtMethod it is compiling.
-      (GetCompilingClass() == nullptr) ||
-      !GetCompilingClass()->CanAccess(resolved_class.Get())) {
-    MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
-    return false;
-  }
-
   HInstruction* object = LoadLocal(reference, Primitive::kPrimNot, dex_pc);
   HLoadClass* cls = new (arena_) HLoadClass(
       graph_->GetCurrentMethod(),
       type_index,
       *dex_compilation_unit_->GetDexFile(),
       IsOutermostCompilingClass(type_index),
-      dex_pc);
+      dex_pc,
+      !can_access);
   current_block_->AddInstruction(cls);
 
   // The class needs a temporary before being used by the type check.
@@ -1632,7 +1681,6 @@
     DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
     current_block_->AddInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc));
   }
-  return true;
 }
 
 bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index) const {
@@ -2747,16 +2795,13 @@
       bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
           dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
           &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
-      if (!can_access) {
-        MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
-        return false;
-      }
       current_block_->AddInstruction(new (arena_) HLoadClass(
           graph_->GetCurrentMethod(),
           type_index,
           *dex_compilation_unit_->GetDexFile(),
           IsOutermostCompilingClass(type_index),
-          dex_pc));
+          dex_pc,
+          !can_access));
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
@@ -2783,18 +2828,14 @@
       uint8_t destination = instruction.VRegA_22c();
       uint8_t reference = instruction.VRegB_22c();
       uint16_t type_index = instruction.VRegC_22c();
-      if (!BuildTypeCheck(instruction, destination, reference, type_index, dex_pc)) {
-        return false;
-      }
+      BuildTypeCheck(instruction, destination, reference, type_index, dex_pc);
       break;
     }
 
     case Instruction::CHECK_CAST: {
       uint8_t reference = instruction.VRegA_21c();
       uint16_t type_index = instruction.VRegB_21c();
-      if (!BuildTypeCheck(instruction, -1, reference, type_index, dex_pc)) {
-        return false;
-      }
+      BuildTypeCheck(instruction, -1, reference, type_index, dex_pc);
       break;
     }
 
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 4c8e3d0..6910d51 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -187,6 +187,10 @@
   // Builds an instance field access node and returns whether the instruction is supported.
   bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
 
+  void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+                                        uint32_t dex_pc,
+                                        bool is_put,
+                                        Primitive::Type field_type);
   // Builds a static field access node and returns whether the instruction is supported.
   bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
 
@@ -232,8 +236,7 @@
                               uint32_t dex_pc);
 
   // Builds a `HInstanceOf`, or a `HCheckCast` instruction.
-  // Returns whether we succeeded in building the instruction.
-  bool BuildTypeCheck(const Instruction& instruction,
+  void BuildTypeCheck(const Instruction& instruction,
                       uint8_t destination,
                       uint8_t reference,
                       uint16_t type_index,
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index be05691..1da2a07 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -413,6 +413,151 @@
   InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
 }
 
+void CodeGenerator::CreateUnresolvedFieldLocationSummary(
+    HInstruction* field_access,
+    Primitive::Type field_type,
+    const FieldAccessCallingConvention& calling_convention) {
+  bool is_instance = field_access->IsUnresolvedInstanceFieldGet()
+      || field_access->IsUnresolvedInstanceFieldSet();
+  bool is_get = field_access->IsUnresolvedInstanceFieldGet()
+      || field_access->IsUnresolvedStaticFieldGet();
+
+  ArenaAllocator* allocator = field_access->GetBlock()->GetGraph()->GetArena();
+  LocationSummary* locations =
+      new (allocator) LocationSummary(field_access, LocationSummary::kCall);
+
+  locations->AddTemp(calling_convention.GetFieldIndexLocation());
+
+  if (is_instance) {
+    // Add the `this` object for instance field accesses.
+    locations->SetInAt(0, calling_convention.GetObjectLocation());
+  }
+
+  // Note that pSetXXStatic/pGetXXStatic always takes/returns an int or int64
+  // regardless of the the type. Because of that we forced to special case
+  // the access to floating point values.
+  if (is_get) {
+    if (Primitive::IsFloatingPointType(field_type)) {
+      // The return value will be stored in regular registers while register
+      // allocator expects it in a floating point register.
+      // Note We don't need to request additional temps because the return
+      // register(s) are already blocked due the call and they may overlap with
+      // the input or field index.
+      // The transfer between the two will be done at codegen level.
+      locations->SetOut(calling_convention.GetFpuLocation(field_type));
+    } else {
+      locations->SetOut(calling_convention.GetReturnLocation(field_type));
+    }
+  } else {
+     size_t set_index = is_instance ? 1 : 0;
+     if (Primitive::IsFloatingPointType(field_type)) {
+      // The set value comes from a float location while the calling convention
+      // expects it in a regular register location. Allocate a temp for it and
+      // make the transfer at codegen.
+      AddLocationAsTemp(calling_convention.GetSetValueLocation(field_type, is_instance), locations);
+      locations->SetInAt(set_index, calling_convention.GetFpuLocation(field_type));
+    } else {
+      locations->SetInAt(set_index,
+          calling_convention.GetSetValueLocation(field_type, is_instance));
+    }
+  }
+}
+
+void CodeGenerator::GenerateUnresolvedFieldAccess(
+    HInstruction* field_access,
+    Primitive::Type field_type,
+    uint32_t field_index,
+    uint32_t dex_pc,
+    const FieldAccessCallingConvention& calling_convention) {
+  LocationSummary* locations = field_access->GetLocations();
+
+  MoveConstant(locations->GetTemp(0), field_index);
+
+  bool is_instance = field_access->IsUnresolvedInstanceFieldGet()
+      || field_access->IsUnresolvedInstanceFieldSet();
+  bool is_get = field_access->IsUnresolvedInstanceFieldGet()
+      || field_access->IsUnresolvedStaticFieldGet();
+
+  if (!is_get && Primitive::IsFloatingPointType(field_type)) {
+    // Copy the float value to be set into the calling convention register.
+    // Note that using directly the temp location is problematic as we don't
+    // support temp register pairs. To avoid boilerplate conversion code, use
+    // the location from the calling convention.
+    MoveLocation(calling_convention.GetSetValueLocation(field_type, is_instance),
+                 locations->InAt(is_instance ? 1 : 0),
+                 (Primitive::Is64BitType(field_type) ? Primitive::kPrimLong : Primitive::kPrimInt));
+  }
+
+  QuickEntrypointEnum entrypoint = kQuickSet8Static;  // Initialize to anything to avoid warnings.
+  switch (field_type) {
+    case Primitive::kPrimBoolean:
+      entrypoint = is_instance
+          ? (is_get ? kQuickGetBooleanInstance : kQuickSet8Instance)
+          : (is_get ? kQuickGetBooleanStatic : kQuickSet8Static);
+      break;
+    case Primitive::kPrimByte:
+      entrypoint = is_instance
+          ? (is_get ? kQuickGetByteInstance : kQuickSet8Instance)
+          : (is_get ? kQuickGetByteStatic : kQuickSet8Static);
+      break;
+    case Primitive::kPrimShort:
+      entrypoint = is_instance
+          ? (is_get ? kQuickGetShortInstance : kQuickSet16Instance)
+          : (is_get ? kQuickGetShortStatic : kQuickSet16Static);
+      break;
+    case Primitive::kPrimChar:
+      entrypoint = is_instance
+          ? (is_get ? kQuickGetCharInstance : kQuickSet16Instance)
+          : (is_get ? kQuickGetCharStatic : kQuickSet16Static);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      entrypoint = is_instance
+          ? (is_get ? kQuickGet32Instance : kQuickSet32Instance)
+          : (is_get ? kQuickGet32Static : kQuickSet32Static);
+      break;
+    case Primitive::kPrimNot:
+      entrypoint = is_instance
+          ? (is_get ? kQuickGetObjInstance : kQuickSetObjInstance)
+          : (is_get ? kQuickGetObjStatic : kQuickSetObjStatic);
+      break;
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
+      entrypoint = is_instance
+          ? (is_get ? kQuickGet64Instance : kQuickSet64Instance)
+          : (is_get ? kQuickGet64Static : kQuickSet64Static);
+      break;
+    default:
+      LOG(FATAL) << "Invalid type " << field_type;
+  }
+  InvokeRuntime(entrypoint, field_access, dex_pc, nullptr);
+
+  if (is_get && Primitive::IsFloatingPointType(field_type)) {
+    MoveLocation(locations->Out(), calling_convention.GetReturnLocation(field_type), field_type);
+  }
+}
+
+void CodeGenerator::CreateLoadClassLocationSummary(HLoadClass* cls,
+                                                   Location runtime_type_index_location,
+                                                   Location runtime_return_location) {
+  ArenaAllocator* allocator = cls->GetBlock()->GetGraph()->GetArena();
+  LocationSummary::CallKind call_kind = cls->NeedsAccessCheck()
+      ? LocationSummary::kCall
+      : (cls->CanCallRuntime()
+          ? LocationSummary::kCallOnSlowPath
+          : LocationSummary::kNoCall);
+  LocationSummary* locations = new (allocator) LocationSummary(cls, call_kind);
+  if (cls->NeedsAccessCheck()) {
+    locations->SetInAt(0, Location::NoLocation());
+    locations->AddTemp(runtime_type_index_location);
+    locations->SetOut(runtime_return_location);
+  } else {
+    locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetOut(Location::RequiresRegister());
+  }
+}
+
+
 void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const {
   // The DCHECKS below check that a register is not specified twice in
   // the summary. The out location can overlap with an input, so we need
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 5da0e59..0a36989 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -143,6 +143,22 @@
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
 };
 
+class FieldAccessCallingConvention {
+ public:
+  virtual Location GetObjectLocation() const = 0;
+  virtual Location GetFieldIndexLocation() const = 0;
+  virtual Location GetReturnLocation(Primitive::Type type) const = 0;
+  virtual Location GetSetValueLocation(Primitive::Type type, bool is_instance) const = 0;
+  virtual Location GetFpuLocation(Primitive::Type type) const = 0;
+  virtual ~FieldAccessCallingConvention() {}
+
+ protected:
+  FieldAccessCallingConvention() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConvention);
+};
+
 class CodeGenerator {
  public:
   // Compiles the graph to executable instructions. Returns whether the compilation
@@ -177,6 +193,9 @@
   virtual void Bind(HBasicBlock* block) = 0;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
   virtual void MoveConstant(Location destination, int32_t value) = 0;
+  virtual void MoveLocation(Location dst, Location src, Primitive::Type dst_type) = 0;
+  virtual void AddLocationAsTemp(Location location, LocationSummary* locations) = 0;
+
   virtual Assembler* GetAssembler() = 0;
   virtual const Assembler& GetAssembler() const = 0;
   virtual size_t GetWordSize() const = 0;
@@ -385,6 +404,23 @@
 
   void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke);
 
+  void CreateUnresolvedFieldLocationSummary(
+      HInstruction* field_access,
+      Primitive::Type field_type,
+      const FieldAccessCallingConvention& calling_convention);
+
+  void GenerateUnresolvedFieldAccess(
+      HInstruction* field_access,
+      Primitive::Type field_type,
+      uint32_t field_index,
+      uint32_t dex_pc,
+      const FieldAccessCallingConvention& calling_convention);
+
+  // TODO: This overlaps a bit with MoveFromReturnRegister. Refactor for a better design.
+  static void CreateLoadClassLocationSummary(HLoadClass* cls,
+                                             Location runtime_type_index_location,
+                                             Location runtime_return_location);
+
   void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; }
   DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; }
 
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index a7dbb53..08d8d88 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -473,12 +473,8 @@
                     kNumberOfRegisterPairs,
                     ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
                                         arraysize(kCoreCalleeSaves)),
-                    graph->IsDebuggable()
-                        // If the graph is debuggable, we need to save the fpu registers ourselves,
-                        // as the stubs do not do it.
-                        ? 0
-                        : ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
-                                              arraysize(kFpuCalleeSaves)),
+                    ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
+                                        arraysize(kFpuCalleeSaves)),
                     compiler_options,
                     stats),
       block_labels_(nullptr),
@@ -611,7 +607,12 @@
     }
 
     blocked_core_registers_[kCoreSavedRegisterForBaseline] = false;
+  }
 
+  if (is_baseline || GetGraph()->IsDebuggable()) {
+    // Stubs do not save callee-save floating point registers. If the graph
+    // is debuggable, we need to deal with these registers differently. For
+    // now, just block them.
     for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
       blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
     }
@@ -906,6 +907,10 @@
           Primitive::kPrimInt);
     } else if (source.IsFpuRegister()) {
       UNIMPLEMENTED(FATAL);
+    } else if (source.IsFpuRegisterPair()) {
+      __ vmovrrd(destination.AsRegisterPairLow<Register>(),
+                 destination.AsRegisterPairHigh<Register>(),
+                 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
     } else {
       DCHECK(source.IsDoubleStackSlot());
       DCHECK(ExpectedPairLayout(destination));
@@ -917,6 +922,10 @@
       __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
                          SP,
                          source.GetStackIndex());
+    } else if (source.IsRegisterPair()) {
+      __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+                 source.AsRegisterPairLow<Register>(),
+                 source.AsRegisterPairHigh<Register>());
     } else {
       UNIMPLEMENTED(FATAL);
     }
@@ -1038,6 +1047,25 @@
   __ LoadImmediate(location.AsRegister<Register>(), value);
 }
 
+void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
+  if (Primitive::Is64BitType(dst_type)) {
+    Move64(dst, src);
+  } else {
+    Move32(dst, src);
+  }
+}
+
+void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) {
+  if (location.IsRegister()) {
+    locations->AddTemp(location);
+  } else if (location.IsRegisterPair()) {
+    locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
+    locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
+  } else {
+    UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+  }
+}
+
 void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint,
                                      HInstruction* instruction,
                                      uint32_t dex_pc,
@@ -3605,6 +3633,74 @@
   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
 }
 
+void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderARM::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderARM::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionARM calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
   LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
       ? LocationSummary::kCallOnSlowPath
@@ -4372,17 +4468,24 @@
 }
 
 void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
-  LocationSummary::CallKind call_kind = cls->CanCallRuntime()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  InvokeRuntimeCallingConvention calling_convention;
+  CodeGenerator::CreateLoadClassLocationSummary(
+      cls,
+      Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+      Location::RegisterLocation(R0));
 }
 
 void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
+  if (cls->NeedsAccessCheck()) {
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+                            cls,
+                            cls->GetDexPc(),
+                            nullptr);
+    return;
+  }
+
   Register out = locations->Out().AsRegister<Register>();
   Register current_method = locations->InAt(0).AsRegister<Register>();
   if (cls->IsReferrersClass()) {
@@ -4508,6 +4611,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind = LocationSummary::kNoCall;
       break;
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCall;
       break;
@@ -4548,10 +4652,11 @@
     __ CompareAndBranchIfZero(obj, &zero);
   }
 
-  // In case of an interface check, we put the object class into the object register.
+  // In case of an interface/unresolved check, we put the object class into the object register.
   // This is safe, as the register is caller-save, and the object must be in another
   // register if it survives the runtime call.
-  Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+  Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+      (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
       ? obj
       : out;
   __ LoadFromOffset(kLoadWord, target, obj, class_offset);
@@ -4632,7 +4737,7 @@
       }
       break;
     }
-
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default: {
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -4673,6 +4778,7 @@
           ? LocationSummary::kCallOnSlowPath
           : LocationSummary::kNoCall;
       break;
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCall;
       break;
@@ -4777,6 +4883,7 @@
       __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
       break;
     }
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default:
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 111112e..16d1d38 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -96,6 +96,38 @@
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARM);
 };
 
+class FieldAccessCallingConventionARM : public FieldAccessCallingConvention {
+ public:
+  FieldAccessCallingConventionARM() {}
+
+  Location GetObjectLocation() const OVERRIDE {
+    return Location::RegisterLocation(R1);
+  }
+  Location GetFieldIndexLocation() const OVERRIDE {
+    return Location::RegisterLocation(R0);
+  }
+  Location GetReturnLocation(Primitive::Type type) const OVERRIDE {
+    return Primitive::Is64BitType(type)
+        ? Location::RegisterPairLocation(R0, R1)
+        : Location::RegisterLocation(R0);
+  }
+  Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+    return Primitive::Is64BitType(type)
+        ? Location::RegisterPairLocation(R2, R3)
+        : (is_instance
+            ? Location::RegisterLocation(R2)
+            : Location::RegisterLocation(R1));
+  }
+  Location GetFpuLocation(Primitive::Type type) const OVERRIDE {
+    return Primitive::Is64BitType(type)
+        ? Location::FpuRegisterPairLocation(S0, S1)
+        : Location::FpuRegisterLocation(S0);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionARM);
+};
+
 class ParallelMoveResolverARM : public ParallelMoveResolverWithSwap {
  public:
   ParallelMoveResolverARM(ArenaAllocator* allocator, CodeGeneratorARM* codegen)
@@ -225,6 +257,9 @@
   void Bind(HBasicBlock* block) OVERRIDE;
   void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   void MoveConstant(Location destination, int32_t value) OVERRIDE;
+  void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+  void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
   size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 78ecfde..415b37d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -19,7 +19,6 @@
 #include "arch/arm64/instruction_set_features_arm64.h"
 #include "art_method.h"
 #include "code_generator_utils.h"
-#include "common_arm64.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
@@ -119,11 +118,8 @@
 
   CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize,
       register_set->GetCoreRegisters() & (~callee_saved_core_registers.list()));
-  CPURegList fp_list = CPURegList(
-      CPURegister::kFPRegister,
-      kDRegSize,
-      register_set->GetFloatingPointRegisters()
-          & (~(codegen->GetGraph()->IsDebuggable() ? 0 : callee_saved_fp_registers.list())));
+  CPURegList fp_list = CPURegList(CPURegister::kFPRegister, kDRegSize,
+      register_set->GetFloatingPointRegisters() & (~callee_saved_fp_registers.list()));
 
   MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler();
   UseScratchRegisterScope temps(masm);
@@ -583,9 +579,7 @@
                     kNumberOfAllocatableFPRegisters,
                     kNumberOfAllocatableRegisterPairs,
                     callee_saved_core_registers.list(),
-                    // If the graph is debuggable, we need to save the fpu registers ourselves,
-                    // as the stubs do not do it.
-                    graph->IsDebuggable() ? 0 : callee_saved_fp_registers.list(),
+                    callee_saved_fp_registers.list(),
                     compiler_options,
                     stats),
       block_labels_(nullptr),
@@ -666,7 +660,7 @@
 void ParallelMoveResolverARM64::EmitMove(size_t index) {
   DCHECK_LT(index, moves_.size());
   MoveOperands* move = moves_[index];
-  codegen_->MoveLocation(move->GetDestination(), move->GetSource());
+  codegen_->MoveLocation(move->GetDestination(), move->GetSource(), Primitive::kPrimVoid);
 }
 
 void CodeGeneratorARM64::GenerateFrameEntry() {
@@ -750,7 +744,9 @@
   }
 
   if (instruction->IsCurrentMethod()) {
-    MoveLocation(location, Location::DoubleStackSlot(kCurrentMethodStackOffset));
+    MoveLocation(location,
+                 Location::DoubleStackSlot(kCurrentMethodStackOffset),
+                 Primitive::kPrimVoid);
   } else if (locations != nullptr && locations->Out().Equals(location)) {
     return;
   } else if (instruction->IsIntConstant()
@@ -793,6 +789,14 @@
   __ Mov(RegisterFrom(location, Primitive::kPrimInt), value);
 }
 
+void CodeGeneratorARM64::AddLocationAsTemp(Location location, LocationSummary* locations) {
+  if (location.IsRegister()) {
+    locations->AddTemp(location);
+  } else {
+    UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+  }
+}
+
 Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
   Primitive::Type type = load->GetType();
 
@@ -860,7 +864,12 @@
     while (!reserved_core_baseline_registers.IsEmpty()) {
       blocked_core_registers_[reserved_core_baseline_registers.PopLowestIndex().code()] = true;
     }
+  }
 
+  if (is_baseline || GetGraph()->IsDebuggable()) {
+    // Stubs do not save callee-save floating point registers. If the graph
+    // is debuggable, we need to deal with these registers differently. For
+    // now, just block them.
     CPURegList reserved_fp_baseline_registers = callee_saved_fp_registers;
     while (!reserved_fp_baseline_registers.IsEmpty()) {
       blocked_fpu_registers_[reserved_fp_baseline_registers.PopLowestIndex().code()] = true;
@@ -943,7 +952,9 @@
          (cst->IsDoubleConstant() && type == Primitive::kPrimDouble);
 }
 
-void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Primitive::Type type) {
+void CodeGeneratorARM64::MoveLocation(Location destination,
+                                      Location source,
+                                      Primitive::Type dst_type) {
   if (source.Equals(destination)) {
     return;
   }
@@ -952,7 +963,7 @@
   // locations. When moving from and to a register, the argument type can be
   // used to generate 32bit instead of 64bit moves. In debug mode we also
   // checks the coherency of the locations and the type.
-  bool unspecified_type = (type == Primitive::kPrimVoid);
+  bool unspecified_type = (dst_type == Primitive::kPrimVoid);
 
   if (destination.IsRegister() || destination.IsFpuRegister()) {
     if (unspecified_type) {
@@ -962,30 +973,44 @@
                                   || src_cst->IsFloatConstant()
                                   || src_cst->IsNullConstant()))) {
         // For stack slots and 32bit constants, a 64bit type is appropriate.
-        type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
+        dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
       } else {
         // If the source is a double stack slot or a 64bit constant, a 64bit
         // type is appropriate. Else the source is a register, and since the
         // type has not been specified, we chose a 64bit type to force a 64bit
         // move.
-        type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
+        dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
       }
     }
-    DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) ||
-           (destination.IsRegister() && !Primitive::IsFloatingPointType(type)));
-    CPURegister dst = CPURegisterFrom(destination, type);
+    DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) ||
+           (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type)));
+    CPURegister dst = CPURegisterFrom(destination, dst_type);
     if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
       DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
       __ Ldr(dst, StackOperandFrom(source));
     } else if (source.IsConstant()) {
-      DCHECK(CoherentConstantAndType(source, type));
+      DCHECK(CoherentConstantAndType(source, dst_type));
       MoveConstant(dst, source.GetConstant());
-    } else {
+    } else if (source.IsRegister()) {
       if (destination.IsRegister()) {
-        __ Mov(Register(dst), RegisterFrom(source, type));
+        __ Mov(Register(dst), RegisterFrom(source, dst_type));
       } else {
         DCHECK(destination.IsFpuRegister());
-        __ Fmov(FPRegister(dst), FPRegisterFrom(source, type));
+        Primitive::Type source_type = Primitive::Is64BitType(dst_type)
+            ? Primitive::kPrimLong
+            : Primitive::kPrimInt;
+        __ Fmov(FPRegisterFrom(destination, dst_type), RegisterFrom(source, source_type));
+      }
+    } else {
+      DCHECK(source.IsFpuRegister());
+      if (destination.IsRegister()) {
+        Primitive::Type source_type = Primitive::Is64BitType(dst_type)
+            ? Primitive::kPrimDouble
+            : Primitive::kPrimFloat;
+        __ Fmov(RegisterFrom(destination, dst_type), FPRegisterFrom(source, source_type));
+      } else {
+        DCHECK(destination.IsFpuRegister());
+        __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type));
       }
     }
   } else {  // The destination is not a register. It must be a stack slot.
@@ -993,16 +1018,17 @@
     if (source.IsRegister() || source.IsFpuRegister()) {
       if (unspecified_type) {
         if (source.IsRegister()) {
-          type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
+          dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
         } else {
-          type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
+          dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
         }
       }
-      DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) &&
-             (source.IsFpuRegister() == Primitive::IsFloatingPointType(type)));
-      __ Str(CPURegisterFrom(source, type), StackOperandFrom(destination));
+      DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) &&
+             (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type)));
+      __ Str(CPURegisterFrom(source, dst_type), StackOperandFrom(destination));
     } else if (source.IsConstant()) {
-      DCHECK(unspecified_type || CoherentConstantAndType(source, type)) << source << " " << type;
+      DCHECK(unspecified_type || CoherentConstantAndType(source, dst_type))
+          << source << " " << dst_type;
       UseScratchRegisterScope temps(GetVIXLAssembler());
       HConstant* src_cst = source.GetConstant();
       CPURegister temp;
@@ -2362,6 +2388,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind = LocationSummary::kNoCall;
       break;
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCall;
       break;
@@ -2403,10 +2430,11 @@
     __ Cbz(obj, &zero);
   }
 
-  // In case of an interface check, we put the object class into the object register.
+  // In case of an interface/unresolved check, we put the object class into the object register.
   // This is safe, as the register is caller-save, and the object must be in another
   // register if it survives the runtime call.
-  Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+  Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+      (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
       ? obj
       : out;
   __ Ldr(target, HeapOperand(obj.W(), class_offset));
@@ -2487,7 +2515,7 @@
       }
       break;
     }
-
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default: {
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -2528,6 +2556,7 @@
           ? LocationSummary::kCallOnSlowPath
           : LocationSummary::kNoCall;
       break;
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCall;
       break;
@@ -2633,6 +2662,7 @@
       __ Cbnz(temp, slow_path->GetEntryLabel());
       break;
     }
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default:
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
@@ -2988,14 +3018,23 @@
 }
 
 void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
-  LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath
-                                                              : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  InvokeRuntimeCallingConvention calling_convention;
+  CodeGenerator::CreateLoadClassLocationSummary(
+      cls,
+      LocationFrom(calling_convention.GetRegisterAt(0)),
+      LocationFrom(vixl::x0));
 }
 
 void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
+  if (cls->NeedsAccessCheck()) {
+    codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex());
+    codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+                            cls,
+                            cls->GetDexPc(),
+                            nullptr);
+    return;
+  }
+
   Register out = OutputRegister(cls);
   Register current_method = InputRegisterAt(cls, 0);
   if (cls->IsReferrersClass()) {
@@ -3508,6 +3547,74 @@
   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
 }
 
+void LocationsBuilderARM64::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderARM64::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderARM64::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderARM64::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionARM64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
 void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
   new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
 }
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 7178081..a068b48 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
 
 #include "code_generator.h"
+#include "common_arm64.h"
 #include "dex/compiler_enums.h"
 #include "driver/compiler_options.h"
 #include "nodes.h"
@@ -141,6 +142,34 @@
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARM64);
 };
 
+class FieldAccessCallingConventionARM64 : public FieldAccessCallingConvention {
+ public:
+  FieldAccessCallingConventionARM64() {}
+
+  Location GetObjectLocation() const OVERRIDE {
+    return helpers::LocationFrom(vixl::x1);
+  }
+  Location GetFieldIndexLocation() const OVERRIDE {
+    return helpers::LocationFrom(vixl::x0);
+  }
+  Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return helpers::LocationFrom(vixl::x0);
+  }
+  Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+    return Primitive::Is64BitType(type)
+        ? helpers::LocationFrom(vixl::x2)
+        : (is_instance
+            ? helpers::LocationFrom(vixl::x2)
+            : helpers::LocationFrom(vixl::x1));
+  }
+  Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return helpers::LocationFrom(vixl::d0);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionARM64);
+};
+
 class InstructionCodeGeneratorARM64 : public HGraphVisitor {
  public:
   InstructionCodeGeneratorARM64(HGraph* graph, CodeGeneratorARM64* codegen);
@@ -334,10 +363,9 @@
   // Code generation helpers.
   void MoveConstant(vixl::CPURegister destination, HConstant* constant);
   void MoveConstant(Location destination, int32_t value) OVERRIDE;
-  // The type is optional. When specified it must be coherent with the
-  // locations, and is used for optimisation and debugging.
-  void MoveLocation(Location destination, Location source,
-                    Primitive::Type type = Primitive::kPrimVoid);
+  void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+  void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
   void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src);
   void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
   void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index ad0a39c..756336d 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -617,7 +617,7 @@
 
 void CodeGeneratorMIPS64::MoveLocation(Location destination,
                                        Location source,
-                                       Primitive::Type type) {
+                                       Primitive::Type dst_type) {
   if (source.Equals(destination)) {
     return;
   }
@@ -625,7 +625,7 @@
   // A valid move can always be inferred from the destination and source
   // locations. When moving from and to a register, the argument type can be
   // used to generate 32bit instead of 64bit moves.
-  bool unspecified_type = (type == Primitive::kPrimVoid);
+  bool unspecified_type = (dst_type == Primitive::kPrimVoid);
   DCHECK_EQ(unspecified_type, false);
 
   if (destination.IsRegister() || destination.IsFpuRegister()) {
@@ -636,21 +636,21 @@
                                   || src_cst->IsFloatConstant()
                                   || src_cst->IsNullConstant()))) {
         // For stack slots and 32bit constants, a 64bit type is appropriate.
-        type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
+        dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
       } else {
         // If the source is a double stack slot or a 64bit constant, a 64bit
         // type is appropriate. Else the source is a register, and since the
         // type has not been specified, we chose a 64bit type to force a 64bit
         // move.
-        type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
+        dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
       }
     }
-    DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) ||
-           (destination.IsRegister() && !Primitive::IsFloatingPointType(type)));
+    DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) ||
+           (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type)));
     if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
       // Move to GPR/FPR from stack
       LoadOperandType load_type = source.IsStackSlot() ? kLoadWord : kLoadDoubleword;
-      if (Primitive::IsFloatingPointType(type)) {
+      if (Primitive::IsFloatingPointType(dst_type)) {
         __ LoadFpuFromOffset(load_type,
                              destination.AsFpuRegister<FpuRegister>(),
                              SP,
@@ -665,31 +665,47 @@
     } else if (source.IsConstant()) {
       // Move to GPR/FPR from constant
       GpuRegister gpr = AT;
-      if (!Primitive::IsFloatingPointType(type)) {
+      if (!Primitive::IsFloatingPointType(dst_type)) {
         gpr = destination.AsRegister<GpuRegister>();
       }
-      if (type == Primitive::kPrimInt || type == Primitive::kPrimFloat) {
+      if (dst_type == Primitive::kPrimInt || dst_type == Primitive::kPrimFloat) {
         __ LoadConst32(gpr, GetInt32ValueOf(source.GetConstant()->AsConstant()));
       } else {
         __ LoadConst64(gpr, GetInt64ValueOf(source.GetConstant()->AsConstant()));
       }
-      if (type == Primitive::kPrimFloat) {
+      if (dst_type == Primitive::kPrimFloat) {
         __ Mtc1(gpr, destination.AsFpuRegister<FpuRegister>());
-      } else if (type == Primitive::kPrimDouble) {
+      } else if (dst_type == Primitive::kPrimDouble) {
         __ Dmtc1(gpr, destination.AsFpuRegister<FpuRegister>());
       }
-    } else {
+    } else if (source.IsRegister()) {
       if (destination.IsRegister()) {
         // Move to GPR from GPR
         __ Move(destination.AsRegister<GpuRegister>(), source.AsRegister<GpuRegister>());
       } else {
+        DCHECK(destination.IsFpuRegister());
+        if (Primitive::Is64BitType(dst_type)) {
+          __ Dmtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>());
+        } else {
+          __ Mtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>());
+        }
+      }
+    } else if (source.IsFpuRegister()) {
+      if (destination.IsFpuRegister()) {
         // Move to FPR from FPR
-        if (type == Primitive::kPrimFloat) {
+        if (dst_type == Primitive::kPrimFloat) {
           __ MovS(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
         } else {
-          DCHECK_EQ(type, Primitive::kPrimDouble);
+          DCHECK_EQ(dst_type, Primitive::kPrimDouble);
           __ MovD(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
         }
+      } else {
+        DCHECK(destination.IsRegister());
+        if (Primitive::Is64BitType(dst_type)) {
+          __ Dmfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>());
+        } else {
+          __ Mfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>());
+        }
       }
     }
   } else {  // The destination is not a register. It must be a stack slot.
@@ -697,13 +713,13 @@
     if (source.IsRegister() || source.IsFpuRegister()) {
       if (unspecified_type) {
         if (source.IsRegister()) {
-          type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
+          dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
         } else {
-          type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
+          dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
         }
       }
-      DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) &&
-             (source.IsFpuRegister() == Primitive::IsFloatingPointType(type)));
+      DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) &&
+             (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type)));
       // Move to stack from GPR/FPR
       StoreOperandType store_type = destination.IsStackSlot() ? kStoreWord : kStoreDoubleword;
       if (source.IsRegister()) {
@@ -861,6 +877,14 @@
   __ LoadConst32(location.AsRegister<GpuRegister>(), value);
 }
 
+void CodeGeneratorMIPS64::AddLocationAsTemp(Location location, LocationSummary* locations) {
+  if (location.IsRegister()) {
+    locations->AddTemp(location);
+  } else {
+    UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+  }
+}
+
 Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const {
   Primitive::Type type = load->GetType();
 
@@ -2566,15 +2590,24 @@
 }
 
 void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) {
-  LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath
-                                                              : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  InvokeRuntimeCallingConvention calling_convention;
+  CodeGenerator::CreateLoadClassLocationSummary(
+      cls,
+      Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+      Location::RegisterLocation(A0));
 }
 
 void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
+  if (cls->NeedsAccessCheck()) {
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+                            cls,
+                            cls->GetDexPc(),
+                            nullptr);
+    return;
+  }
+
   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
   GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
   if (cls->IsReferrersClass()) {
@@ -3118,6 +3151,74 @@
   HandleFieldSet(instruction, instruction->GetFieldInfo());
 }
 
+void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionMIPS64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
 void LocationsBuilderMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
   new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
 }
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 16461d6..5e8f9e7 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -106,6 +106,31 @@
   DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
 };
 
+class FieldAccessCallingConventionMIPS64 : public FieldAccessCallingConvention {
+ public:
+  FieldAccessCallingConventionMIPS64() {}
+
+  Location GetObjectLocation() const OVERRIDE {
+    return Location::RegisterLocation(A1);
+  }
+  Location GetFieldIndexLocation() const OVERRIDE {
+    return Location::RegisterLocation(A0);
+  }
+  Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return Location::RegisterLocation(A0);
+  }
+  Location GetSetValueLocation(
+      Primitive::Type type ATTRIBUTE_UNUSED, bool is_instance) const OVERRIDE {
+    return is_instance ? Location::RegisterLocation(A2) : Location::RegisterLocation(A1);
+  }
+  Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return Location::FpuRegisterLocation(F0);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionMIPS64);
+};
+
 class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap {
  public:
   ParallelMoveResolverMIPS64(ArenaAllocator* allocator, CodeGeneratorMIPS64* codegen)
@@ -280,11 +305,13 @@
   void Finalize(CodeAllocator* allocator) OVERRIDE;
 
   // Code generation helpers.
-
-  void MoveLocation(Location destination, Location source, Primitive::Type type);
+  void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
 
   void MoveConstant(Location destination, int32_t value) OVERRIDE;
 
+  void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
+
   void SwapLocations(Location loc1, Location loc2, Primitive::Type type);
 
   // Generate code to invoke a runtime entry point.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 3d97132..5ef7de0 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -827,7 +827,10 @@
           Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
           Primitive::kPrimInt);
     } else if (source.IsFpuRegister()) {
-      LOG(FATAL) << "Unimplemented";
+      XmmRegister src_reg = source.AsFpuRegister<XmmRegister>();
+      __ movd(destination.AsRegisterPairLow<Register>(), src_reg);
+      __ psrlq(src_reg, Immediate(32));
+      __ movd(destination.AsRegisterPairHigh<Register>(), src_reg);
     } else {
       // No conflict possible, so just do the moves.
       DCHECK(source.IsDoubleStackSlot());
@@ -840,6 +843,15 @@
       __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
     } else if (source.IsDoubleStackSlot()) {
       __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
+    } else if (source.IsRegisterPair()) {
+      size_t elem_size = Primitive::ComponentSize(Primitive::kPrimInt);
+      // Create stack space for 2 elements.
+      __ subl(ESP, Immediate(2 * elem_size));
+      __ movl(Address(ESP, 0), source.AsRegisterPairLow<Register>());
+      __ movl(Address(ESP, elem_size), source.AsRegisterPairHigh<Register>());
+      __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+      // And remove the temporary stack space we allocated.
+      __ addl(ESP, Immediate(2 * elem_size));
     } else {
       LOG(FATAL) << "Unimplemented";
     }
@@ -966,6 +978,25 @@
   __ movl(location.AsRegister<Register>(), Immediate(value));
 }
 
+void CodeGeneratorX86::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
+  if (Primitive::Is64BitType(dst_type)) {
+    Move64(dst, src);
+  } else {
+    Move32(dst, src);
+  }
+}
+
+void CodeGeneratorX86::AddLocationAsTemp(Location location, LocationSummary* locations) {
+  if (location.IsRegister()) {
+    locations->AddTemp(location);
+  } else if (location.IsRegisterPair()) {
+    locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
+    locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
+  } else {
+    UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+  }
+}
+
 void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* successor) {
   DCHECK(!successor->IsExitBlock());
 
@@ -4085,6 +4116,74 @@
   HandleFieldGet(instruction, instruction->GetFieldInfo());
 }
 
+void LocationsBuilderX86::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderX86::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderX86::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderX86::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionX86 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
 void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
   LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
       ? LocationSummary::kCallOnSlowPath
@@ -4890,17 +4989,24 @@
 }
 
 void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
-  LocationSummary::CallKind call_kind = cls->CanCallRuntime()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  InvokeRuntimeCallingConvention calling_convention;
+  CodeGenerator::CreateLoadClassLocationSummary(
+      cls,
+      Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+      Location::RegisterLocation(EAX));
 }
 
 void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
+  if (cls->NeedsAccessCheck()) {
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+                            cls,
+                            cls->GetDexPc(),
+                            nullptr);
+    return;
+  }
+
   Register out = locations->Out().AsRegister<Register>();
   Register current_method = locations->InAt(0).AsRegister<Register>();
   if (cls->IsReferrersClass()) {
@@ -5022,6 +5128,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind = LocationSummary::kNoCall;
       break;
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCall;
       break;
@@ -5062,10 +5169,11 @@
     __ j(kEqual, &zero);
   }
 
-  // In case of an interface check, we put the object class into the object register.
+  // In case of an interface/unresolved check, we put the object class into the object register.
   // This is safe, as the register is caller-save, and the object must be in another
   // register if it survives the runtime call.
-  Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+  Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+      (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
       ? obj
       : out;
   __ movl(target, Address(obj, class_offset));
@@ -5174,7 +5282,7 @@
       }
       break;
     }
-
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default: {
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -5216,6 +5324,7 @@
           : LocationSummary::kNoCall;
       break;
     case TypeCheckKind::kInterfaceCheck:
+    case TypeCheckKind::kUnresolvedCheck:
       call_kind = LocationSummary::kCall;
       break;
     case TypeCheckKind::kArrayCheck:
@@ -5342,6 +5451,7 @@
       __ j(kNotEqual, slow_path->GetEntryLabel());
       break;
     }
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default:
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 2c2fc65..ae2d84f 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -91,6 +91,36 @@
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorX86);
 };
 
+class FieldAccessCallingConventionX86 : public FieldAccessCallingConvention {
+ public:
+  FieldAccessCallingConventionX86() {}
+
+  Location GetObjectLocation() const OVERRIDE {
+    return Location::RegisterLocation(ECX);
+  }
+  Location GetFieldIndexLocation() const OVERRIDE {
+    return Location::RegisterLocation(EAX);
+  }
+  Location GetReturnLocation(Primitive::Type type) const OVERRIDE {
+    return Primitive::Is64BitType(type)
+        ? Location::RegisterPairLocation(EAX, EDX)
+        : Location::RegisterLocation(EAX);
+  }
+  Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+    return Primitive::Is64BitType(type)
+        ? Location::RegisterPairLocation(EDX, EBX)
+        : (is_instance
+            ? Location::RegisterLocation(EDX)
+            : Location::RegisterLocation(ECX));
+  }
+  Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return Location::FpuRegisterLocation(XMM0);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionX86);
+};
+
 class ParallelMoveResolverX86 : public ParallelMoveResolverWithSwap {
  public:
   ParallelMoveResolverX86(ArenaAllocator* allocator, CodeGeneratorX86* codegen)
@@ -228,6 +258,9 @@
   void Bind(HBasicBlock* block) OVERRIDE;
   void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   void MoveConstant(Location destination, int32_t value) OVERRIDE;
+  void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+  void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
   size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 6ea6138..272d86f 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -990,6 +990,19 @@
   Load64BitValue(location.AsRegister<CpuRegister>(), static_cast<int64_t>(value));
 }
 
+void CodeGeneratorX86_64::MoveLocation(
+    Location dst, Location src, Primitive::Type dst_type ATTRIBUTE_UNUSED) {
+  Move(dst, src);
+}
+
+void CodeGeneratorX86_64::AddLocationAsTemp(Location location, LocationSummary* locations) {
+  if (location.IsRegister()) {
+    locations->AddTemp(location);
+  } else {
+    UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+  }
+}
+
 void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
   DCHECK(!successor->IsExitBlock());
 
@@ -3849,6 +3862,74 @@
   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
 }
 
+void LocationsBuilderX86_64::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedInstanceFieldGet(
+    HUnresolvedInstanceFieldGet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderX86_64::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedInstanceFieldSet(
+    HUnresolvedInstanceFieldSet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderX86_64::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedStaticFieldGet(
+    HUnresolvedStaticFieldGet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
+void LocationsBuilderX86_64::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->CreateUnresolvedFieldLocationSummary(
+      instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedStaticFieldSet(
+    HUnresolvedStaticFieldSet* instruction) {
+  FieldAccessCallingConventionX86_64 calling_convention;
+  codegen_->GenerateUnresolvedFieldAccess(instruction,
+                                          instruction->GetFieldType(),
+                                          instruction->GetFieldIndex(),
+                                          instruction->GetDexPc(),
+                                          calling_convention);
+}
+
 void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
   LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
       ? LocationSummary::kCallOnSlowPath
@@ -4613,17 +4694,24 @@
 }
 
 void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
-  LocationSummary::CallKind call_kind = cls->CanCallRuntime()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  InvokeRuntimeCallingConvention calling_convention;
+  CodeGenerator::CreateLoadClassLocationSummary(
+      cls,
+      Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+      Location::RegisterLocation(RAX));
 }
 
 void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
+  if (cls->NeedsAccessCheck()) {
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+                            cls,
+                            cls->GetDexPc(),
+                            nullptr);
+    return;
+  }
+
   CpuRegister out = locations->Out().AsRegister<CpuRegister>();
   CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
   if (cls->IsReferrersClass()) {
@@ -4736,6 +4824,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind = LocationSummary::kNoCall;
       break;
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCall;
       break;
@@ -4776,10 +4865,11 @@
     __ j(kEqual, &zero);
   }
 
-  // In case of an interface check, we put the object class into the object register.
+  // In case of an interface/unresolved check, we put the object class into the object register.
   // This is safe, as the register is caller-save, and the object must be in another
   // register if it survives the runtime call.
-  CpuRegister target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+  CpuRegister target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+      (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
       ? obj
       : out;
   __ movl(target, Address(obj, class_offset));
@@ -4893,7 +4983,7 @@
       }
       break;
     }
-
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default: {
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -4934,6 +5024,7 @@
           ? LocationSummary::kCallOnSlowPath
           : LocationSummary::kNoCall;
       break;
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
       call_kind = LocationSummary::kCall;
       break;
@@ -5061,6 +5152,7 @@
       __ j(kNotEqual, slow_path->GetEntryLabel());
       break;
     }
+    case TypeCheckKind::kUnresolvedCheck:
     case TypeCheckKind::kInterfaceCheck:
     default:
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 197ce63..ecc8630 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -70,6 +70,35 @@
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
 };
 
+class FieldAccessCallingConventionX86_64 : public FieldAccessCallingConvention {
+ public:
+  FieldAccessCallingConventionX86_64() {}
+
+  Location GetObjectLocation() const OVERRIDE {
+    return Location::RegisterLocation(RSI);
+  }
+  Location GetFieldIndexLocation() const OVERRIDE {
+    return Location::RegisterLocation(RDI);
+  }
+  Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return Location::RegisterLocation(RAX);
+  }
+  Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+    return Primitive::Is64BitType(type)
+        ? Location::RegisterLocation(RDX)
+        : (is_instance
+            ? Location::RegisterLocation(RDX)
+            : Location::RegisterLocation(RSI));
+  }
+  Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return Location::FpuRegisterLocation(XMM0);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionX86_64);
+};
+
+
 class InvokeDexCallingConventionVisitorX86_64 : public InvokeDexCallingConventionVisitor {
  public:
   InvokeDexCallingConventionVisitorX86_64() {}
@@ -215,6 +244,9 @@
   void Bind(HBasicBlock* block) OVERRIDE;
   void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   void MoveConstant(Location destination, int32_t value) OVERRIDE;
+  void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+  void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
   size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 2c6c3b7..d38f4c8 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -398,6 +398,22 @@
     StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
   }
 
+  void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE {
+    StartAttributeStream("field_type") << field_access->GetFieldType();
+  }
+
+  void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* field_access) OVERRIDE {
+    StartAttributeStream("field_type") << field_access->GetFieldType();
+  }
+
+  void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* field_access) OVERRIDE {
+    StartAttributeStream("field_type") << field_access->GetFieldType();
+  }
+
+  void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* field_access) OVERRIDE {
+    StartAttributeStream("field_type") << field_access->GetFieldType();
+  }
+
   void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
     StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
   }
@@ -485,8 +501,11 @@
         StartAttributeStream("can_be_null")
             << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
         StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
+      } else if (instruction->IsLoadClass()) {
+        StartAttributeStream("klass") << "unresolved";
       } else {
-        DCHECK(!is_after_pass_) << "Type info should be valid after reference type propagation";
+        DCHECK(!is_after_pass_)
+            << "Expected a valid rti after reference type propagation";
       }
     }
     if (disasm_info_ != nullptr) {
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 32f45b5..56f2718 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -47,14 +47,16 @@
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
   HInstruction* to_remove = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
                                                            Primitive::kPrimNot,
@@ -62,7 +64,8 @@
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
   HInstruction* different_offset = block->GetLastInstruction();
   // Kill the value.
   block->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
@@ -72,14 +75,16 @@
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
   HInstruction* use_after_kill = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HExit());
 
@@ -118,7 +123,8 @@
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
 
   block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
   HBasicBlock* then = new (&allocator) HBasicBlock(graph);
@@ -139,7 +145,8 @@
                                                           false,
                                                           kUnknownFieldIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache));
+                                                          dex_cache,
+                                                          0));
   then->AddInstruction(new (&allocator) HGoto());
   else_->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
                                                            Primitive::kPrimBoolean,
@@ -147,7 +154,8 @@
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
   else_->AddInstruction(new (&allocator) HGoto());
   join->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
                                                           Primitive::kPrimBoolean,
@@ -155,7 +163,8 @@
                                                           false,
                                                           kUnknownFieldIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache));
+                                                          dex_cache,
+                                                          0));
   join->AddInstruction(new (&allocator) HExit());
 
   graph->TryBuildingSsa();
@@ -191,7 +200,8 @@
                                                            false,
                                                            kUnknownFieldIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache));
+                                                           dex_cache,
+                                                           0));
   block->AddInstruction(new (&allocator) HGoto());
 
   HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
@@ -212,7 +222,8 @@
                                                                  false,
                                                                  kUnknownFieldIndex,
                                                                  graph->GetDexFile(),
-                                                                 dex_cache));
+                                                                 dex_cache,
+                                                                 0));
   HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
   loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
 
@@ -225,7 +236,8 @@
                                                                false,
                                                                kUnknownFieldIndex,
                                                                graph->GetDexFile(),
-                                                               dex_cache));
+                                                               dex_cache,
+                                                               0));
   HInstruction* field_set = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
                                                                Primitive::kPrimBoolean,
@@ -233,7 +245,8 @@
                                                                false,
                                                                kUnknownFieldIndex,
                                                                graph->GetDexFile(),
-                                                               dex_cache));
+                                                               dex_cache,
+                                                               0));
   HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HGoto());
 
@@ -243,7 +256,8 @@
                                                           false,
                                                           kUnknownFieldIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache));
+                                                          dex_cache,
+                                                          0));
   HInstruction* field_get_in_exit = exit->GetLastInstruction();
   exit->AddInstruction(new (&allocator) HExit());
 
@@ -339,7 +353,8 @@
                                                              false,
                                                              kUnknownFieldIndex,
                                                              graph->GetDexFile(),
-                                                             dex_cache));
+                                                             dex_cache,
+                                                             0));
 
     SideEffectsAnalysis side_effects(graph);
     side_effects.Run();
@@ -362,7 +377,8 @@
                                            false,
                                            kUnknownFieldIndex,
                                            graph->GetDexFile(),
-                                           dex_cache),
+                                           dex_cache,
+                                           0),
         outer_loop_body->GetLastInstruction());
 
     SideEffectsAnalysis side_effects(graph);
@@ -386,7 +402,8 @@
                                            false,
                                            kUnknownFieldIndex,
                                            graph->GetDexFile(),
-                                           dex_cache),
+                                           dex_cache,
+                                           0),
         inner_loop_body->GetLastInstruction());
 
     SideEffectsAnalysis side_effects(graph);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 039029a..f3b5f08 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -43,6 +43,11 @@
 static constexpr size_t kMaximumNumberOfHInstructions = 12;
 
 void HInliner::Run() {
+  const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
+  if ((compiler_options.GetInlineDepthLimit() == 0)
+      || (compiler_options.GetInlineMaxCodeUnits() == 0)) {
+    return;
+  }
   if (graph_->IsDebuggable()) {
     // For simplicity, we currently never inline when the graph is debuggable. This avoids
     // doing some logic in the runtime to discover if a method could have been inlined.
@@ -247,12 +252,14 @@
     return false;
   }
 
-  uint16_t class_def_idx = resolved_method->GetDeclaringClass()->GetDexClassDefIndex();
-  if (!compiler_driver_->IsMethodVerifiedWithoutFailures(
-        resolved_method->GetDexMethodIndex(), class_def_idx, *resolved_method->GetDexFile())) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
-                   << " couldn't be verified, so it cannot be inlined";
-    return false;
+  if (!resolved_method->GetDeclaringClass()->IsVerified()) {
+    uint16_t class_def_idx = resolved_method->GetDeclaringClass()->GetDexClassDefIndex();
+    if (!compiler_driver_->IsMethodVerifiedWithoutFailures(
+          resolved_method->GetDexMethodIndex(), class_def_idx, *resolved_method->GetDexFile())) {
+      VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+                     << " couldn't be verified, so it cannot be inlined";
+      return false;
+    }
   }
 
   if (invoke_instruction->IsInvokeStaticOrDirect() &&
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 3287a0a..86a3ad9 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -216,7 +216,11 @@
   }
 
   ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
-  DCHECK(class_rti.IsValid() && class_rti.IsExact());
+  if (!class_rti.IsValid()) {
+    // Happens when the loaded class is unresolved.
+    return false;
+  }
+  DCHECK(class_rti.IsExact());
   if (class_rti.IsSupertypeOf(obj_rti)) {
     *outcome = true;
     return true;
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 1b4d161..b60905d 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -404,6 +404,29 @@
   GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
 
+  // Some architectures, such as ARM and MIPS (prior to r6), have a
+  // conditional move instruction which only changes the target
+  // (output) register if the condition is true (MIPS prior to r6 had
+  // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
+  // change the target (output) register.  If the condition is true the
+  // output register gets the contents of the "rs" register; otherwise,
+  // the output register is set to zero. One consequence of this is
+  // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
+  // needs to use a pair of SELEQZ/SELNEZ instructions.  After
+  // executing this pair of instructions one of the output registers
+  // from the pair will necessarily contain zero. Then the code ORs the
+  // output registers from the SELEQZ/SELNEZ instructions to get the
+  // final result.
+  //
+  // The initial test to see if the output register is same as the
+  // first input register is needed to make sure that value in the
+  // first input register isn't clobbered before we've finished
+  // computing the output value. The logic in the corresponding else
+  // clause performs the same task but makes sure the second input
+  // register isn't clobbered in the event that it's the same register
+  // as the output register; the else clause also handles the case
+  // where the output register is distinct from both the first, and the
+  // second input registers.
   if (out == lhs) {
     __ Slt(AT, rhs, lhs);
     if (is_min) {
@@ -512,13 +535,12 @@
   CreateFPToFP(arena_, invoke);
 }
 
-// 0x200 - +zero
-// 0x040 - +infinity
-// 0x020 - -zero
-// 0x004 - -infinity
-// 0x002 - quiet NaN
-// 0x001 - signaling NaN
-const constexpr uint16_t CLASS_MASK = 0x267;
+const constexpr uint16_t kFPLeaveUnchanged = kPositiveZero |
+                                             kPositiveInfinity |
+                                             kNegativeZero |
+                                             kNegativeInfinity |
+                                             kQuietNaN |
+                                             kSignalingNaN;
 
 void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
   LocationSummary* locations = invoke->GetLocations();
@@ -534,7 +556,7 @@
   //     }
   __ ClassD(out, in);
   __ Dmfc1(AT, out);
-  __ Andi(AT, AT, CLASS_MASK);       // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
+  __ Andi(AT, AT, kFPLeaveUnchanged);   // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
   __ MovD(out, in);
   __ Bnezc(AT, &done);
 
@@ -583,7 +605,7 @@
   //     }
   __ ClassD(out, in);
   __ Dmfc1(AT, out);
-  __ Andi(AT, AT, CLASS_MASK);       // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
+  __ Andi(AT, AT, kFPLeaveUnchanged);   // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
   __ MovD(out, in);
   __ Bnezc(AT, &done);
 
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index ec4a9ec..558892d 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -106,11 +106,11 @@
   NullHandle<mirror::DexCache> dex_cache;
   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
       parameter_, Primitive::kPrimLong, MemberOffset(10),
-      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
       parameter_, constant_, Primitive::kPrimInt, MemberOffset(20),
-      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
 
   EXPECT_EQ(get_field->GetBlock(), loop_body_);
@@ -127,11 +127,11 @@
   NullHandle<mirror::DexCache> dex_cache;
   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
       parameter_, Primitive::kPrimLong, MemberOffset(10),
-      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
       parameter_, get_field, Primitive::kPrimLong, MemberOffset(10),
-      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+      false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
 
   EXPECT_EQ(get_field->GetBlock(), loop_body_);
@@ -146,7 +146,7 @@
 
   // Populate the loop with instructions: set/get array with different types.
   HInstruction* get_array = new (&allocator_) HArrayGet(
-      parameter_, constant_, Primitive::kPrimLong);
+      parameter_, constant_, Primitive::kPrimLong, 0);
   loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
   HInstruction* set_array = new (&allocator_) HArraySet(
       parameter_, constant_, constant_, Primitive::kPrimInt, 0);
@@ -164,7 +164,7 @@
 
   // Populate the loop with instructions: set/get array with same types.
   HInstruction* get_array = new (&allocator_) HArrayGet(
-      parameter_, constant_, Primitive::kPrimLong);
+      parameter_, constant_, Primitive::kPrimLong, 0);
   loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
   HInstruction* set_array = new (&allocator_) HArraySet(
       parameter_, get_array, constant_, Primitive::kPrimLong, 0);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index d52f592..dbf46ce 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1067,6 +1067,10 @@
   M(Shr, BinaryOperation)                                               \
   M(StaticFieldGet, Instruction)                                        \
   M(StaticFieldSet, Instruction)                                        \
+  M(UnresolvedInstanceFieldGet, Instruction)                            \
+  M(UnresolvedInstanceFieldSet, Instruction)                            \
+  M(UnresolvedStaticFieldGet, Instruction)                              \
+  M(UnresolvedStaticFieldSet, Instruction)                              \
   M(StoreLocal, Instruction)                                            \
   M(Sub, BinaryOperation)                                               \
   M(SuspendCheck, Instruction)                                          \
@@ -1711,7 +1715,7 @@
 
 class HInstruction : public ArenaObject<kArenaAllocInstruction> {
  public:
-  HInstruction(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+  HInstruction(SideEffects side_effects, uint32_t dex_pc)
       : previous_(nullptr),
         next_(nullptr),
         block_(nullptr),
@@ -2068,7 +2072,7 @@
 template<size_t N>
 class HTemplateInstruction: public HInstruction {
  public:
-  HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+  HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc)
       : HInstruction(side_effects, dex_pc), inputs_() {}
   virtual ~HTemplateInstruction() {}
 
@@ -2095,7 +2099,7 @@
 template<>
 class HTemplateInstruction<0>: public HInstruction {
  public:
-  explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+  explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc)
       : HInstruction(side_effects, dex_pc) {}
 
   virtual ~HTemplateInstruction() {}
@@ -2121,7 +2125,7 @@
 template<intptr_t N>
 class HExpression : public HTemplateInstruction<N> {
  public:
-  HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+  HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc)
       : HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {}
   virtual ~HExpression() {}
 
@@ -4206,7 +4210,7 @@
                     uint32_t field_idx,
                     const DexFile& dex_file,
                     Handle<mirror::DexCache> dex_cache,
-                    uint32_t dex_pc = kNoDexPc)
+                    uint32_t dex_pc)
       : HExpression(
             field_type,
             SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
@@ -4252,7 +4256,7 @@
                     uint32_t field_idx,
                     const DexFile& dex_file,
                     Handle<mirror::DexCache> dex_cache,
-                    uint32_t dex_pc = kNoDexPc)
+                    uint32_t dex_pc)
       : HTemplateInstruction(
           SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
@@ -4287,7 +4291,7 @@
   HArrayGet(HInstruction* array,
             HInstruction* index,
             Primitive::Type type,
-            uint32_t dex_pc = kNoDexPc)
+            uint32_t dex_pc)
       : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
@@ -4403,7 +4407,7 @@
 
 class HArrayLength : public HExpression<1> {
  public:
-  explicit HArrayLength(HInstruction* array, uint32_t dex_pc = kNoDexPc)
+  explicit HArrayLength(HInstruction* array, uint32_t dex_pc)
       : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
     // Note that arrays do not change length, so the instruction does not
     // depend on any write.
@@ -4509,12 +4513,14 @@
              uint16_t type_index,
              const DexFile& dex_file,
              bool is_referrers_class,
-             uint32_t dex_pc)
+             uint32_t dex_pc,
+             bool needs_access_check)
       : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
         type_index_(type_index),
         dex_file_(dex_file),
         is_referrers_class_(is_referrers_class),
         generate_clinit_check_(false),
+        needs_access_check_(needs_access_check),
         loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
     SetRawInputAt(0, current_method);
   }
@@ -4534,19 +4540,22 @@
   bool NeedsEnvironment() const OVERRIDE {
     // Will call runtime and load the class if the class is not loaded yet.
     // TODO: finer grain decision.
-    return !is_referrers_class_;
+    return !is_referrers_class_ || needs_access_check_;
   }
 
   bool MustGenerateClinitCheck() const {
     return generate_clinit_check_;
   }
-
   void SetMustGenerateClinitCheck(bool generate_clinit_check) {
     generate_clinit_check_ = generate_clinit_check;
   }
 
   bool CanCallRuntime() const {
-    return MustGenerateClinitCheck() || !is_referrers_class_;
+    return MustGenerateClinitCheck() || !is_referrers_class_ || needs_access_check_;
+  }
+
+  bool NeedsAccessCheck() const {
+    return needs_access_check_;
   }
 
   bool CanThrow() const OVERRIDE {
@@ -4582,6 +4591,7 @@
   // Whether this instruction must generate the initialization check.
   // Used for code generation.
   bool generate_clinit_check_;
+  bool needs_access_check_;
 
   ReferenceTypeInfo loaded_class_rti_;
 
@@ -4665,7 +4675,7 @@
                   uint32_t field_idx,
                   const DexFile& dex_file,
                   Handle<mirror::DexCache> dex_cache,
-                  uint32_t dex_pc = kNoDexPc)
+                  uint32_t dex_pc)
       : HExpression(
             field_type,
             SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
@@ -4708,7 +4718,7 @@
                   uint32_t field_idx,
                   const DexFile& dex_file,
                   Handle<mirror::DexCache> dex_cache,
-                  uint32_t dex_pc = kNoDexPc)
+                  uint32_t dex_pc)
       : HTemplateInstruction(
           SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
@@ -4735,6 +4745,112 @@
   DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
 };
 
+class HUnresolvedInstanceFieldGet : public HExpression<1> {
+ public:
+  HUnresolvedInstanceFieldGet(HInstruction* obj,
+                              Primitive::Type field_type,
+                              uint32_t field_index,
+                              uint32_t dex_pc)
+      : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+        field_index_(field_index) {
+    SetRawInputAt(0, obj);
+  }
+
+  bool NeedsEnvironment() const OVERRIDE { return true; }
+  bool CanThrow() const OVERRIDE { return true; }
+
+  Primitive::Type GetFieldType() const { return GetType(); }
+  uint32_t GetFieldIndex() const { return field_index_; }
+
+  DECLARE_INSTRUCTION(UnresolvedInstanceFieldGet);
+
+ private:
+  const uint32_t field_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet);
+};
+
+class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> {
+ public:
+  HUnresolvedInstanceFieldSet(HInstruction* obj,
+                              HInstruction* value,
+                              Primitive::Type field_type,
+                              uint32_t field_index,
+                              uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+        field_type_(field_type),
+        field_index_(field_index) {
+    DCHECK_EQ(field_type, value->GetType());
+    SetRawInputAt(0, obj);
+    SetRawInputAt(1, value);
+  }
+
+  bool NeedsEnvironment() const OVERRIDE { return true; }
+  bool CanThrow() const OVERRIDE { return true; }
+
+  Primitive::Type GetFieldType() const { return field_type_; }
+  uint32_t GetFieldIndex() const { return field_index_; }
+
+  DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet);
+
+ private:
+  const Primitive::Type field_type_;
+  const uint32_t field_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet);
+};
+
+class HUnresolvedStaticFieldGet : public HExpression<0> {
+ public:
+  HUnresolvedStaticFieldGet(Primitive::Type field_type,
+                            uint32_t field_index,
+                            uint32_t dex_pc)
+      : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+        field_index_(field_index) {
+  }
+
+  bool NeedsEnvironment() const OVERRIDE { return true; }
+  bool CanThrow() const OVERRIDE { return true; }
+
+  Primitive::Type GetFieldType() const { return GetType(); }
+  uint32_t GetFieldIndex() const { return field_index_; }
+
+  DECLARE_INSTRUCTION(UnresolvedStaticFieldGet);
+
+ private:
+  const uint32_t field_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet);
+};
+
+class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> {
+ public:
+  HUnresolvedStaticFieldSet(HInstruction* value,
+                            Primitive::Type field_type,
+                            uint32_t field_index,
+                            uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+        field_type_(field_type),
+        field_index_(field_index) {
+    DCHECK_EQ(field_type, value->GetType());
+    SetRawInputAt(0, value);
+  }
+
+  bool NeedsEnvironment() const OVERRIDE { return true; }
+  bool CanThrow() const OVERRIDE { return true; }
+
+  Primitive::Type GetFieldType() const { return field_type_; }
+  uint32_t GetFieldIndex() const { return field_index_; }
+
+  DECLARE_INSTRUCTION(UnresolvedStaticFieldSet);
+
+ private:
+  const Primitive::Type field_type_;
+  const uint32_t field_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet);
+};
+
 // Implement the move-exception DEX instruction.
 class HLoadException : public HExpression<0> {
  public:
@@ -4787,6 +4903,7 @@
  * or `HCheckCast`.
  */
 enum class TypeCheckKind {
+  kUnresolvedCheck,       // Check against an unresolved type.
   kExactCheck,            // Can do a single class compare.
   kClassHierarchyCheck,   // Can just walk the super class chain.
   kAbstractClassCheck,    // Can just walk the super class chain, starting one up.
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index ddc5730..f7cc872 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -23,7 +23,8 @@
 class HX86ComputeBaseMethodAddress : public HExpression<0> {
  public:
   // Treat the value as an int32_t, but it is really a 32 bit native pointer.
-  HX86ComputeBaseMethodAddress() : HExpression(Primitive::kPrimInt, SideEffects::None()) {}
+  HX86ComputeBaseMethodAddress()
+      : HExpression(Primitive::kPrimInt, SideEffects::None(), kNoDexPc) {}
 
   DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress);
 
@@ -37,7 +38,7 @@
   HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
                             HConstant* constant,
                             bool needs_materialization = true)
-      : HExpression(constant->GetType(), SideEffects::None()),
+      : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc),
         needs_materialization_(needs_materialization) {
     SetRawInputAt(0, method_base);
     SetRawInputAt(1, constant);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index dbfbd96..12d6b03 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -310,9 +310,6 @@
 
   std::unique_ptr<std::ostream> visualizer_output_;
 
-  // Delegate to Quick in case the optimizing compiler cannot compile a method.
-  std::unique_ptr<Compiler> delegate_;
-
   DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
 };
 
@@ -321,11 +318,9 @@
 OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
     : Compiler(driver, kMaximumCompilationTimeBeforeWarning),
       run_optimizations_(
-          driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
-      delegate_(Create(driver, Compiler::Kind::kQuick)) {}
+          driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime) {}
 
 void OptimizingCompiler::Init() {
-  delegate_->Init();
   // Enable C1visualizer output. Must be done in Init() because the compiler
   // driver is not fully initialized when passed to the compiler's constructor.
   CompilerDriver* driver = GetCompilerDriver();
@@ -344,7 +339,6 @@
 }
 
 void OptimizingCompiler::UnInit() const {
-  delegate_->UnInit();
 }
 
 OptimizingCompiler::~OptimizingCompiler() {
@@ -353,8 +347,7 @@
   }
 }
 
-void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu) const {
-  delegate_->InitCompilationUnit(cu);
+void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const {
 }
 
 bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED,
@@ -569,6 +562,9 @@
   return linker_patches;
 }
 
+// TODO: The function below uses too much stack space. Bug: 24698147
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
 CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
                                                      CodeGenerator* codegen,
                                                      CompilerDriver* compiler_driver,
@@ -618,6 +614,7 @@
   soa.Self()->TransitionFromSuspendedToRunnable();
   return compiled_method;
 }
+#pragma GCC diagnostic pop
 
 CompiledMethod* OptimizingCompiler::CompileBaseline(
     CodeGenerator* codegen,
@@ -829,8 +826,12 @@
   return compiled_method;
 }
 
-static bool HasOnlyUnresolvedFailures(const VerifiedMethod* verified_method) {
-  uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS;
+static bool CanHandleVerificationFailure(const VerifiedMethod* verified_method) {
+  // For access errors the compiler will use the unresolved helpers (e.g. HInvokeUnresolved).
+  uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS
+      | verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS
+      | verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD
+      | verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD;
   return (verified_method->GetEncounteredVerificationFailures() & (~unresolved_mask)) == 0;
 }
 
@@ -847,7 +848,7 @@
   const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
   DCHECK(!verified_method->HasRuntimeThrow());
   if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)
-      || HasOnlyUnresolvedFailures(verified_method)) {
+      || CanHandleVerificationFailure(verified_method)) {
      method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
                          method_idx, jclass_loader, dex_file, dex_cache);
   } else {
@@ -858,15 +859,6 @@
     }
   }
 
-  if (method != nullptr) {
-    return method;
-  }
-  method = delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
-                              jclass_loader, dex_file, dex_cache);
-
-  if (method != nullptr) {
-    MaybeRecordStat(MethodCompilationStat::kCompiledQuick);
-  }
   return method;
 }
 
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index c7701b7..6375cf1 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -29,11 +29,12 @@
   kAttemptCompilation = 0,
   kCompiledBaseline,
   kCompiledOptimized,
-  kCompiledQuick,
   kInlinedInvoke,
   kInstructionSimplifications,
   kInstructionSimplificationsArch,
   kUnresolvedMethod,
+  kUnresolvedField,
+  kUnresolvedFieldNotAFastAccess,
   kNotCompiledBranchOutsideMethodCode,
   kNotCompiledCannotBuildSSA,
   kNotCompiledCantAccesType,
@@ -45,7 +46,6 @@
   kNotCompiledPathological,
   kNotCompiledSpaceFilter,
   kNotCompiledUnhandledInstruction,
-  kNotCompiledUnresolvedField,
   kNotCompiledUnsupportedIsa,
   kNotCompiledVerifyAtRuntime,
   kNotOptimizedDisabled,
@@ -73,14 +73,11 @@
           compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation];
       size_t optimized_percent =
           compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation];
-      size_t quick_percent =
-          compile_stats_[kCompiledQuick] * 100 / compile_stats_[kAttemptCompilation];
       std::ostringstream oss;
       oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: ";
 
       oss << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, ";
       oss << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized, ";
-      oss << quick_percent << "% (" << compile_stats_[kCompiledQuick] << ") quick.";
 
       LOG(INFO) << oss.str();
 
@@ -99,11 +96,12 @@
       case kAttemptCompilation : return "kAttemptCompilation";
       case kCompiledBaseline : return "kCompiledBaseline";
       case kCompiledOptimized : return "kCompiledOptimized";
-      case kCompiledQuick : return "kCompiledQuick";
       case kInlinedInvoke : return "kInlinedInvoke";
       case kInstructionSimplifications: return "kInstructionSimplifications";
       case kInstructionSimplificationsArch: return "kInstructionSimplificationsArch";
       case kUnresolvedMethod : return "kUnresolvedMethod";
+      case kUnresolvedField : return "kUnresolvedField";
+      case kUnresolvedFieldNotAFastAccess : return "kUnresolvedFieldNotAFastAccess";
       case kNotCompiledBranchOutsideMethodCode: return "kNotCompiledBranchOutsideMethodCode";
       case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA";
       case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType";
@@ -115,7 +113,6 @@
       case kNotCompiledPathological : return "kNotCompiledPathological";
       case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter";
       case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
-      case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField";
       case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
       case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime";
       case kNotOptimizedDisabled : return "kNotOptimizedDisabled";
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index fe837e4..f7a7e42 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -52,6 +52,8 @@
   void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
   void VisitInstanceFieldGet(HInstanceFieldGet* instr) OVERRIDE;
   void VisitStaticFieldGet(HStaticFieldGet* instr) OVERRIDE;
+  void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) OVERRIDE;
+  void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instr) OVERRIDE;
   void VisitInvoke(HInvoke* instr) OVERRIDE;
   void VisitArrayGet(HArrayGet* instr) OVERRIDE;
   void VisitCheckCast(HCheckCast* instr) OVERRIDE;
@@ -119,8 +121,9 @@
           if (instr->IsBoundType()) {
             DCHECK(instr->AsBoundType()->GetUpperBound().IsValid());
           } else if (instr->IsLoadClass()) {
-            DCHECK(instr->AsLoadClass()->GetReferenceTypeInfo().IsExact());
-            DCHECK(instr->AsLoadClass()->GetLoadedClassRTI().IsValid());
+            HLoadClass* cls = instr->AsLoadClass();
+            DCHECK(cls->GetReferenceTypeInfo().IsExact());
+            DCHECK(!cls->GetLoadedClassRTI().IsValid() || cls->GetLoadedClassRTI().IsExact());
           } else if (instr->IsNullCheck()) {
             DCHECK(instr->GetReferenceTypeInfo().IsEqual(instr->InputAt(0)->GetReferenceTypeInfo()))
                 << "NullCheck " << instr->GetReferenceTypeInfo()
@@ -166,6 +169,7 @@
       SHARED_REQUIRES(Locks::mutator_lock_) {
   ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
   ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  DCHECK(class_rti.IsValid());
   HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null);
   // Narrow the type as much as possible.
   if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
@@ -314,6 +318,15 @@
     return;
   }
 
+  HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    if (!class_rti.IsValid()) {
+      // He have loaded an unresolved class. Don't bother bounding the type.
+      return;
+    }
+  }
   // We only need to bound the type if we have uses in the relevant block.
   // So start with null and create the HBoundType lazily, only if it's needed.
   HBoundType* bound_type = nullptr;
@@ -334,8 +347,6 @@
     if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
       if (bound_type == nullptr) {
         ScopedObjectAccess soa(Thread::Current());
-        HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
-        ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
         HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
         if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
           bound_type = CreateBoundType(
@@ -450,6 +461,22 @@
   UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
 }
 
+void RTPVisitor::VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) {
+  // TODO: Use descriptor to get the actual type.
+  if (instr->GetFieldType() == Primitive::kPrimNot) {
+    instr->SetReferenceTypeInfo(
+      ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
+  }
+}
+
+void RTPVisitor::VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instr) {
+  // TODO: Use descriptor to get the actual type.
+  if (instr->GetFieldType() == Primitive::kPrimNot) {
+    instr->SetReferenceTypeInfo(
+      ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
+  }
+}
+
 void RTPVisitor::VisitLoadClass(HLoadClass* instr) {
   ScopedObjectAccess soa(Thread::Current());
   mirror::DexCache* dex_cache =
@@ -457,10 +484,10 @@
   // Get type from dex cache assuming it was populated by the verifier.
   mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
   // TODO: investigating why we are still getting unresolved classes: b/22821472.
-  ReferenceTypeInfo::TypeHandle handle = (resolved_class != nullptr)
-    ? handles_->NewHandle(resolved_class)
-    : object_class_handle_;
-  instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
+  if (resolved_class != nullptr) {
+    instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
+        handles_->NewHandle(resolved_class), /* is_exact */ true));
+  }
   instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true));
 }
 
@@ -499,6 +526,15 @@
 }
 
 void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
+  HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    if (!class_rti.IsValid()) {
+      // He have loaded an unresolved class. Don't bother bounding the type.
+      return;
+    }
+  }
   HInstruction* obj = check_cast->InputAt(0);
   HBoundType* bound_type = nullptr;
   for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
@@ -506,8 +542,6 @@
     if (check_cast->StrictlyDominates(user)) {
       if (bound_type == nullptr) {
         ScopedObjectAccess soa(Thread::Current());
-        HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
-        ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
         if (ShouldCreateBoundType(check_cast->GetNext(), obj, class_rti, check_cast, nullptr)) {
           bound_type = CreateBoundType(
               GetGraph()->GetArena(),
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 2bb5a8b..21b36cb 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -488,7 +488,8 @@
                                                          false,
                                                          kUnknownFieldIndex,
                                                          graph->GetDexFile(),
-                                                         dex_cache);
+                                                         dex_cache,
+                                                         0);
   block->AddInstruction(test);
   block->AddInstruction(new (allocator) HIf(test));
   HBasicBlock* then = new (allocator) HBasicBlock(graph);
@@ -513,14 +514,16 @@
                                               false,
                                               kUnknownFieldIndex,
                                               graph->GetDexFile(),
-                                              dex_cache);
+                                              dex_cache,
+                                              0);
 *input2 = new (allocator) HInstanceFieldGet(parameter,
                                             Primitive::kPrimInt,
                                             MemberOffset(42),
                                             false,
                                             kUnknownFieldIndex,
                                             graph->GetDexFile(),
-                                            dex_cache);
+                                            dex_cache,
+                                            0);
   then->AddInstruction(*input1);
   else_->AddInstruction(*input2);
   join->AddInstruction(new (allocator) HExit());
@@ -634,7 +637,8 @@
                                              false,
                                              kUnknownFieldIndex,
                                              graph->GetDexFile(),
-                                             dex_cache);
+                                             dex_cache,
+                                             0);
   block->AddInstruction(*field);
   *ret = new (allocator) HReturn(*field);
   block->AddInstruction(*ret);
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index f27cecc..a095809 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -24,6 +24,7 @@
                                         uint32_t num_dex_registers,
                                         uint8_t inlining_depth) {
   DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry";
+  DCHECK_NE(dex_pc, static_cast<uint32_t>(-1)) << "invalid dex_pc";
   current_entry_.dex_pc = dex_pc;
   current_entry_.native_pc_offset = native_pc_offset;
   current_entry_.register_mask = register_mask;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 2a0912e..4380596 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -287,7 +287,7 @@
       case 1:
         return Base::REG2_TOKEN;
       case 2:
-        return REG3_TOKEN;
+        return Base::REG3_TOKEN;
       case 3:
         return REG4_TOKEN;
       default:
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 017402d..bd994f4 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -92,6 +92,17 @@
         fmt);
   }
 
+  std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), std::string fmt) {
+    return RepeatTemplatedRegisters<Reg, Reg, Reg>(f,
+        GetRegisters(),
+        GetRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        fmt);
+  }
+
   std::string Repeatrb(void (Ass::*f)(Reg, Reg), std::string fmt) {
     return RepeatTemplatedRegisters<Reg, Reg>(f,
         GetRegisters(),
@@ -118,6 +129,66 @@
     return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
   }
 
+  template <typename Reg1Type, typename Reg2Type, typename ImmType,
+            RegisterView Reg1View, RegisterView Reg2View>
+  std::string RepeatRegRegImmBits(void (Ass::*f)(Reg1Type, Reg2Type, ImmType),
+                                  int imm_bits,
+                                  std::string fmt) {
+    const std::vector<Reg1Type*> reg1_registers = GetRegisters();
+    const std::vector<Reg2Type*> reg2_registers = GetRegisters();
+    std::string str;
+    std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0);
+
+    for (auto reg1 : reg1_registers) {
+      for (auto reg2 : reg2_registers) {
+        for (int64_t imm : imms) {
+          ImmType new_imm = CreateImmediate(imm);
+          (assembler_.get()->*f)(*reg1, *reg2, new_imm);
+          std::string base = fmt;
+
+          std::string reg1_string = GetRegName<Reg1View>(*reg1);
+          size_t reg1_index;
+          while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+            base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+          }
+
+          std::string reg2_string = GetRegName<Reg2View>(*reg2);
+          size_t reg2_index;
+          while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+            base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+          }
+
+          size_t imm_index = base.find(IMM_TOKEN);
+          if (imm_index != std::string::npos) {
+            std::ostringstream sreg;
+            sreg << imm;
+            std::string imm_string = sreg.str();
+            base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
+          }
+
+          if (str.size() > 0) {
+            str += "\n";
+          }
+          str += base;
+        }
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
+  template <typename Reg1Type, typename Reg2Type, typename ImmType>
+  std::string RepeatRRIb(void (Ass::*f)(Reg1Type, Reg2Type, ImmType),
+                         int imm_bits,
+                         std::string fmt) {
+    return RepeatRegRegImmBits<Reg1Type,
+                               Reg2Type,
+                               ImmType,
+                               RegisterView::kUsePrimaryName,
+                               RegisterView::kUsePrimaryName>(f, imm_bits, fmt);
+  }
+
   std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) {
     return RepeatTemplatedRegisters<FPReg, FPReg>(f,
                                                   GetFPRegisters(),
@@ -127,14 +198,27 @@
                                                   fmt);
   }
 
-  std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&), size_t imm_bytes, std::string fmt) {
+  std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), std::string fmt) {
+    return RepeatTemplatedRegisters<FPReg, FPReg, FPReg>(f,
+                                                         GetFPRegisters(),
+                                                         GetFPRegisters(),
+                                                         GetFPRegisters(),
+                                                         &AssemblerTest::GetFPRegName,
+                                                         &AssemblerTest::GetFPRegName,
+                                                         &AssemblerTest::GetFPRegName,
+                                                         fmt);
+  }
+
+  std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&),
+                        size_t imm_bytes,
+                        std::string fmt) {
     return RepeatTemplatedRegistersImm<FPReg, FPReg>(f,
-                                                  GetFPRegisters(),
-                                                  GetFPRegisters(),
-                                                  &AssemblerTest::GetFPRegName,
-                                                  &AssemblerTest::GetFPRegName,
-                                                  imm_bytes,
-                                                  fmt);
+                                                     GetFPRegisters(),
+                                                     GetFPRegisters(),
+                                                     &AssemblerTest::GetFPRegName,
+                                                     &AssemblerTest::GetFPRegName,
+                                                     imm_bytes,
+                                                     fmt);
   }
 
   std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) {
@@ -339,6 +423,63 @@
     return res;
   }
 
+  const int kMaxBitsExhaustiveTest = 8;
+
+  // Create a couple of immediate values up to the number of bits given.
+  virtual std::vector<int64_t> CreateImmediateValuesBits(const int imm_bits, bool as_uint = false) {
+    CHECK_GT(imm_bits, 0);
+    CHECK_LE(imm_bits, 64);
+    std::vector<int64_t> res;
+
+    if (imm_bits <= kMaxBitsExhaustiveTest) {
+      if (as_uint) {
+        for (uint64_t i = MinInt<uint64_t>(imm_bits); i <= MaxInt<uint64_t>(imm_bits); i++) {
+          res.push_back(static_cast<int64_t>(i));
+        }
+      } else {
+        for (int64_t i = MinInt<int64_t>(imm_bits); i <= MaxInt<int64_t>(imm_bits); i++) {
+          res.push_back(i);
+        }
+      }
+    } else {
+      if (as_uint) {
+        for (uint64_t i = MinInt<uint64_t>(kMaxBitsExhaustiveTest);
+             i <= MaxInt<uint64_t>(kMaxBitsExhaustiveTest);
+             i++) {
+          res.push_back(static_cast<int64_t>(i));
+        }
+        for (int i = 0; i <= imm_bits; i++) {
+          uint64_t j = (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1) +
+                       ((MaxInt<uint64_t>(imm_bits) -
+                        (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1))
+                        * i / imm_bits);
+          res.push_back(static_cast<int64_t>(j));
+        }
+      } else {
+        for (int i = 0; i <= imm_bits; i++) {
+          int64_t j = MinInt<int64_t>(imm_bits) +
+                      ((((MinInt<int64_t>(kMaxBitsExhaustiveTest) - 1) -
+                         MinInt<int64_t>(imm_bits))
+                        * i) / imm_bits);
+          res.push_back(static_cast<int64_t>(j));
+        }
+        for (int64_t i = MinInt<int64_t>(kMaxBitsExhaustiveTest);
+             i <= MaxInt<int64_t>(kMaxBitsExhaustiveTest);
+             i++) {
+          res.push_back(static_cast<int64_t>(i));
+        }
+        for (int i = 0; i <= imm_bits; i++) {
+          int64_t j = (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1) +
+                      ((MaxInt<int64_t>(imm_bits) - (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1))
+                       * i / imm_bits);
+          res.push_back(static_cast<int64_t>(j));
+        }
+      }
+    }
+
+    return res;
+  }
+
   // Create an immediate from the specific value.
   virtual Imm CreateImmediate(int64_t imm_value) = 0;
 
@@ -406,6 +547,52 @@
     return str;
   }
 
+  template <typename Reg1, typename Reg2, typename Reg3>
+  std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3),
+                                       const std::vector<Reg1*> reg1_registers,
+                                       const std::vector<Reg2*> reg2_registers,
+                                       const std::vector<Reg3*> reg3_registers,
+                                       std::string (AssemblerTest::*GetName1)(const Reg1&),
+                                       std::string (AssemblerTest::*GetName2)(const Reg2&),
+                                       std::string (AssemblerTest::*GetName3)(const Reg3&),
+                                       std::string fmt) {
+    std::string str;
+    for (auto reg1 : reg1_registers) {
+      for (auto reg2 : reg2_registers) {
+        for (auto reg3 : reg3_registers) {
+          (assembler_.get()->*f)(*reg1, *reg2, *reg3);
+          std::string base = fmt;
+
+          std::string reg1_string = (this->*GetName1)(*reg1);
+          size_t reg1_index;
+          while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+            base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+          }
+
+          std::string reg2_string = (this->*GetName2)(*reg2);
+          size_t reg2_index;
+          while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+            base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+          }
+
+          std::string reg3_string = (this->*GetName3)(*reg3);
+          size_t reg3_index;
+          while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
+            base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
+          }
+
+          if (str.size() > 0) {
+            str += "\n";
+          }
+          str += base;
+        }
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
   template <typename Reg1, typename Reg2>
   std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&),
                                           const std::vector<Reg1*> reg1_registers,
@@ -500,6 +687,7 @@
   static constexpr const char* REG_TOKEN = "{reg}";
   static constexpr const char* REG1_TOKEN = "{reg1}";
   static constexpr const char* REG2_TOKEN = "{reg2}";
+  static constexpr const char* REG3_TOKEN = "{reg3}";
   static constexpr const char* IMM_TOKEN = "{imm}";
 
  private:
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index c8b3fe5..43c9d94 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -67,12 +67,20 @@
 
   // This is intended to be run as a test.
   bool CheckTools() {
-    if (!FileExists(FindTool(assembler_cmd_name_))) {
+    std::string asm_tool = FindTool(assembler_cmd_name_);
+    if (!FileExists(asm_tool)) {
+      LOG(ERROR) << "Could not find assembler from " << assembler_cmd_name_;
+      LOG(ERROR) << "FindTool returned " << asm_tool;
+      FindToolDump(assembler_cmd_name_);
       return false;
     }
     LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand();
 
-    if (!FileExists(FindTool(objdump_cmd_name_))) {
+    std::string objdump_tool = FindTool(objdump_cmd_name_);
+    if (!FileExists(objdump_tool)) {
+      LOG(ERROR) << "Could not find objdump from " << objdump_cmd_name_;
+      LOG(ERROR) << "FindTool returned " << objdump_tool;
+      FindToolDump(objdump_cmd_name_);
       return false;
     }
     LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand();
@@ -80,7 +88,11 @@
     // Disassembly is optional.
     std::string disassembler = GetDisassembleCommand();
     if (disassembler.length() != 0) {
-      if (!FileExists(FindTool(disassembler_cmd_name_))) {
+      std::string disassembler_tool = FindTool(disassembler_cmd_name_);
+      if (!FileExists(disassembler_tool)) {
+        LOG(ERROR) << "Could not find disassembler from " << disassembler_cmd_name_;
+        LOG(ERROR) << "FindTool returned " << disassembler_tool;
+        FindToolDump(disassembler_cmd_name_);
         return false;
       }
       LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand();
@@ -493,7 +505,7 @@
     std::string error_msg;
     if (!Exec(args, &error_msg)) {
       EXPECT_TRUE(false) << error_msg;
-      return "";
+      UNREACHABLE();
     }
 
     std::ifstream in(tmp_file.c_str());
@@ -508,6 +520,39 @@
     return line;
   }
 
+  // For debug purposes.
+  void FindToolDump(std::string tool_name) {
+    // Find the current tool. Wild-card pattern is "arch-string*tool-name".
+    std::string gcc_path = GetRootPath() + GetGCCRootPath();
+    std::vector<std::string> args;
+    args.push_back("find");
+    args.push_back(gcc_path);
+    args.push_back("-name");
+    args.push_back(architecture_string_ + "*" + tool_name);
+    args.push_back("|");
+    args.push_back("sort");
+    std::string tmp_file = GetTmpnam();
+    args.push_back(">");
+    args.push_back(tmp_file);
+    std::string sh_args = Join(args, ' ');
+
+    args.clear();
+    args.push_back("/bin/sh");
+    args.push_back("-c");
+    args.push_back(sh_args);
+
+    std::string error_msg;
+    if (!Exec(args, &error_msg)) {
+      EXPECT_TRUE(false) << error_msg;
+      UNREACHABLE();
+    }
+
+    std::ifstream in(tmp_file.c_str());
+    if (in) {
+      LOG(ERROR) << in.rdbuf();
+    }
+  }
+
   // Use a consistent tmpnam, so store it.
   std::string GetTmpnam() {
     if (tmpnam_.length() == 0) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index c170313..d083eb4 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -46,6 +46,20 @@
   kStoreDoubleword
 };
 
+// Used to test the values returned by ClassS/ClassD.
+enum FPClassMaskType {
+  kSignalingNaN      = 0x001,
+  kQuietNaN          = 0x002,
+  kNegativeInfinity  = 0x004,
+  kNegativeNormal    = 0x008,
+  kNegativeSubnormal = 0x010,
+  kNegativeZero      = 0x020,
+  kPositiveInfinity  = 0x040,
+  kPositiveNormal    = 0x080,
+  kPositiveSubnormal = 0x100,
+  kPositiveZero      = 0x200,
+};
+
 class Mips64Assembler FINAL : public Assembler {
  public:
   Mips64Assembler() {}
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
new file mode 100644
index 0000000..2071aca
--- /dev/null
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "assembler_mips64.h"
+
+#include <inttypes.h>
+#include <map>
+#include <random>
+
+#include "base/bit_utils.h"
+#include "base/stl_util.h"
+#include "utils/assembler_test.h"
+
+namespace art {
+
+struct MIPS64CpuRegisterCompare {
+  bool operator()(const mips64::GpuRegister& a, const mips64::GpuRegister& b) const {
+    return a < b;
+  }
+};
+
+class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler,
+                                                 mips64::GpuRegister,
+                                                 mips64::FpuRegister,
+                                                 uint32_t> {
+ public:
+  typedef AssemblerTest<mips64::Mips64Assembler,
+                        mips64::GpuRegister,
+                        mips64::FpuRegister,
+                        uint32_t> Base;
+
+ protected:
+  // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
+  std::string GetArchitectureString() OVERRIDE {
+    return "mips64";
+  }
+
+  std::string GetAssemblerParameters() OVERRIDE {
+    return " --no-warn -march=mips64r6";
+  }
+
+  std::string GetDisassembleParameters() OVERRIDE {
+    return " -D -bbinary -mmips:isa64r6";
+  }
+
+  void SetUpHelpers() OVERRIDE {
+    if (registers_.size() == 0) {
+      registers_.push_back(new mips64::GpuRegister(mips64::ZERO));
+      registers_.push_back(new mips64::GpuRegister(mips64::AT));
+      registers_.push_back(new mips64::GpuRegister(mips64::V0));
+      registers_.push_back(new mips64::GpuRegister(mips64::V1));
+      registers_.push_back(new mips64::GpuRegister(mips64::A0));
+      registers_.push_back(new mips64::GpuRegister(mips64::A1));
+      registers_.push_back(new mips64::GpuRegister(mips64::A2));
+      registers_.push_back(new mips64::GpuRegister(mips64::A3));
+      registers_.push_back(new mips64::GpuRegister(mips64::A4));
+      registers_.push_back(new mips64::GpuRegister(mips64::A5));
+      registers_.push_back(new mips64::GpuRegister(mips64::A6));
+      registers_.push_back(new mips64::GpuRegister(mips64::A7));
+      registers_.push_back(new mips64::GpuRegister(mips64::T0));
+      registers_.push_back(new mips64::GpuRegister(mips64::T1));
+      registers_.push_back(new mips64::GpuRegister(mips64::T2));
+      registers_.push_back(new mips64::GpuRegister(mips64::T3));
+      registers_.push_back(new mips64::GpuRegister(mips64::S0));
+      registers_.push_back(new mips64::GpuRegister(mips64::S1));
+      registers_.push_back(new mips64::GpuRegister(mips64::S2));
+      registers_.push_back(new mips64::GpuRegister(mips64::S3));
+      registers_.push_back(new mips64::GpuRegister(mips64::S4));
+      registers_.push_back(new mips64::GpuRegister(mips64::S5));
+      registers_.push_back(new mips64::GpuRegister(mips64::S6));
+      registers_.push_back(new mips64::GpuRegister(mips64::S7));
+      registers_.push_back(new mips64::GpuRegister(mips64::T8));
+      registers_.push_back(new mips64::GpuRegister(mips64::T9));
+      registers_.push_back(new mips64::GpuRegister(mips64::K0));
+      registers_.push_back(new mips64::GpuRegister(mips64::K1));
+      registers_.push_back(new mips64::GpuRegister(mips64::GP));
+      registers_.push_back(new mips64::GpuRegister(mips64::SP));
+      registers_.push_back(new mips64::GpuRegister(mips64::S8));
+      registers_.push_back(new mips64::GpuRegister(mips64::RA));
+
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::ZERO), "zero");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::AT), "at");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::V0), "v0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::V1), "v1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A0), "a0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A1), "a1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A2), "a2");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A3), "a3");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A4), "a4");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A5), "a5");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A6), "a6");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A7), "a7");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T0), "t0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T1), "t1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T2), "t2");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T3), "t3");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S0), "s0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S1), "s1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S2), "s2");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S3), "s3");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S4), "s4");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S5), "s5");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S6), "s6");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S7), "s7");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T8), "t8");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T9), "t9");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::K0), "k0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::K1), "k1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::GP), "gp");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::SP), "sp");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S8), "s8");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::RA), "ra");
+
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F0));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F1));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F2));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F3));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F4));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F5));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F6));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F7));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F8));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F9));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F10));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F11));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F12));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F13));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F14));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F15));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F16));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F17));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F18));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F19));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F20));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F21));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F22));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F23));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F24));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F25));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F26));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F27));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F28));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F29));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F30));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F31));
+    }
+  }
+
+  void TearDown() OVERRIDE {
+    AssemblerTest::TearDown();
+    STLDeleteElements(&registers_);
+    STLDeleteElements(&fp_registers_);
+  }
+
+  std::vector<mips64::GpuRegister*> GetRegisters() OVERRIDE {
+    return registers_;
+  }
+
+  std::vector<mips64::FpuRegister*> GetFPRegisters() OVERRIDE {
+    return fp_registers_;
+  }
+
+  uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
+    return imm_value;
+  }
+
+  std::string GetSecondaryRegisterName(const mips64::GpuRegister& reg) OVERRIDE {
+    CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+    return secondary_register_names_[reg];
+  }
+
+ private:
+  std::vector<mips64::GpuRegister*> registers_;
+  std::map<mips64::GpuRegister, std::string, MIPS64CpuRegisterCompare> secondary_register_names_;
+
+  std::vector<mips64::FpuRegister*> fp_registers_;
+};
+
+
+TEST_F(AssemblerMIPS64Test, Toolchain) {
+  EXPECT_TRUE(CheckTools());
+}
+
+
+///////////////////
+// FP Operations //
+///////////////////
+
+TEST_F(AssemblerMIPS64Test, SqrtS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtS, "sqrt.s ${reg1}, ${reg2}"), "sqrt.s");
+}
+
+TEST_F(AssemblerMIPS64Test, SqrtD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtD, "sqrt.d ${reg1}, ${reg2}"), "sqrt.d");
+}
+
+TEST_F(AssemblerMIPS64Test, AbsS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::AbsS, "abs.s ${reg1}, ${reg2}"), "abs.s");
+}
+
+TEST_F(AssemblerMIPS64Test, AbsD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::AbsD, "abs.d ${reg1}, ${reg2}"), "abs.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundLS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundLS, "round.l.s ${reg1}, ${reg2}"), "round.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundLD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundLD, "round.l.d ${reg1}, ${reg2}"), "round.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundWS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundWS, "round.w.s ${reg1}, ${reg2}"), "round.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundWD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundWD, "round.w.d ${reg1}, ${reg2}"), "round.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilLS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilLS, "ceil.l.s ${reg1}, ${reg2}"), "ceil.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilLD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilLD, "ceil.l.d ${reg1}, ${reg2}"), "ceil.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilWS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilWS, "ceil.w.s ${reg1}, ${reg2}"), "ceil.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilWD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilWD, "ceil.w.d ${reg1}, ${reg2}"), "ceil.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorLS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorLS, "floor.l.s ${reg1}, ${reg2}"), "floor.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorLD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorLD, "floor.l.d ${reg1}, ${reg2}"), "floor.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorWS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorWS, "floor.w.s ${reg1}, ${reg2}"), "floor.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorWD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorWD, "floor.w.d ${reg1}, ${reg2}"), "floor.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SelS) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::SelS, "sel.s ${reg1}, ${reg2}, ${reg3}"), "sel.s");
+}
+
+TEST_F(AssemblerMIPS64Test, SelD) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::SelD, "sel.d ${reg1}, ${reg2}, ${reg3}"), "sel.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RintS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RintS, "rint.s ${reg1}, ${reg2}"), "rint.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RintD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RintD, "rint.d ${reg1}, ${reg2}"), "rint.d");
+}
+
+TEST_F(AssemblerMIPS64Test, ClassS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::ClassS, "class.s ${reg1}, ${reg2}"), "class.s");
+}
+
+TEST_F(AssemblerMIPS64Test, ClassD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::ClassD, "class.d ${reg1}, ${reg2}"), "class.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MinS) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MinS, "min.s ${reg1}, ${reg2}, ${reg3}"), "min.s");
+}
+
+TEST_F(AssemblerMIPS64Test, MinD) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MinD, "min.d ${reg1}, ${reg2}, ${reg3}"), "min.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MaxS) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MaxS, "max.s ${reg1}, ${reg2}, ${reg3}"), "max.s");
+}
+
+TEST_F(AssemblerMIPS64Test, MaxD) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MaxD, "max.d ${reg1}, ${reg2}, ${reg3}"), "max.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CvtDL) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtdl, "cvt.d.l ${reg1}, ${reg2}"), "cvt.d.l");
+}
+
+//////////
+// MISC //
+//////////
+
+TEST_F(AssemblerMIPS64Test, Bitswap) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
+}
+
+TEST_F(AssemblerMIPS64Test, Dbitswap) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dbitswap, "dbitswap ${reg1}, ${reg2}"), "dbitswap");
+}
+
+TEST_F(AssemblerMIPS64Test, Dsbh) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dsbh, "dsbh ${reg1}, ${reg2}"), "dsbh");
+}
+
+TEST_F(AssemblerMIPS64Test, Dshd) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dshd, "dshd ${reg1}, ${reg2}"), "dshd");
+}
+
+TEST_F(AssemblerMIPS64Test, Wsbh) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh");
+}
+
+TEST_F(AssemblerMIPS64Test, Sc) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sc, -9, "sc ${reg1}, {imm}(${reg2})"), "sc");
+}
+
+TEST_F(AssemblerMIPS64Test, Scd) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Scd, -9, "scd ${reg1}, {imm}(${reg2})"), "scd");
+}
+
+TEST_F(AssemblerMIPS64Test, Ll) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Ll, -9, "ll ${reg1}, {imm}(${reg2})"), "ll");
+}
+
+TEST_F(AssemblerMIPS64Test, Lld) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lld, -9, "lld ${reg1}, {imm}(${reg2})"), "lld");
+}
+
+TEST_F(AssemblerMIPS64Test, Rotr) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Rotr, 5, "rotr ${reg1}, ${reg2}, {imm}"), "rotr");
+}
+
+TEST_F(AssemblerMIPS64Test, Seleqz) {
+  DriverStr(RepeatRRR(&mips64::Mips64Assembler::Seleqz, "seleqz ${reg1}, ${reg2}, ${reg3}"),
+            "seleqz");
+}
+
+TEST_F(AssemblerMIPS64Test, Selnez) {
+  DriverStr(RepeatRRR(&mips64::Mips64Assembler::Selnez, "selnez ${reg1}, ${reg2}, ${reg3}"),
+            "selnez");
+}
+
+TEST_F(AssemblerMIPS64Test, Clz) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Clz, "clz ${reg1}, ${reg2}"), "clz");
+}
+
+TEST_F(AssemblerMIPS64Test, Clo) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Clo, "clo ${reg1}, ${reg2}"), "clo");
+}
+
+TEST_F(AssemblerMIPS64Test, Dclz) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dclz, "dclz ${reg1}, ${reg2}"), "dclz");
+}
+
+TEST_F(AssemblerMIPS64Test, Dclo) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dclo, "dclo ${reg1}, ${reg2}"), "dclo");
+}
+
+}  // namespace art
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index cc32da1..680e2d7 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -235,11 +235,7 @@
   UsageError("  --compiler-backend=(Quick|Optimizing): select compiler backend");
   UsageError("      set.");
   UsageError("      Example: --compiler-backend=Optimizing");
-  if (kUseOptimizingCompiler) {
-    UsageError("      Default: Optimizing");
-  } else {
-    UsageError("      Default: Quick");
-  }
+  UsageError("      Default: Optimizing");
   UsageError("");
   UsageError("  --compiler-filter="
                 "(verify-none"
@@ -503,7 +499,7 @@
 class Dex2Oat FINAL {
  public:
   explicit Dex2Oat(TimingLogger* timings) :
-      compiler_kind_(kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick),
+      compiler_kind_(Compiler::kOptimizing),
       instruction_set_(kRuntimeISA),
       // Take the default set of instruction features from the build.
       verification_results_(nullptr),
@@ -752,10 +748,9 @@
 
   void ProcessOptions(ParserOptions* parser_options) {
     image_ = (!image_filename_.empty());
-    if (!parser_options->requested_specific_compiler && !kUseOptimizingCompiler) {
-      // If no specific compiler is requested, the current behavior is
-      // to compile the boot image with Quick, and the rest with Optimizing.
-      compiler_kind_ = image_ ? Compiler::kQuick : Compiler::kOptimizing;
+    if (image_) {
+      // We need the boot image to always be debuggable.
+      parser_options->debuggable = true;
     }
 
     if (oat_filename_.empty() && oat_fd_ == -1) {
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index 1ac7930..82bc8b9 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -109,11 +109,12 @@
   std::string boot_image_location_;
 };
 
-#if defined (ART_TARGET)
+#if defined (ART_TARGET) && !defined(__mips__)
 TEST_F(ImgDiagTest, ImageDiffPidSelf) {
 #else
 // Can't run this test on the host, it will fail when trying to open /proc/kpagestats
 // because it's root read-only.
+// Also test fails on mips. b/24596015.
 TEST_F(ImgDiagTest, DISABLED_ImageDiffPidSelf) {
 #endif
   // Invoke 'img_diag' against the current process.
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 059c4cd..8d81f2a 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -67,6 +67,7 @@
   gc/heap.cc \
   gc/reference_processor.cc \
   gc/reference_queue.cc \
+  gc/scoped_gc_critical_section.cc \
   gc/space/bump_pointer_space.cc \
   gc/space/dlmalloc_space.cc \
   gc/space/image_space.cc \
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index dc1cf8a..d09631b 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -839,13 +839,12 @@
 TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
     /*
      * Called by managed code to resolve a static field and store a 64-bit primitive value.
-     * On entry r0 holds field index, r1:r2 hold new_val
+     * On entry r0 holds field index, r2:r3 hold new_val
      */
     .extern artSet64StaticFromCode
 ENTRY art_quick_set64_static
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12   @ save callee saves in case of GC
-    mov    r3, r2                        @ pass one half of wide argument
-    mov    r2, r1                        @ pass other half of wide argument
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r12   @ save callee saves in case of GC
+                                         @ r2:r3 contain the wide argument
     ldr    r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
     str    r9, [sp, #-16]!               @ expand the frame and pass Thread::Current
     .cfi_adjust_cfa_offset 16
@@ -870,6 +869,7 @@
     .extern artSet64InstanceFromCode
 ENTRY art_quick_set64_instance
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr  @ save callee saves in case of GC
+                                         @ r2:r3 contain the wide argument
     ldr    r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
     str    r9, [sp, #-12]!               @ expand the frame and pass Thread::Current
     .cfi_adjust_cfa_offset 12
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 1f2ce02..395cee8 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -30,7 +30,7 @@
 
   // Look for variants that need a fix for a53 erratum 835769.
   static const char* arm64_variants_with_a53_835769_bug[] = {
-      "default", "generic"  // Pessimistically assume all generic ARM64s are A53s.
+      "default", "generic", "cortex-a53"  // Pessimistically assume all generic ARM64s are A53s.
   };
   bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug,
                                                  arraysize(arm64_variants_with_a53_835769_bug),
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 6812178..be5a15e 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1421,9 +1421,8 @@
     .extern artSet64StaticFromCode
 ENTRY art_quick_set64_static
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  // save callee saves in case of GC
-    mov    x3, x1                     // Store value
     ldr    x1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer
-    mov    x2, x3                     // Put value param
+                                      // x2 contains the parameter
     mov    x3, xSELF                  // pass Thread::Current
     bl     artSet64StaticFromCode
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ce1b2f3..68156ae 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1244,7 +1244,7 @@
     .extern artSet64StaticFromCode
 ENTRY art_quick_set64_static
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME    # save callee saves in case of GC
-    move   $a2, $a1                      # pass new_val
+                                         # a2 contains the new val
     ld     $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp)  # pass referrer's Method*
     jal    artSet64StaticFromCode        # (field_idx, referrer, new_val, Thread*)
     move   $a3, rSELF                    # pass Thread::Current
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index f10799c..0d2457e 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -774,22 +774,6 @@
     return result;
   }
 
-  // Method with 32b arg0, 64b arg1
-  size_t Invoke3UWithReferrer(size_t arg0, uint64_t arg1, uintptr_t code, Thread* self,
-                              ArtMethod* referrer) {
-#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \
-    defined(__aarch64__)
-    // Just pass through.
-    return Invoke3WithReferrer(arg0, arg1, 0U, code, self, referrer);
-#else
-    // Need to split up arguments.
-    uint32_t lower = static_cast<uint32_t>(arg1 & 0xFFFFFFFF);
-    uint32_t upper = static_cast<uint32_t>((arg1 >> 32) & 0xFFFFFFFF);
-
-    return Invoke3WithReferrer(arg0, lower, upper, code, self, referrer);
-#endif
-  }
-
   static uintptr_t GetEntrypoint(Thread* self, QuickEntrypointEnum entrypoint) {
     int32_t offset;
 #ifdef __LP64__
@@ -1974,21 +1958,23 @@
 }
 
 
-// TODO: Complete these tests for 32b architectures.
+// TODO: Complete these tests for 32b architectures
 
 static void GetSet64Static(ArtField* f, Thread* self, ArtMethod* referrer,
                            StubTest* test)
     SHARED_REQUIRES(Locks::mutator_lock_) {
-#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \
-    defined(__aarch64__)
+#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) \
+    || defined(__aarch64__)
   uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };
 
   for (size_t i = 0; i < arraysize(values); ++i) {
-    test->Invoke3UWithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
-                               values[i],
-                               StubTest::GetEntrypoint(self, kQuickSet64Static),
-                               self,
-                               referrer);
+    // 64 bit FieldSet stores the set value in the second register.
+    test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
+                              0U,
+                              values[i],
+                              StubTest::GetEntrypoint(self, kQuickSet64Static),
+                              self,
+                              referrer);
 
     size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
                                            0U, 0U,
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 5394c46..4a106e4 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1254,15 +1254,18 @@
 // Call artSet64StaticFromCode with 3 word size arguments plus with the referrer in the 2nd position
 // so that new_val is aligned on even registers were we passing arguments in registers.
 DEFINE_FUNCTION art_quick_set64_static
+    // TODO: Implement SETUP_GOT_NOSAVE for got_reg = ecx to avoid moving around the registers.
+    movd %ebx, %xmm0
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  ebx, ebx  // save ref containing registers for GC
-    mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx  // get referrer
+    movd %xmm0, %ebx
+    mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ecx  // get referrer
     subl LITERAL(12), %esp        // alignment padding
     CFI_ADJUST_CFA_OFFSET(12)
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    PUSH edx                      // pass high half of new_val
-    PUSH ecx                      // pass low half of new_val
-    PUSH ebx                      // pass referrer
+    PUSH ebx                      // pass high half of new_val
+    PUSH edx                      // pass low half of new_val
+    PUSH ecx                      // pass referrer
     PUSH eax                      // pass field_idx
     call SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*)
     addl LITERAL(32), %esp        // pop arguments
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 8a0a29b..5c413d2 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1203,7 +1203,7 @@
 
 // This is singled out as the argument order is different.
 DEFINE_FUNCTION art_quick_set64_static
-    movq %rsi, %rdx                      // pass new_val
+                                         // new_val is already in %rdx
     movq 8(%rsp), %rsi                   // pass referrer
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
                                          // field_idx is in rdi
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 345428c..6f2aa46 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -23,7 +23,6 @@
 #include "mem_map.h"
 #include "mutex.h"
 #include "thread-inl.h"
-#include "base/memory_tool.h"
 
 namespace art {
 
@@ -290,8 +289,7 @@
     begin_(nullptr),
     end_(nullptr),
     ptr_(nullptr),
-    arena_head_(nullptr),
-    is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL) {
+    arena_head_(nullptr) {
 }
 
 void ArenaAllocator::UpdateBytesAllocated() {
@@ -302,14 +300,13 @@
   }
 }
 
-void* ArenaAllocator::AllocValgrind(size_t bytes, ArenaAllocKind kind) {
+void* ArenaAllocator::AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind) {
   size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
   if (UNLIKELY(ptr_ + rounded_bytes > end_)) {
     // Obtain a new block.
     ObtainNewArenaForAllocation(rounded_bytes);
-    if (UNLIKELY(ptr_ == nullptr)) {
-      return nullptr;
-    }
+    CHECK(ptr_ != nullptr);
+    MEMORY_TOOL_MAKE_UNDEFINED(ptr_, end_ - ptr_);
   }
   ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
   uint8_t* ret = ptr_;
@@ -318,6 +315,7 @@
   for (uint8_t* ptr = ret; ptr < ptr_; ++ptr) {
     CHECK_EQ(*ptr, 0U);
   }
+  MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
   MEMORY_TOOL_MAKE_NOACCESS(ret + bytes, rounded_bytes - bytes);
   return ret;
 }
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index b4f19ee..565b416 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -21,6 +21,7 @@
 #include <stddef.h>
 
 #include "base/bit_utils.h"
+#include "base/memory_tool.h"
 #include "debug_stack.h"
 #include "macros.h"
 #include "mutex.h"
@@ -149,6 +150,52 @@
 
 typedef ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations> ArenaAllocatorStats;
 
+template <bool kAvailable, bool kValgrind>
+class ArenaAllocatorMemoryToolCheckImpl {
+  // This is the generic template but since there is a partial specialization
+  // for kValgrind == false, this can be instantiated only for kValgrind == true.
+  static_assert(kValgrind, "This template can be instantiated only for Valgrind.");
+  static_assert(kAvailable, "Valgrind implies memory tool availability.");
+
+ public:
+  ArenaAllocatorMemoryToolCheckImpl() : is_running_on_valgrind_(RUNNING_ON_MEMORY_TOOL) { }
+  bool IsRunningOnMemoryTool() { return is_running_on_valgrind_; }
+
+ private:
+  const bool is_running_on_valgrind_;
+};
+
+template <bool kAvailable>
+class ArenaAllocatorMemoryToolCheckImpl<kAvailable, false> {
+ public:
+  ArenaAllocatorMemoryToolCheckImpl() { }
+  bool IsRunningOnMemoryTool() { return kAvailable; }
+};
+
+typedef ArenaAllocatorMemoryToolCheckImpl<kMemoryToolIsAvailable, kMemoryToolIsValgrind>
+    ArenaAllocatorMemoryToolCheck;
+
+class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck {
+ public:
+  using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool;
+
+  void MakeDefined(void* ptr, size_t size) {
+    if (IsRunningOnMemoryTool()) {
+      MEMORY_TOOL_MAKE_DEFINED(ptr, size);
+    }
+  }
+  void MakeUndefined(void* ptr, size_t size) {
+    if (IsRunningOnMemoryTool()) {
+      MEMORY_TOOL_MAKE_UNDEFINED(ptr, size);
+    }
+  }
+  void MakeInaccessible(void* ptr, size_t size) {
+    if (IsRunningOnMemoryTool()) {
+      MEMORY_TOOL_MAKE_NOACCESS(ptr, size);
+    }
+  }
+};
+
 class Arena {
  public:
   static constexpr size_t kDefaultSize = 128 * KB;
@@ -233,18 +280,24 @@
   DISALLOW_COPY_AND_ASSIGN(ArenaPool);
 };
 
-class ArenaAllocator : private DebugStackRefCounter, private ArenaAllocatorStats {
+class ArenaAllocator
+    : private DebugStackRefCounter, private ArenaAllocatorStats, private ArenaAllocatorMemoryTool {
  public:
   explicit ArenaAllocator(ArenaPool* pool);
   ~ArenaAllocator();
 
+  using ArenaAllocatorMemoryTool::IsRunningOnMemoryTool;
+  using ArenaAllocatorMemoryTool::MakeDefined;
+  using ArenaAllocatorMemoryTool::MakeUndefined;
+  using ArenaAllocatorMemoryTool::MakeInaccessible;
+
   // Get adapter for use in STL containers. See arena_containers.h .
   ArenaAllocatorAdapter<void> Adapter(ArenaAllocKind kind = kArenaAllocSTL);
 
   // Returns zeroed memory.
   void* Alloc(size_t bytes, ArenaAllocKind kind = kArenaAllocMisc) ALWAYS_INLINE {
-    if (UNLIKELY(is_running_on_memory_tool_)) {
-      return AllocValgrind(bytes, kind);
+    if (UNLIKELY(IsRunningOnMemoryTool())) {
+      return AllocWithMemoryTool(bytes, kind);
     }
     bytes = RoundUp(bytes, kAlignment);
     if (UNLIKELY(ptr_ + bytes > end_)) {
@@ -268,6 +321,7 @@
     auto* end = reinterpret_cast<uint8_t*>(ptr) + ptr_size;
     // If we haven't allocated anything else, we can safely extend.
     if (end == ptr_) {
+      DCHECK(!IsRunningOnMemoryTool());  // Red zone prevents end == ptr_.
       const size_t size_delta = new_size - ptr_size;
       // Check remain space.
       const size_t remain = end_ - ptr_;
@@ -288,7 +342,7 @@
     return static_cast<T*>(Alloc(length * sizeof(T), kind));
   }
 
-  void* AllocValgrind(size_t bytes, ArenaAllocKind kind);
+  void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
 
   void ObtainNewArenaForAllocation(size_t allocation_size);
 
@@ -316,7 +370,6 @@
   uint8_t* end_;
   uint8_t* ptr_;
   Arena* arena_head_;
-  bool is_running_on_memory_tool_;
 
   template <typename U>
   friend class ArenaAllocatorAdapter;
diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h
index 810c1c4..e7ea09d 100644
--- a/runtime/base/arena_containers.h
+++ b/runtime/base/arena_containers.h
@@ -161,7 +161,7 @@
     return arena_allocator_->AllocArray<T>(n, ArenaAllocatorAdapterKind::Kind());
   }
   void deallocate(pointer p, size_type n) {
-    UNUSED(p, n);
+    arena_allocator_->MakeInaccessible(p, sizeof(T) * n);
   }
 
   void construct(pointer p, const_reference val) {
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 1b0d774..9c78ee5 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -263,6 +263,33 @@
                       : static_cast<unsigned_type>(value));
 }
 
+// Generate maximum/minimum values for signed/unsigned n-bit integers
+template <typename T>
+static constexpr T MaxInt(size_t bits) {
+  return
+      DCHECK_CONSTEXPR(bits > 0, "bits cannot be zero", 0)
+      DCHECK_CONSTEXPR(bits <= BitSizeOf<T>(), "kBits must be < max.", 0)
+      bits == BitSizeOf<T>()
+          ? std::numeric_limits<T>::max()
+          : std::is_signed<T>::value
+                ? (bits == 1
+                       ? 0
+                       : static_cast<T>(MaxInt<typename std::make_unsigned<T>::type>(bits - 1)))
+                : static_cast<T>(UINT64_C(1) << bits) - static_cast<T>(1);
+}
+
+template <typename T>
+static constexpr T MinInt(size_t bits) {
+  return
+      DCHECK_CONSTEXPR(bits > 0, "bits cannot be zero", 0)
+      DCHECK_CONSTEXPR(bits <= BitSizeOf<T>(), "kBits must be < max.", 0)
+      bits == BitSizeOf<T>()
+          ? std::numeric_limits<T>::min()
+          : std::is_signed<T>::value
+                ? (bits == 1 ? -1 : static_cast<T>(-1) - MaxInt<T>(bits))
+                : static_cast<T>(0);
+}
+
 // Using the Curiously Recurring Template Pattern to implement everything shared
 // by LowToHighBitIterator and HighToLowBitIterator, i.e. everything but operator*().
 template <typename T, typename Iter>
diff --git a/runtime/base/memory_tool.h b/runtime/base/memory_tool.h
index e0bdcfe..e1a2e07 100644
--- a/runtime/base/memory_tool.h
+++ b/runtime/base/memory_tool.h
@@ -32,10 +32,12 @@
 #define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s)
 #define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s)
 #define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s)
+constexpr bool kMemoryToolIsAvailable = true;
 #else
 #define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
 #define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
 #define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+constexpr bool kMemoryToolIsAvailable = false;
 #endif
 
 #define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
@@ -54,6 +56,7 @@
 #define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s)
 #define ATTRIBUTE_NO_SANITIZE_ADDRESS
 #define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND
+constexpr bool kMemoryToolIsAvailable = true;
 constexpr bool kMemoryToolIsValgrind = true;
 constexpr bool kMemoryToolDetectsLeaks = true;
 constexpr bool kMemoryToolAddsRedzones = true;
diff --git a/runtime/base/scoped_arena_allocator.cc b/runtime/base/scoped_arena_allocator.cc
index d823edd..31f96e4 100644
--- a/runtime/base/scoped_arena_allocator.cc
+++ b/runtime/base/scoped_arena_allocator.cc
@@ -29,8 +29,7 @@
     bottom_arena_(nullptr),
     top_arena_(nullptr),
     top_ptr_(nullptr),
-    top_end_(nullptr),
-    is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL > 0) {
+    top_end_(nullptr) {
 }
 
 ArenaStack::~ArenaStack() {
@@ -91,7 +90,7 @@
   }
 }
 
-void* ArenaStack::AllocValgrind(size_t bytes, ArenaAllocKind kind) {
+void* ArenaStack::AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind) {
   size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
   uint8_t* ptr = top_ptr_;
   if (UNLIKELY(static_cast<size_t>(top_end_ - ptr) < rounded_bytes)) {
diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h
index ca514e4..4f51370 100644
--- a/runtime/base/scoped_arena_allocator.h
+++ b/runtime/base/scoped_arena_allocator.h
@@ -32,11 +32,16 @@
 class ScopedArenaAllocatorAdapter;
 
 // Holds a list of Arenas for use by ScopedArenaAllocator stack.
-class ArenaStack : private DebugStackRefCounter {
+class ArenaStack : private DebugStackRefCounter, private ArenaAllocatorMemoryTool {
  public:
   explicit ArenaStack(ArenaPool* arena_pool);
   ~ArenaStack();
 
+  using ArenaAllocatorMemoryTool::IsRunningOnMemoryTool;
+  using ArenaAllocatorMemoryTool::MakeDefined;
+  using ArenaAllocatorMemoryTool::MakeUndefined;
+  using ArenaAllocatorMemoryTool::MakeInaccessible;
+
   void Reset();
 
   size_t PeakBytesAllocated() {
@@ -64,8 +69,8 @@
 
   // Private - access via ScopedArenaAllocator or ScopedArenaAllocatorAdapter.
   void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE {
-    if (UNLIKELY(is_running_on_memory_tool_)) {
-      return AllocValgrind(bytes, kind);
+    if (UNLIKELY(IsRunningOnMemoryTool())) {
+      return AllocWithMemoryTool(bytes, kind);
     }
     size_t rounded_bytes = RoundUp(bytes, 8);
     uint8_t* ptr = top_ptr_;
@@ -80,7 +85,7 @@
   uint8_t* AllocateFromNextArena(size_t rounded_bytes);
   void UpdatePeakStatsAndRestore(const ArenaAllocatorStats& restore_stats);
   void UpdateBytesAllocated();
-  void* AllocValgrind(size_t bytes, ArenaAllocKind kind);
+  void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
 
   StatsAndPool stats_and_pool_;
   Arena* bottom_arena_;
@@ -88,8 +93,6 @@
   uint8_t* top_ptr_;
   uint8_t* top_end_;
 
-  const bool is_running_on_memory_tool_;
-
   friend class ScopedArenaAllocator;
   template <typename T>
   friend class ScopedArenaAllocatorAdapter;
diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h
index 82db60e..eecc55f 100644
--- a/runtime/base/scoped_arena_containers.h
+++ b/runtime/base/scoped_arena_containers.h
@@ -153,9 +153,8 @@
                                                     ArenaAllocatorAdapterKind::Kind()));
   }
   void deallocate(pointer p, size_type n) {
-    UNUSED(p);
-    UNUSED(n);
     DebugStackIndirectTopRef::CheckTop();
+    arena_stack_->MakeInaccessible(p, sizeof(T) * n);
   }
 
   void construct(pointer p, const_reference val) {
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index b6ad547..beabce3 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -2463,6 +2463,9 @@
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = obj}};
     if (sc.Check(soa, true, "EL", args)) {
+      if (obj != nullptr) {
+        down_cast<JNIEnvExt*>(env)->RecordMonitorEnter(obj);
+      }
       JniValueType result;
       result.i = baseEnv(env)->MonitorEnter(env, obj);
       if (sc.Check(soa, false, "i", &result)) {
@@ -2477,6 +2480,9 @@
     ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = obj}};
     if (sc.Check(soa, true, "EL", args)) {
+      if (obj != nullptr) {
+        down_cast<JNIEnvExt*>(env)->CheckMonitorRelease(obj);
+      }
       JniValueType result;
       result.i = baseEnv(env)->MonitorExit(env, obj);
       if (sc.Check(soa, false, "i", &result)) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index dbc5cec..acb39c5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -79,6 +79,7 @@
 #include "scoped_thread_state_change.h"
 #include "handle_scope-inl.h"
 #include "thread-inl.h"
+#include "trace.h"
 #include "utils.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 #include "verifier/method_verifier.h"
@@ -1299,6 +1300,9 @@
 }
 
 void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
+  // Acquire tracing_enabled before locking class linker lock to prevent lock order violation. Since
+  // enabling tracing requires the mutator lock, there are no race conditions here.
+  const bool tracing_enabled = Trace::IsTracingEnabled();
   Thread* const self = Thread::Current();
   WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
@@ -1320,6 +1324,14 @@
     // Need to make sure to not copy ArtMethods without doing read barriers since the roots are
     // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
     boot_class_table_.VisitRoots(buffered_visitor);
+
+    // If tracing is enabled, then mark all the class loaders to prevent unloading.
+    if (tracing_enabled) {
+      for (const ClassLoaderData& data : class_loaders_) {
+        GcRoot<mirror::Object> root(GcRoot<mirror::Object>(self->DecodeJObject(data.weak_root)));
+        root.VisitRoot(visitor, RootInfo(kRootVMInternal));
+      }
+    }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_class_roots_) {
       mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
@@ -2650,10 +2662,8 @@
                                                   const DexFile& dex_file,
                                                   bool allow_failure) {
   // Search assuming unique-ness of dex file.
-  JavaVMExt* const vm = self->GetJniEnv()->vm;
   for (jweak weak_root : dex_caches_) {
-    mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(
-        vm->DecodeWeakGlobal(self, weak_root));
+    mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root));
     if (dex_cache != nullptr && dex_cache->GetDexFile() == &dex_file) {
       return dex_cache;
     }
@@ -6202,10 +6212,9 @@
 
 void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const {
   Thread* const self = Thread::Current();
-  JavaVMExt* const vm = self->GetJniEnv()->vm;
   for (const ClassLoaderData& data : class_loaders_) {
-    auto* const class_loader = down_cast<mirror::ClassLoader*>(
-        vm->DecodeWeakGlobal(self, data.weak_root));
+    // Need to use DecodeJObject so that we get null for cleared JNI weak globals.
+    auto* const class_loader = down_cast<mirror::ClassLoader*>(self->DecodeJObject(data.weak_root));
     if (class_loader != nullptr) {
       visitor->Visit(class_loader);
     }
@@ -6218,8 +6227,8 @@
   JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
   for (auto it = class_loaders_.begin(); it != class_loaders_.end(); ) {
     const ClassLoaderData& data = *it;
-    auto* const class_loader = down_cast<mirror::ClassLoader*>(
-        vm->DecodeWeakGlobal(self, data.weak_root));
+    // Need to use DecodeJObject so that we get null for cleared JNI weak globals.
+    auto* const class_loader = down_cast<mirror::ClassLoader*>(self->DecodeJObject(data.weak_root));
     if (class_loader != nullptr) {
       ++it;
     } else {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 739403f..7f3e938 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -883,6 +883,7 @@
   friend class ImageWriter;  // for GetClassRoots
   friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
   friend class JniCompilerTest;  // for GetRuntimeQuickGenericJniStub
+  friend class JniInternalTest;  // for GetRuntimeQuickGenericJniStub
   ART_FRIEND_TEST(mirror::DexCacheTest, Open);  // for AllocDexCache
 
   DISALLOW_COPY_AND_ASSIGN(ClassLinker);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 450031a..b19381d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -30,6 +30,7 @@
 #include "dex_instruction.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/allocation_record.h"
+#include "gc/scoped_gc_critical_section.h"
 #include "gc/space/large_object_space.h"
 #include "gc/space/space-inl.h"
 #include "handle_scope.h"
@@ -559,14 +560,15 @@
     return;
   }
 
+  Thread* const self = Thread::Current();
   {
     // TODO: dalvik only warned if there were breakpoints left over. clear in Dbg::Disconnected?
-    ReaderMutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
+    ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
     CHECK_EQ(gBreakpoints.size(), 0U);
   }
 
   {
-    MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
+    MutexLock mu(self, *Locks::deoptimization_lock_);
     CHECK_EQ(deoptimization_requests_.size(), 0U);
     CHECK_EQ(full_deoptimization_event_count_, 0U);
     CHECK_EQ(dex_pc_change_event_ref_count_, 0U);
@@ -579,15 +581,11 @@
 
   Runtime* runtime = Runtime::Current();
   ScopedSuspendAll ssa(__FUNCTION__);
-  Thread* self = Thread::Current();
-  ThreadState old_state = self->SetStateUnsafe(kRunnable);
-  CHECK_NE(old_state, kRunnable);
   if (RequiresDeoptimization()) {
     runtime->GetInstrumentation()->EnableDeoptimization();
   }
   instrumentation_events_ = 0;
   gDebuggerActive = true;
-  CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
   LOG(INFO) << "Debugger is active";
 }
 
@@ -602,6 +600,10 @@
   Runtime* runtime = Runtime::Current();
   Thread* self = Thread::Current();
   {
+    // Required for DisableDeoptimization.
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa(__FUNCTION__);
     ThreadState old_state = self->SetStateUnsafe(kRunnable);
     // Debugger may not be active at this point.
@@ -3166,6 +3168,10 @@
   }
   CHECK_EQ(self->GetState(), kRunnable);
   ScopedThreadSuspension sts(self, kWaitingForDeoptimization);
+  // Required for ProcessDeoptimizationRequest.
+  gc::ScopedGCCriticalSection gcs(self,
+                                  gc::kGcCauseInstrumentation,
+                                  gc::kCollectorTypeInstrumentation);
   // We need to suspend mutator threads first.
   ScopedSuspendAll ssa(__FUNCTION__);
   const ThreadState old_state = self->SetStateUnsafe(kRunnable);
diff --git a/runtime/debugger.h b/runtime/debugger.h
index b4d42de..b3617e4 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -731,7 +731,7 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   static void ProcessDeoptimizationRequest(const DeoptimizationRequest& request)
-      REQUIRES(Locks::mutator_lock_);
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);
 
   static void RequestDeoptimizationLocked(const DeoptimizationRequest& req)
       REQUIRES(Locks::deoptimization_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 8ae0b07..f66628d 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -411,12 +411,6 @@
     ThrowNullPointerExceptionForMethodAccess(method_idx, type);
     return nullptr;  // Failure.
   } else if (access_check) {
-    // Incompatible class change should have been handled in resolve method.
-    if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
-      ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
-                                        referrer);
-      return nullptr;  // Failure.
-    }
     mirror::Class* methods_class = resolved_method->GetDeclaringClass();
     mirror::Class* referring_class = referrer->GetDeclaringClass();
     bool can_access_resolved_method =
@@ -426,6 +420,12 @@
       DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
       return nullptr;  // Failure.
     }
+    // Incompatible class change should have been handled in resolve method.
+    if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
+      ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
+                                        referrer);
+      return nullptr;  // Failure.
+    }
   }
   switch (type) {
     case kStatic:
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index fc5c52e..58f256a 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -65,6 +65,9 @@
 static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   JNIEnvExt* env = self->GetJniEnv();
+  if (UNLIKELY(env->check_jni)) {
+    env->CheckNoHeldMonitors();
+  }
   env->locals.SetSegmentState(env->local_ref_cookie);
   env->local_ref_cookie = saved_local_ref_cookie;
   self->PopHandleScope();
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 468179c..0a7a69f 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -620,7 +620,10 @@
     gc_mark_stack_->PushBack(to_ref);
   } else {
     CHECK_EQ(static_cast<uint32_t>(mark_stack_mode),
-             static_cast<uint32_t>(kMarkStackModeGcExclusive));
+             static_cast<uint32_t>(kMarkStackModeGcExclusive))
+        << "ref=" << to_ref
+        << " self->gc_marking=" << self->GetIsGcMarking()
+        << " cc->is_marking=" << is_marking_;
     CHECK(self == thread_running_gc_)
         << "Only GC-running thread should access the mark stack "
         << "in the GC exclusive mark stack mode";
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 95ba380..416510d 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -40,6 +40,8 @@
   kCollectorTypeHeapTrim,
   // A (mostly) concurrent copying collector.
   kCollectorTypeCC,
+  // Instrumentation critical section fake collector.
+  kCollectorTypeInstrumentation,
   // A homogeneous space compaction collector used in background transition
   // when both foreground and background collector are CMS.
   kCollectorTypeHomogeneousSpaceCompact,
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 6be683d..84243df 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -33,6 +33,7 @@
     case kGcCauseDisableMovingGc: return "DisableMovingGc";
     case kGcCauseHomogeneousSpaceCompact: return "HomogeneousSpaceCompact";
     case kGcCauseTrim: return "HeapTrim";
+    case kGcCauseInstrumentation: return "Instrumentation";
     default:
       LOG(FATAL) << "Unreachable";
       UNREACHABLE();
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index 0536f32d..34c7766 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -39,6 +39,8 @@
   kGcCauseDisableMovingGc,
   // Not a real GC cause, used when we trim the heap.
   kGcCauseTrim,
+  // Not a real GC cause, used to implement exclusion between GC and instrumentation.
+  kGcCauseInstrumentation,
   // GC triggered for background transition when both foreground and background collector are CMS.
   kGcCauseHomogeneousSpaceCompact,
 };
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 7d664fa..657fcb5 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1312,6 +1312,13 @@
   ATRACE_END();
 }
 
+void Heap::StartGC(Thread* self, GcCause cause, CollectorType collector_type) {
+  MutexLock mu(self, *gc_complete_lock_);
+  // Ensure there is only one GC at a time.
+  WaitForGcToCompleteLocked(cause, self);
+  collector_type_running_ = collector_type;
+}
+
 void Heap::TrimSpaces(Thread* self) {
   {
     // Need to do this before acquiring the locks since we don't want to get suspended while
@@ -1319,10 +1326,7 @@
     ScopedThreadStateChange tsc(self, kWaitingForGcToComplete);
     // Pretend we are doing a GC to prevent background compaction from deleting the space we are
     // trimming.
-    MutexLock mu(self, *gc_complete_lock_);
-    // Ensure there is only one GC at a time.
-    WaitForGcToCompleteLocked(kGcCauseTrim, self);
-    collector_type_running_ = kCollectorTypeHeapTrim;
+    StartGC(self, kGcCauseTrim, kCollectorTypeHeapTrim);
   }
   ATRACE_BEGIN(__FUNCTION__);
   const uint64_t start_ns = NanoTime();
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index d0d0be3..cc48172 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -775,6 +775,8 @@
       REQUIRES(Locks::mutator_lock_);
 
   void LogGC(GcCause gc_cause, collector::GarbageCollector* collector);
+  void StartGC(Thread* self, GcCause cause, CollectorType collector_type)
+      REQUIRES(!*gc_complete_lock_);
   void FinishGC(Thread* self, collector::GcType gc_type) REQUIRES(!*gc_complete_lock_);
 
   // Create a mem map with a preferred base address.
@@ -1325,6 +1327,7 @@
   friend class collector::MarkSweep;
   friend class collector::SemiSpace;
   friend class ReferenceQueue;
+  friend class ScopedGCCriticalSection;
   friend class VerifyReferenceCardVisitor;
   friend class VerifyReferenceVisitor;
   friend class VerifyObjectVisitor;
diff --git a/runtime/gc/scoped_gc_critical_section.cc b/runtime/gc/scoped_gc_critical_section.cc
new file mode 100644
index 0000000..e7786a1
--- /dev/null
+++ b/runtime/gc/scoped_gc_critical_section.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "scoped_gc_critical_section.h"
+
+#include "gc/collector_type.h"
+#include "gc/heap.h"
+#include "runtime.h"
+#include "thread-inl.h"
+
+namespace art {
+namespace gc {
+
+ScopedGCCriticalSection::ScopedGCCriticalSection(Thread* self,
+                                                 GcCause cause,
+                                                 CollectorType collector_type)
+    : self_(self) {
+  Runtime::Current()->GetHeap()->StartGC(self, cause, collector_type);
+  old_cause_ = self->StartAssertNoThreadSuspension("ScopedGCCriticalSection");
+}
+ScopedGCCriticalSection::~ScopedGCCriticalSection() {
+  self_->EndAssertNoThreadSuspension(old_cause_);
+  Runtime::Current()->GetHeap()->FinishGC(self_, collector::kGcTypeNone);
+}
+
+}  // namespace gc
+}  // namespace art
+
diff --git a/runtime/gc/scoped_gc_critical_section.h b/runtime/gc/scoped_gc_critical_section.h
new file mode 100644
index 0000000..ec93bca
--- /dev/null
+++ b/runtime/gc/scoped_gc_critical_section.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_SCOPED_GC_CRITICAL_SECTION_H_
+#define ART_RUNTIME_GC_SCOPED_GC_CRITICAL_SECTION_H_
+
+#include "base/mutex.h"
+#include "collector_type.h"
+#include "gc_cause.h"
+
+namespace art {
+
+class Thread;
+
+namespace gc {
+
+// Wait until the GC is finished and then prevent GC from starting until the destructor. Used
+// to prevent deadlocks in places where we call ClassLinker::VisitClass with all th threads
+// suspended.
+class ScopedGCCriticalSection {
+ public:
+  ScopedGCCriticalSection(Thread* self, GcCause cause, CollectorType collector_type)
+      ACQUIRE(Roles::uninterruptible_);
+  ~ScopedGCCriticalSection() RELEASE(Roles::uninterruptible_);
+
+ private:
+  Thread* const self_;
+  const char* old_cause_;
+};
+
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_SCOPED_GC_CRITICAL_SECTION_H_
diff --git a/runtime/globals.h b/runtime/globals.h
index d70f3ab..987a94e 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -58,12 +58,6 @@
 static constexpr bool kIsTargetBuild = false;
 #endif
 
-#if defined(ART_USE_OPTIMIZING_COMPILER)
-static constexpr bool kUseOptimizingCompiler = true;
-#else
-static constexpr bool kUseOptimizingCompiler = false;
-#endif
-
 // Garbage collector constants.
 static constexpr bool kMovingCollector = true;
 static constexpr bool kMarkCompactSupport = false && kMovingCollector;
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index c9ba6cf..a5b63b4 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -17,6 +17,7 @@
 #include "indirect_reference_table-inl.h"
 
 #include "jni_internal.h"
+#include "nth_caller_visitor.h"
 #include "reference_table.h"
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 8046056..612ca14 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -146,9 +146,13 @@
 
   // Deoptimization.
   void EnableDeoptimization()
-      REQUIRES(Locks::mutator_lock_, !deoptimized_methods_lock_);
+      REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!deoptimized_methods_lock_);
+  // Calls UndeoptimizeEverything which may visit class linker classes through ConfigureStubs.
   void DisableDeoptimization(const char* key)
-      REQUIRES(Locks::mutator_lock_, !deoptimized_methods_lock_);
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+      REQUIRES(!deoptimized_methods_lock_);
+
   bool AreAllMethodsDeoptimized() const {
     return interpreter_stubs_installed_;
   }
@@ -156,12 +160,17 @@
 
   // Executes everything with interpreter.
   void DeoptimizeEverything(const char* key)
-      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+      REQUIRES(!Locks::thread_list_lock_,
+               !Locks::classlinker_classes_lock_,
                !deoptimized_methods_lock_);
 
-  // Executes everything with compiled code (or interpreter if there is no code).
+  // Executes everything with compiled code (or interpreter if there is no code). May visit class
+  // linker classes through ConfigureStubs.
   void UndeoptimizeEverything(const char* key)
-      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+      REQUIRES(!Locks::thread_list_lock_,
+               !Locks::classlinker_classes_lock_,
                !deoptimized_methods_lock_);
 
   // Deoptimize a method by forcing its execution with the interpreter. Nevertheless, a static
@@ -183,12 +192,16 @@
   // Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
   void EnableMethodTracing(const char* key,
                            bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
-      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+      REQUIRES(!Locks::thread_list_lock_,
+               !Locks::classlinker_classes_lock_,
                !deoptimized_methods_lock_);
 
   // Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
   void DisableMethodTracing(const char* key)
-      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+      REQUIRES(!Locks::thread_list_lock_,
+               !Locks::classlinker_classes_lock_,
                !deoptimized_methods_lock_);
 
   InterpreterHandlerTable GetInterpreterHandlerTable() const
@@ -393,7 +406,9 @@
   // instrumentation level it needs. Therefore the current instrumentation level
   // becomes the highest instrumentation level required by a client.
   void ConfigureStubs(const char* key, InstrumentationLevel desired_instrumentation_level)
-      REQUIRES(Locks::mutator_lock_, !deoptimized_methods_lock_, !Locks::thread_list_lock_,
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+      REQUIRES(!deoptimized_methods_lock_,
+               !Locks::thread_list_lock_,
                !Locks::classlinker_classes_lock_);
 
   void UpdateInterpreterHandlerTable() REQUIRES(Locks::mutator_lock_) {
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index d98d246..e4688a2 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -20,6 +20,7 @@
 #include "common_throws.h"
 #include "class_linker-inl.h"
 #include "dex_file.h"
+#include "gc/scoped_gc_critical_section.h"
 #include "handle_scope-inl.h"
 #include "jvalue.h"
 #include "runtime.h"
@@ -151,6 +152,9 @@
     ScopedObjectAccess soa(Thread::Current());
     instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
     ScopedThreadSuspension sts(soa.Self(), kSuspended);
+    gc::ScopedGCCriticalSection gcs(soa.Self(),
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa("Instrumentation::ConfigureStubs");
     instr->ConfigureStubs(key, level);
   }
@@ -203,6 +207,9 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa("Single method deoptimization");
     if (enable_deoptimization) {
       instrumentation->EnableDeoptimization();
@@ -216,6 +223,9 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa("Single method undeoptimization");
     instrumentation->Undeoptimize(method);
     if (disable_deoptimization) {
@@ -228,6 +238,9 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa("Full deoptimization");
     if (enable_deoptimization) {
       instrumentation->EnableDeoptimization();
@@ -240,6 +253,9 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa("Full undeoptimization");
     instrumentation->UndeoptimizeEverything(key);
     if (disable_deoptimization) {
@@ -252,6 +268,9 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa("EnableMethodTracing");
     instrumentation->EnableMethodTracing(key, needs_interpreter);
   }
@@ -261,6 +280,9 @@
     Runtime* runtime = Runtime::Current();
     instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
     ScopedThreadSuspension sts(self, kSuspended);
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa("EnableMethodTracing");
     instrumentation->DisableMethodTracing(key);
   }
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index e9c16c1..9b9c5d2 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -111,6 +111,9 @@
   DCHECK(this_object != nullptr);
   ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*));
   if (info != nullptr) {
+    // Since the instrumentation is marked from the declaring class we need to mark the card so
+    // that mod-union tables and card rescanning know about the update.
+    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass());
     info->AddInvokeInfo(thread, dex_pc, this_object->GetClass());
   }
 }
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index b18b430..4104d7a 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -16,10 +16,17 @@
 
 #include "jni_env_ext.h"
 
+#include <algorithm>
+#include <vector>
+
 #include "check_jni.h"
 #include "indirect_reference_table.h"
 #include "java_vm_ext.h"
 #include "jni_internal.h"
+#include "lock_word.h"
+#include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
+#include "thread-inl.h"
 
 namespace art {
 
@@ -63,14 +70,14 @@
 JNIEnvExt::~JNIEnvExt() {
 }
 
-jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) {
   if (obj == nullptr) {
     return nullptr;
   }
   return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
 }
 
-void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::DeleteLocalRef(jobject obj) {
   if (obj != nullptr) {
     locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
   }
@@ -86,14 +93,14 @@
   monitors.Dump(os);
 }
 
-void JNIEnvExt::PushFrame(int capacity) SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::PushFrame(int capacity) {
   UNUSED(capacity);  // cpplint gets confused with (int) and thinks its a cast.
   // TODO: take 'capacity' into account.
   stacked_local_ref_cookies.push_back(local_ref_cookie);
   local_ref_cookie = locals.GetSegmentState();
 }
 
-void JNIEnvExt::PopFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::PopFrame() {
   locals.SetSegmentState(local_ref_cookie);
   local_ref_cookie = stacked_local_ref_cookies.back();
   stacked_local_ref_cookies.pop_back();
@@ -104,4 +111,118 @@
                 IndirectReferenceTable::SegmentStateOffset().Int32Value());
 }
 
+// Use some defining part of the caller's frame as the identifying mark for the JNI segment.
+static uintptr_t GetJavaCallFrame(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) {
+  NthCallerVisitor zeroth_caller(self, 0, false);
+  zeroth_caller.WalkStack();
+  if (zeroth_caller.caller == nullptr) {
+    // No Java code, must be from pure native code.
+    return 0;
+  } else if (zeroth_caller.GetCurrentQuickFrame() == nullptr) {
+    // Shadow frame = interpreter. Use the actual shadow frame's address.
+    DCHECK(zeroth_caller.GetCurrentShadowFrame() != nullptr);
+    return reinterpret_cast<uintptr_t>(zeroth_caller.GetCurrentShadowFrame());
+  } else {
+    // Quick frame = compiled code. Use the bottom of the frame.
+    return reinterpret_cast<uintptr_t>(zeroth_caller.GetCurrentQuickFrame());
+  }
+}
+
+void JNIEnvExt::RecordMonitorEnter(jobject obj) {
+  locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self), obj));
+}
+
+static std::string ComputeMonitorDescription(Thread* self,
+                                             jobject obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+  mirror::Object* o = self->DecodeJObject(obj);
+  if ((o->GetLockWord(false).GetState() == LockWord::kThinLocked) &&
+      Locks::mutator_lock_->IsExclusiveHeld(self)) {
+    // Getting the identity hashcode here would result in lock inflation and suspension of the
+    // current thread, which isn't safe if this is the only runnable thread.
+    return StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)",
+                        reinterpret_cast<intptr_t>(o),
+                        PrettyTypeOf(o).c_str());
+  } else {
+    // IdentityHashCode can cause thread suspension, which would invalidate o if it moved. So
+    // we get the pretty type before we call IdentityHashCode.
+    const std::string pretty_type(PrettyTypeOf(o));
+    return StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), pretty_type.c_str());
+  }
+}
+
+static void RemoveMonitors(Thread* self,
+                           uintptr_t frame,
+                           ReferenceTable* monitors,
+                           std::vector<std::pair<uintptr_t, jobject>>* locked_objects)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  auto kept_end = std::remove_if(
+      locked_objects->begin(),
+      locked_objects->end(),
+      [self, frame, monitors](const std::pair<uintptr_t, jobject>& pair)
+          SHARED_REQUIRES(Locks::mutator_lock_) {
+        if (frame == pair.first) {
+          mirror::Object* o = self->DecodeJObject(pair.second);
+          monitors->Remove(o);
+          return true;
+        }
+        return false;
+      });
+  locked_objects->erase(kept_end, locked_objects->end());
+}
+
+void JNIEnvExt::CheckMonitorRelease(jobject obj) {
+  uintptr_t current_frame = GetJavaCallFrame(self);
+  std::pair<uintptr_t, jobject> exact_pair = std::make_pair(current_frame, obj);
+  auto it = std::find(locked_objects_.begin(), locked_objects_.end(), exact_pair);
+  bool will_abort = false;
+  if (it != locked_objects_.end()) {
+    locked_objects_.erase(it);
+  } else {
+    // Check whether this monitor was locked in another JNI "session."
+    mirror::Object* mirror_obj = self->DecodeJObject(obj);
+    for (std::pair<uintptr_t, jobject>& pair : locked_objects_) {
+      if (self->DecodeJObject(pair.second) == mirror_obj) {
+        std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
+        vm->JniAbortF("<JNI MonitorExit>",
+                      "Unlocking monitor that wasn't locked here: %s",
+                      monitor_descr.c_str());
+        will_abort = true;
+        break;
+      }
+    }
+  }
+
+  // When we abort, also make sure that any locks from the current "session" are removed from
+  // the monitors table, otherwise we may visit local objects in GC during abort (which won't be
+  // valid anymore).
+  if (will_abort) {
+    RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+  }
+}
+
+void JNIEnvExt::CheckNoHeldMonitors() {
+  uintptr_t current_frame = GetJavaCallFrame(self);
+  // The locked_objects_ are grouped by their stack frame component, as this enforces structured
+  // locking, and the groups form a stack. So the current frame entries are at the end. Check
+  // whether the vector is empty, and when there are elements, whether the last element belongs
+  // to this call - this signals that there are unlocked monitors.
+  if (!locked_objects_.empty()) {
+    std::pair<uintptr_t, jobject>& pair = locked_objects_[locked_objects_.size() - 1];
+    if (pair.first == current_frame) {
+      std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
+      vm->JniAbortF("<JNI End>",
+                    "Still holding a locked object on JNI end: %s",
+                    monitor_descr.c_str());
+      // When we abort, also make sure that any locks from the current "session" are removed from
+      // the monitors table, otherwise we may visit local objects in GC during abort.
+      RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+    } else if (kIsDebugBuild) {
+      // Make sure there are really no other entries and our checking worked as expected.
+      for (std::pair<uintptr_t, jobject>& check_pair : locked_objects_) {
+        CHECK_NE(check_pair.first, current_frame);
+      }
+    }
+  }
+}
+
 }  // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 9b55536..3828ff0 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -43,8 +43,8 @@
 
   void SetCheckJniEnabled(bool enabled);
 
-  void PushFrame(int capacity);
-  void PopFrame();
+  void PushFrame(int capacity) SHARED_REQUIRES(Locks::mutator_lock_);
+  void PopFrame() SHARED_REQUIRES(Locks::mutator_lock_);
 
   template<typename T>
   T AddLocalReference(mirror::Object* obj)
@@ -89,10 +89,27 @@
   // Used by -Xcheck:jni.
   const JNINativeInterface* unchecked_functions;
 
+  // Functions to keep track of monitor lock and unlock operations. Used to ensure proper locking
+  // rules in CheckJNI mode.
+
+  // Record locking of a monitor.
+  void RecordMonitorEnter(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Check the release, that is, that the release is performed in the same JNI "segment."
+  void CheckMonitorRelease(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Check that no monitors are held that have been acquired in this JNI "segment."
+  void CheckNoHeldMonitors() SHARED_REQUIRES(Locks::mutator_lock_);
+
  private:
   // The constructor should not be called directly. It may leave the object in an erronuous state,
   // and the result needs to be checked.
   JNIEnvExt(Thread* self, JavaVMExt* vm);
+
+  // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
+  // to ensure that only monitors locked in this native frame are being unlocked, and that at
+  // the end all are unlocked.
+  std::vector<std::pair<uintptr_t, jobject>> locked_objects_;
 };
 
 // Used to save and restore the JNIEnvExt state when not going through code created by the JNI
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 2a0cb28..41b368e 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -607,11 +607,64 @@
     EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
   }
 
+  void SetUpForTest(bool direct, const char* method_name, const char* method_sig,
+                    void* native_fnptr) {
+    // Initialize class loader and set generic JNI entrypoint.
+    // Note: this code is adapted from the jni_compiler_test, and taken with minimal modifications.
+    if (!runtime_->IsStarted()) {
+      {
+        ScopedObjectAccess soa(Thread::Current());
+        class_loader_ = LoadDex("MyClassNatives");
+        StackHandleScope<1> hs(soa.Self());
+        Handle<mirror::ClassLoader> loader(
+            hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
+        mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
+        const auto pointer_size = class_linker_->GetImagePointerSize();
+        ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) :
+            c->FindVirtualMethod(method_name, method_sig, pointer_size);
+        ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
+        method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
+      }
+      // Start runtime.
+      Thread::Current()->TransitionFromSuspendedToRunnable();
+      bool started = runtime_->Start();
+      CHECK(started);
+    }
+    // JNI operations after runtime start.
+    env_ = Thread::Current()->GetJniEnv();
+    jklass_ = env_->FindClass("MyClassNatives");
+    ASSERT_TRUE(jklass_ != nullptr) << method_name << " " << method_sig;
+
+    if (direct) {
+      jmethod_ = env_->GetStaticMethodID(jklass_, method_name, method_sig);
+    } else {
+      jmethod_ = env_->GetMethodID(jklass_, method_name, method_sig);
+    }
+    ASSERT_TRUE(jmethod_ != nullptr) << method_name << " " << method_sig;
+
+    if (native_fnptr != nullptr) {
+      JNINativeMethod methods[] = { { method_name, method_sig, native_fnptr } };
+      ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1))
+          << method_name << " " << method_sig;
+    } else {
+      env_->UnregisterNatives(jklass_);
+    }
+
+    jmethodID constructor = env_->GetMethodID(jklass_, "<init>", "()V");
+    jobj_ = env_->NewObject(jklass_, constructor);
+    ASSERT_TRUE(jobj_ != nullptr) << method_name << " " << method_sig;
+  }
+
   JavaVMExt* vm_;
   JNIEnv* env_;
   jclass aioobe_;
   jclass ase_;
   jclass sioobe_;
+
+  jclass jklass_;
+  jobject jobj_;
+  jobject class_loader_;
+  jmethodID jmethod_;
 };
 
 TEST_F(JniInternalTest, AllocObject) {
@@ -2111,4 +2164,38 @@
   }
 }
 
+void Java_MyClassNatives_foo_exit(JNIEnv* env, jobject thisObj) {
+  // Release the monitor on self. This should trigger an abort.
+  env->MonitorExit(thisObj);
+}
+
+TEST_F(JniInternalTest, MonitorExitLockedInDifferentCall) {
+  SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo_exit));
+  ASSERT_NE(jobj_, nullptr);
+
+  env_->MonitorEnter(jobj_);
+  EXPECT_FALSE(env_->ExceptionCheck());
+
+  CheckJniAbortCatcher check_jni_abort_catcher;
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+  check_jni_abort_catcher.Check("Unlocking monitor that wasn't locked here");
+}
+
+void Java_MyClassNatives_foo_enter_no_exit(JNIEnv* env, jobject thisObj) {
+  // Acquire but don't release the monitor on self. This should trigger an abort on return.
+  env->MonitorEnter(thisObj);
+}
+
+TEST_F(JniInternalTest, MonitorExitNotAllUnlocked) {
+  SetUpForTest(false,
+               "foo",
+               "()V",
+               reinterpret_cast<void*>(&Java_MyClassNatives_foo_enter_no_exit));
+  ASSERT_NE(jobj_, nullptr);
+
+  CheckJniAbortCatcher check_jni_abort_catcher;
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+  check_jni_abort_catcher.Check("Still holding a locked object on JNI end");
+}
+
 }  // namespace art
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 3b84bfa..4aebc2c 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -192,28 +192,38 @@
   }
 }
 
-static void DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
-  std::unique_ptr<std::vector<const DexFile*>> dex_files = ConvertJavaArrayToNative(env, cookie);
-  if (dex_files.get() == nullptr) {
-    DCHECK(env->ExceptionCheck());
-    return;
-  }
-
+static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
   ScopedObjectAccess soa(env);
+  mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
+  if (dex_files_object == nullptr) {
+    ThrowNullPointerException("cookie == null");
+    return JNI_FALSE;
+  }
+  mirror::LongArray* dex_files = dex_files_object->AsLongArray();
 
-  // The Runtime currently never unloads classes, which means any registered
-  // dex files must be kept around forever in case they are used. We
-  // accomplish this here by explicitly leaking those dex files that are
-  // registered.
-  //
-  // TODO: The Runtime should support unloading of classes and freeing of the
-  // dex files for those unloaded classes rather than leaking dex files here.
+  // 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::Current()->GetClassLinker();
-  for (const DexFile* dex_file : *dex_files) {
+  bool all_deleted = true;
+  for (int32_t i = 0, count = dex_files->GetLength(); i < count; ++i) {
+    auto* dex_file = reinterpret_cast<DexFile*>(dex_files->Get(i));
+    if (dex_file == nullptr) {
+      continue;
+    }
+    // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
+    // are calls to DexFile.close while the ART DexFile is still in use.
     if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) {
+      // Clear the element in the array so that we can call close again.
+      dex_files->Set(i, 0);
       delete dex_file;
+    } else {
+      all_deleted = false;
     }
   }
+
+  // TODO: Also unmap the OatFile for this dalvik.system.DexFile.
+
+  return all_deleted ? JNI_TRUE : JNI_FALSE;
 }
 
 static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
@@ -379,7 +389,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)V"),
+  NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
   NATIVE_METHOD(DexFile, defineClassNative,
                 "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;)Ljava/lang/Class;"),
   NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 8fd6849..5da15df 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -229,6 +229,65 @@
   return nullptr;
 }
 
+static mirror::Field* GetPublicFieldRecursive(
+    Thread* self, mirror::Class* clazz, mirror::String* name)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  DCHECK(clazz != nullptr);
+  DCHECK(name != nullptr);
+  DCHECK(self != nullptr);
+
+  StackHandleScope<1> hs(self);
+  MutableHandle<mirror::Class> h_clazz(hs.NewHandle(clazz));
+
+  // We search the current class, its direct interfaces then its superclass.
+  while (h_clazz.Get() != nullptr) {
+    mirror::Field* result = GetDeclaredField(self, h_clazz.Get(), name);
+    if ((result != nullptr) && (result->GetAccessFlags() & kAccPublic)) {
+      return result;
+    } else if (UNLIKELY(self->IsExceptionPending())) {
+      // Something went wrong. Bail out.
+      return nullptr;
+    }
+
+    uint32_t num_direct_interfaces = h_clazz->NumDirectInterfaces();
+    for (uint32_t i = 0; i < num_direct_interfaces; i++) {
+      mirror::Class *iface = mirror::Class::GetDirectInterface(self, h_clazz, i);
+      if (UNLIKELY(iface == nullptr)) {
+        self->AssertPendingException();
+        return nullptr;
+      }
+      result = GetPublicFieldRecursive(self, iface, name);
+      if (result != nullptr) {
+        DCHECK(result->GetAccessFlags() & kAccPublic);
+        return result;
+      } else if (UNLIKELY(self->IsExceptionPending())) {
+        // Something went wrong. Bail out.
+        return nullptr;
+      }
+    }
+
+    // We don't try the superclass if we are an interface.
+    if (h_clazz->IsInterface()) {
+      break;
+    }
+
+    // Get the next class.
+    h_clazz.Assign(h_clazz->GetSuperClass());
+  }
+  return nullptr;
+}
+
+static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstring name) {
+  ScopedFastNativeObjectAccess soa(env);
+  auto* name_string = soa.Decode<mirror::String*>(name);
+  if (UNLIKELY(name_string == nullptr)) {
+    ThrowNullPointerException("name == null");
+    return nullptr;
+  }
+  return soa.AddLocalReference<jobject>(
+      GetPublicFieldRecursive(soa.Self(), DecodeClass(soa, javaThis), name_string));
+}
+
 static jobject Class_getDeclaredFieldInternal(JNIEnv* env, jobject javaThis, jstring name) {
   ScopedFastNativeObjectAccess soa(env);
   auto* name_string = soa.Decode<mirror::String*>(name);
@@ -678,6 +737,7 @@
                 "!([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"),
   NATIVE_METHOD(Class, getDeclaredConstructorsInternal, "!(Z)[Ljava/lang/reflect/Constructor;"),
   NATIVE_METHOD(Class, getDeclaredField, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
+  NATIVE_METHOD(Class, getPublicFieldRecursive, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
   NATIVE_METHOD(Class, getDeclaredFieldInternal, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
   NATIVE_METHOD(Class, getDeclaredFields, "!()[Ljava/lang/reflect/Field;"),
   NATIVE_METHOD(Class, getDeclaredFieldsUnchecked, "!(Z)[Ljava/lang/reflect/Field;"),
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index e1e9ceb..45b9484 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -103,10 +103,17 @@
     // If caller is null, then we called from JNI, just avoid the check since JNI avoids most
     // access checks anyways. TODO: Investigate if this the correct behavior.
     if (caller != nullptr && !caller->CanAccess(c.Get())) {
-      soa.Self()->ThrowNewExceptionF(
-          "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
-          PrettyClass(c.Get()).c_str(), PrettyClass(caller).c_str());
-      return nullptr;
+      if (PrettyDescriptor(c.Get()) == "dalvik.system.DexPathList$Element") {
+        // b/20699073.
+        LOG(WARNING) << "The dalvik.system.DexPathList$Element constructor is not accessible by "
+                        "default. This is a temporary workaround for backwards compatibility "
+                        "with class-loader hacks. Please update your application.";
+      } else {
+        soa.Self()->ThrowNewExceptionF(
+            "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
+            PrettyClass(c.Get()).c_str(), PrettyClass(caller).c_str());
+        return nullptr;
+      }
     }
   }
   if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), c, true, true)) {
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index daae401..85ac4aa 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -62,8 +62,10 @@
     if (heap != nullptr && heap->GetReadBarrierTable()->IsSet(old_ref)) {
       ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
       // Update the field atomically. This may fail if mutator updates before us, but it's ok.
-      obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>(
-          offset, old_ref, ref);
+      if (ref != old_ref) {
+        obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>(
+            offset, old_ref, ref);
+      }
     }
     AssertToSpaceInvariant(obj, offset, ref);
     return ref;
@@ -90,17 +92,17 @@
     // To be implemented.
     return ref;
   } else if (with_read_barrier && kUseTableLookupReadBarrier) {
-    if (kMaybeDuringStartup && IsDuringStartup()) {
-      // During startup, the heap may not be initialized yet. Just
-      // return the given ref.
-      return ref;
-    }
-    if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
+    Thread* self = Thread::Current();
+    if (self != nullptr &&
+        self->GetIsGcMarking() &&
+        Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
       MirrorType* old_ref = ref;
       ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
       // Update the field atomically. This may fail if mutator updates before us, but it's ok.
-      Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
-      atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref);
+      if (ref != old_ref) {
+        Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
+        atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref);
+      }
     }
     AssertToSpaceInvariant(gc_root_source, ref);
     return ref;
@@ -127,19 +129,19 @@
     // To be implemented.
     return ref;
   } else if (with_read_barrier && kUseTableLookupReadBarrier) {
-    if (kMaybeDuringStartup && IsDuringStartup()) {
-      // During startup, the heap may not be initialized yet. Just
-      // return the given ref.
-      return ref;
-    }
-    if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
+    Thread* self = Thread::Current();
+    if (self != nullptr &&
+        self->GetIsGcMarking() &&
+        Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
       auto old_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
       ref = reinterpret_cast<MirrorType*>(Mark(ref));
       auto new_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
       // Update the field atomically. This may fail if mutator updates before us, but it's ok.
-      auto* atomic_root =
-          reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root);
-      atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref);
+      if (new_ref.AsMirrorPtr() != old_ref.AsMirrorPtr()) {
+        auto* atomic_root =
+            reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root);
+        atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref);
+      }
     }
     AssertToSpaceInvariant(gc_root_source, ref);
     return ref;
diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h
index 710c21f..8e5b187 100644
--- a/runtime/read_barrier_c.h
+++ b/runtime/read_barrier_c.h
@@ -26,10 +26,16 @@
 // table-lookup read barriers.
 
 #ifdef ART_USE_READ_BARRIER
+#if ART_READ_BARRIER_TYPE_IS_BAKER
 #define USE_BAKER_READ_BARRIER
-// #define USE_BROOKS_READ_BARRIER
-// #define USE_TABLE_LOOKUP_READ_BARRIER
+#elif ART_READ_BARRIER_TYPE_IS_BROOKS
+#define USE_BROOKS_READ_BARRIER
+#elif ART_READ_BARRIER_TYPE_IS_TABLELOOKUP
+#define USE_TABLE_LOOKUP_READ_BARRIER
+#else
+#error "ART read barrier type must be set"
 #endif
+#endif  // ART_USE_READ_BARRIER
 
 #ifdef ART_HEAP_POISONING
 #define USE_HEAP_POISONING
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 7f72f8a..1d21a64 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -110,7 +110,7 @@
 }
 
 InlineInfo StackVisitor::GetCurrentInlineInfo() const {
-  ArtMethod* outer_method = *GetCurrentQuickFrame();
+  ArtMethod* outer_method = GetOuterMethod();
   uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
   CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
   StackMapEncoding encoding = code_info.ExtractEncoding();
@@ -194,11 +194,12 @@
 }
 
 bool StackVisitor::IsReferenceVReg(ArtMethod* m, uint16_t vreg) {
+  DCHECK_EQ(m, GetMethod());
   // Process register map (which native and runtime methods don't have)
   if (m->IsNative() || m->IsRuntimeMethod() || m->IsProxyMethod()) {
     return false;
   }
-  if (m->IsOptimized(sizeof(void*))) {
+  if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
     return true;  // TODO: Implement.
   }
   const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*));
@@ -251,7 +252,7 @@
     if (GetVRegFromDebuggerShadowFrame(vreg, kind, val)) {
       return true;
     }
-    if (m->IsOptimized(sizeof(void*))) {
+    if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
       return GetVRegFromOptimizedCode(m, vreg, kind, val);
     } else {
       return GetVRegFromQuickCode(m, vreg, kind, val);
@@ -288,15 +289,15 @@
 
 bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKind kind,
                                             uint32_t* val) const {
+  ArtMethod* outer_method = GetOuterMethod();
+  const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*));
+  DCHECK(code_pointer != nullptr);
   DCHECK_EQ(m, GetMethod());
   const DexFile::CodeItem* code_item = m->GetCodeItem();
   DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be null or how would we compile
                                                     // its instructions?
   uint16_t number_of_dex_registers = code_item->registers_size_;
   DCHECK_LT(vreg, code_item->registers_size_);
-  ArtMethod* outer_method = *GetCurrentQuickFrame();
-  const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*));
-  DCHECK(code_pointer != nullptr);
   CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
   StackMapEncoding encoding = code_info.ExtractEncoding();
 
@@ -405,7 +406,7 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably read registers without a context.
     DCHECK(m == GetMethod());
-    if (m->IsOptimized(sizeof(void*))) {
+    if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
       return GetVRegPairFromOptimizedCode(m, vreg, kind_lo, kind_hi, val);
     } else {
       return GetVRegPairFromQuickCode(m, vreg, kind_lo, kind_hi, val);
@@ -481,7 +482,7 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
     DCHECK(m == GetMethod());
-    if (m->IsOptimized(sizeof(void*))) {
+    if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
       return false;
     } else {
       return SetVRegFromQuickCode(m, vreg, new_value, kind);
@@ -590,7 +591,7 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
     DCHECK(m == GetMethod());
-    if (m->IsOptimized(sizeof(void*))) {
+    if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
       return false;
     } else {
       return SetVRegPairFromQuickCode(m, vreg, new_value, kind_lo, kind_hi);
@@ -724,14 +725,14 @@
 uintptr_t StackVisitor::GetReturnPc() const {
   uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame());
   DCHECK(sp != nullptr);
-  uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue();
+  uint8_t* pc_addr = sp + GetOuterMethod()->GetReturnPcOffset().SizeValue();
   return *reinterpret_cast<uintptr_t*>(pc_addr);
 }
 
 void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) {
   uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame());
   CHECK(sp != nullptr);
-  uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue();
+  uint8_t* pc_addr = sp + GetOuterMethod()->GetReturnPcOffset().SizeValue();
   *reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc;
 }
 
diff --git a/runtime/stack.h b/runtime/stack.h
index 292c745..31acf0e 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -473,6 +473,10 @@
 
   ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_);
 
+  ArtMethod* GetOuterMethod() const {
+    return *GetCurrentQuickFrame();
+  }
+
   bool IsShadowFrame() const {
     return cur_shadow_frame_ != nullptr;
   }
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index b3dd2eb..f5d20bd 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -118,11 +118,8 @@
   }
 }
 
-inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
-  AssertThreadSuspensionIsAllowable();
+inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state) {
   DCHECK_NE(new_state, kRunnable);
-  DCHECK_EQ(this, Thread::Current());
-  // Change to non-runnable state, thereby appearing suspended to the system.
   DCHECK_EQ(GetState(), kRunnable);
   union StateAndFlags old_state_and_flags;
   union StateAndFlags new_state_and_flags;
@@ -145,12 +142,9 @@
       break;
     }
   }
+}
 
-  // Change to non-runnable state, thereby appearing suspended to the system.
-  // Mark the release of the share of the mutator_lock_.
-  Locks::mutator_lock_->TransitionFromRunnableToSuspended(this);
-
-  // Once suspended - check the active suspend barrier flag
+inline void Thread::PassActiveSuspendBarriers() {
   while (true) {
     uint16_t current_flags = tls32_.state_and_flags.as_struct.flags;
     if (LIKELY((current_flags & (kCheckpointRequest | kActiveSuspendBarrier)) == 0)) {
@@ -159,11 +153,22 @@
       PassActiveSuspendBarriers(this);
     } else {
       // Impossible
-      LOG(FATAL) << "Fatal, thread transited into suspended without running the checkpoint";
+      LOG(FATAL) << "Fatal, thread transitioned into suspended without running the checkpoint";
     }
   }
 }
 
+inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
+  AssertThreadSuspensionIsAllowable();
+  DCHECK_EQ(this, Thread::Current());
+  // Change to non-runnable state, thereby appearing suspended to the system.
+  TransitionToSuspendedAndRunCheckpoints(new_state);
+  // Mark the release of the share of the mutator_lock_.
+  Locks::mutator_lock_->TransitionFromRunnableToSuspended(this);
+  // Once suspended - check the active suspend barrier flag
+  PassActiveSuspendBarriers();
+}
+
 inline ThreadState Thread::TransitionFromSuspendedToRunnable() {
   union StateAndFlags old_state_and_flags;
   old_state_and_flags.as_int = tls32_.state_and_flags.as_int;
diff --git a/runtime/thread.h b/runtime/thread.h
index d21644d..d262c62 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -247,17 +247,15 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Transition from non-runnable to runnable state acquiring share on mutator_lock_.
-  ThreadState TransitionFromSuspendedToRunnable()
+  ALWAYS_INLINE ThreadState TransitionFromSuspendedToRunnable()
       REQUIRES(!Locks::thread_suspend_count_lock_)
-      SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
-      ALWAYS_INLINE;
+      SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
 
   // Transition from runnable into a state where mutator privileges are denied. Releases share of
   // mutator lock.
-  void TransitionFromRunnableToSuspended(ThreadState new_state)
+  ALWAYS_INLINE void TransitionFromRunnableToSuspended(ThreadState new_state)
       REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_)
-      UNLOCK_FUNCTION(Locks::mutator_lock_)
-      ALWAYS_INLINE;
+      UNLOCK_FUNCTION(Locks::mutator_lock_);
 
   // Once called thread suspension will cause an assertion failure.
   const char* StartAssertNoThreadSuspension(const char* cause) ACQUIRE(Roles::uninterruptible_) {
@@ -1017,11 +1015,15 @@
   // Dbg::Disconnected.
   ThreadState SetStateUnsafe(ThreadState new_state) {
     ThreadState old_state = GetState();
-    tls32_.state_and_flags.as_struct.state = new_state;
-    // if transit to a suspended state, check the pass barrier request.
-    if (UNLIKELY((new_state != kRunnable) &&
-                 (tls32_.state_and_flags.as_struct.flags & kActiveSuspendBarrier))) {
-      PassActiveSuspendBarriers(this);
+    if (old_state == kRunnable && new_state != kRunnable) {
+      // Need to run pending checkpoint and suspend barriers. Run checkpoints in runnable state in
+      // case they need to use a ScopedObjectAccess. If we are holding the mutator lock and a SOA
+      // attempts to TransitionFromSuspendedToRunnable, it results in a deadlock.
+      TransitionToSuspendedAndRunCheckpoints(new_state);
+      // Since we transitioned to a suspended state, check the pass barrier requests.
+      PassActiveSuspendBarriers();
+    } else {
+      tls32_.state_and_flags.as_struct.state = new_state;
     }
     return old_state;
   }
@@ -1064,6 +1066,12 @@
   void SetUpAlternateSignalStack();
   void TearDownAlternateSignalStack();
 
+  ALWAYS_INLINE void TransitionToSuspendedAndRunCheckpoints(ThreadState new_state)
+      REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_);
+
+  ALWAYS_INLINE void PassActiveSuspendBarriers()
+      REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_);
+
   // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to
   // change from being Suspended to Runnable without a suspend request occurring.
   union PACKED(4) StateAndFlags {
diff --git a/runtime/trace.cc b/runtime/trace.cc
index e2743ce..745aa63 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -31,6 +31,7 @@
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "gc/scoped_gc_critical_section.h"
 #include "instrumentation.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
@@ -350,6 +351,10 @@
 
   // Create Trace object.
   {
+    // Required since EnableMethodTracing calls ConfigureStubs which visits class linker classes.
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa(__FUNCTION__);
     MutexLock mu(self, *Locks::trace_lock_);
     if (the_trace_ != nullptr) {
@@ -464,9 +469,10 @@
   Runtime* runtime = Runtime::Current();
   Trace* the_trace = nullptr;
 
+  Thread* const self = Thread::Current();
   pthread_t sampling_pthread = 0U;
   {
-    MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+    MutexLock mu(self, *Locks::trace_lock_);
     if (the_trace_ == nullptr) {
       LOG(ERROR) << "Trace pause requested, but no trace currently running";
       return;
@@ -478,23 +484,26 @@
 
   if (sampling_pthread != 0U) {
     {
-      MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+      MutexLock mu(self, *Locks::trace_lock_);
       the_trace_ = nullptr;
     }
     CHECK_PTHREAD_CALL(pthread_join, (sampling_pthread, nullptr), "sampling thread shutdown");
     sampling_pthread_ = 0U;
     {
-      MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+      MutexLock mu(self, *Locks::trace_lock_);
       the_trace_ = the_trace;
     }
   }
 
   if (the_trace != nullptr) {
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa(__FUNCTION__);
     stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
 
     if (the_trace->trace_mode_ == TraceMode::kSampling) {
-      MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+      MutexLock mu(self, *Locks::thread_list_lock_);
       runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
     } else {
       runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
@@ -530,6 +539,9 @@
   bool enable_stats = (the_trace->flags_ && kTraceCountAllocs) != 0;
 
   {
+    gc::ScopedGCCriticalSection gcs(self,
+                                    gc::kGcCauseInstrumentation,
+                                    gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa(__FUNCTION__);
 
     // Reenable.
@@ -1046,4 +1058,9 @@
   return the_trace_->buffer_size_;
 }
 
+bool Trace::IsTracingEnabled() {
+  MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+  return the_trace_ != nullptr;
+}
+
 }  // namespace art
diff --git a/runtime/trace.h b/runtime/trace.h
index 87a691d..356a81f 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -183,6 +183,9 @@
   static TraceMode GetMode() REQUIRES(!Locks::trace_lock_);
   static size_t GetBufferSize() REQUIRES(!Locks::trace_lock_);
 
+  // Used by class linker to prevent class unloading.
+  static bool IsTracingEnabled() REQUIRES(!Locks::trace_lock_);
+
  private:
   Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags,
         TraceOutputMode output_mode, TraceMode trace_mode);
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index dd88db0..ee3a3b9 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -120,7 +120,7 @@
     private static void testRemoveLocalObject() {
         removeLocalObject(new Object());
     }
-
+    
     private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
         short s8, short s9, short s10);
 
diff --git a/test/024-illegal-access/expected.txt b/test/024-illegal-access/expected.txt
index 5f951f4..0ae4a77 100644
--- a/test/024-illegal-access/expected.txt
+++ b/test/024-illegal-access/expected.txt
@@ -1,2 +1,5 @@
 Got expected failure 1
 Got expected failure 2
+Got expected failure 3
+Got expected failure 4
+Got expected failure 5
diff --git a/test/024-illegal-access/src/Main.java b/test/024-illegal-access/src/Main.java
index bde73e9..84c7114 100644
--- a/test/024-illegal-access/src/Main.java
+++ b/test/024-illegal-access/src/Main.java
@@ -17,7 +17,7 @@
 public class Main {
     static public void main(String[] args) {
         try {
-            PublicAccess.main();
+            PublicAccess.accessStaticField();
             System.err.println("ERROR: call 1 not expected to succeed");
         } catch (VerifyError ve) {
             // dalvik
@@ -28,14 +28,41 @@
         }
 
         try {
-            CheckInstanceof.main(new Object());
+            PublicAccess.accessStaticMethod();
             System.err.println("ERROR: call 2 not expected to succeed");
-        } catch (VerifyError ve) {
-            // dalvik
-            System.out.println("Got expected failure 2");
         } catch (IllegalAccessError iae) {
             // reference
             System.out.println("Got expected failure 2");
         }
+
+        try {
+            PublicAccess.accessInstanceField();
+            System.err.println("ERROR: call 3 not expected to succeed");
+        } catch (VerifyError ve) {
+            // dalvik
+            System.out.println("Got expected failure 3");
+        } catch (IllegalAccessError iae) {
+            // reference
+            System.out.println("Got expected failure 3");
+        }
+
+        try {
+            PublicAccess.accessInstanceMethod();
+            System.err.println("ERROR: call 4 not expected to succeed");
+        } catch (IllegalAccessError iae) {
+            // reference
+            System.out.println("Got expected failure 4");
+        }
+
+        try {
+            CheckInstanceof.main(new Object());
+            System.err.println("ERROR: call 5 not expected to succeed");
+        } catch (VerifyError ve) {
+            // dalvik
+            System.out.println("Got expected failure 5");
+        } catch (IllegalAccessError iae) {
+            // reference
+            System.out.println("Got expected failure 5");
+        }
     }
 }
diff --git a/test/024-illegal-access/src/PublicAccess.java b/test/024-illegal-access/src/PublicAccess.java
index 4e72cd4..e3fef85 100644
--- a/test/024-illegal-access/src/PublicAccess.java
+++ b/test/024-illegal-access/src/PublicAccess.java
@@ -18,8 +18,20 @@
  * Some stuff for access checks.
  */
 public class PublicAccess {
-    public static void main() {
-        String shouldFail = SemiPrivate.mPrivvy;
+    public static void accessStaticField() {
+        String shouldFail = SemiPrivate.mStaticPrivvy;
+        System.out.println("Got " + shouldFail);
+    }
+    public static void accessStaticMethod() {
+        String shouldFail = SemiPrivate.privvyStaticMethod();
+        System.out.println("Got " + shouldFail);
+    }
+    public static void accessInstanceField() {
+        String shouldFail = new SemiPrivate().mInstancePrivvy;
+        System.out.println("Got " + shouldFail);
+    }
+    public static void accessInstanceMethod() {
+        String shouldFail = new SemiPrivate().privvyInstanceMethod();
         System.out.println("Got " + shouldFail);
     }
 }
diff --git a/test/024-illegal-access/src/SemiPrivate.java b/test/024-illegal-access/src/SemiPrivate.java
index 06b16c4..62e0d05 100644
--- a/test/024-illegal-access/src/SemiPrivate.java
+++ b/test/024-illegal-access/src/SemiPrivate.java
@@ -18,5 +18,15 @@
  * Version with package scope access.
  */
 public class SemiPrivate {
-    /* not private */ static String mPrivvy = "stuff";
+    /* not private */ static String mStaticPrivvy = "stuff";
+
+    /* not private */ static String privvyStaticMethod() {
+      return "stuff";
+    }
+
+    /* not private */ String mInstancePrivvy = "stuff";
+
+    /* not private */ String privvyInstanceMethod() {
+      return "stuff";
+    }
 }
diff --git a/test/024-illegal-access/src2/SemiPrivate.java b/test/024-illegal-access/src2/SemiPrivate.java
index 064265a..4f36a07 100644
--- a/test/024-illegal-access/src2/SemiPrivate.java
+++ b/test/024-illegal-access/src2/SemiPrivate.java
@@ -18,5 +18,15 @@
  * Version with private access.
  */
 public class SemiPrivate {
-    private static String mPrivvy = "stuff";
+    private static String mStaticPrivvy = "stuff";
+
+    private static String privvyStaticMethod() {
+      return "stuff";
+    }
+
+    private String mInstancePrivvy = "stuff";
+
+    private String privvyInstanceMethod() {
+      return "stuff";
+    }
 }
diff --git a/test/087-gc-after-link/src/Main.java b/test/087-gc-after-link/src/Main.java
index e0a187a..2f6d496 100644
--- a/test/087-gc-after-link/src/Main.java
+++ b/test/087-gc-after-link/src/Main.java
@@ -155,6 +155,12 @@
      * See if we can GC after a failed load.
      */
     static void testFailLoadAndGc() throws TestFailed {
+        processFailLoadAndGc();
+        Runtime.getRuntime().gc();
+        System.out.println("GC complete.");
+    }
+
+    private static void processFailLoadAndGc() throws TestFailed {
         try {
             BrokenDexLoader loader;
 
@@ -170,7 +176,5 @@
                 ite.printStackTrace();
             }
         }
-        Runtime.getRuntime().gc();
-        System.out.println("GC complete.");
     }
 }
diff --git a/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali b/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
index aa0c2d5..0dc492f 100644
--- a/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
+++ b/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
@@ -5,7 +5,7 @@
 .method public static run(Ljava/lang/Object;)V
    .registers 3
 
-   invoke-static {}, LMain;->assertCallerIsInterpreted()V
+   invoke-static {}, LMain;->assertIsInterpreted()V
 
    # Lock twice, but unlock thrice.
 
diff --git a/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali b/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
index 2c31fda..df6e168 100644
--- a/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
+++ b/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
@@ -5,7 +5,7 @@
 .method public static run(Ljava/lang/Object;)V
    .registers 3
 
-   invoke-static {}, LMain;->assertCallerIsInterpreted()V
+   invoke-static {}, LMain;->assertIsInterpreted()V
 
    # Lock thrice, but only unlock twice.
 
diff --git a/test/088-monitor-verification/smali/OK.smali b/test/088-monitor-verification/smali/OK.smali
index 596798d..a43ecb0 100644
--- a/test/088-monitor-verification/smali/OK.smali
+++ b/test/088-monitor-verification/smali/OK.smali
@@ -20,7 +20,7 @@
 .method public static runNoMonitors(Ljava/lang/Object;Ljava/lang/Object;)V
    .registers 3
 
-   invoke-static {}, LMain;->assertCallerIsManaged()V
+   invoke-static {}, LMain;->assertIsManaged()V
 
    return-void
 
@@ -29,7 +29,7 @@
 .method public static runStraightLine(Ljava/lang/Object;Ljava/lang/Object;)V
    .registers 3
 
-   invoke-static {}, LMain;->assertCallerIsManaged()V
+   invoke-static {}, LMain;->assertIsManaged()V
 
    monitor-enter v1      # 1
    monitor-enter v2      # 2
@@ -44,7 +44,7 @@
 .method public static runBalancedJoin(Ljava/lang/Object;Ljava/lang/Object;)V
    .registers 3
 
-   invoke-static {}, LMain;->assertCallerIsManaged()V
+   invoke-static {}, LMain;->assertIsManaged()V
 
    monitor-enter v1      # 1
 
diff --git a/test/088-monitor-verification/smali/TooDeep.smali b/test/088-monitor-verification/smali/TooDeep.smali
index 1a8f2f0..a1e3281 100644
--- a/test/088-monitor-verification/smali/TooDeep.smali
+++ b/test/088-monitor-verification/smali/TooDeep.smali
@@ -7,7 +7,7 @@
 
    # Lock depth is 33, which is more than the verifier supports. This should have been punted to
    # the interpreter.
-   invoke-static {}, LMain;->assertCallerIsInterpreted()V
+   invoke-static {}, LMain;->assertIsInterpreted()V
 
    monitor-enter v2        #  1
    monitor-enter v2        #  2
diff --git a/test/088-monitor-verification/smali/UnbalancedJoin.smali b/test/088-monitor-verification/smali/UnbalancedJoin.smali
index da8f773..993f32c 100644
--- a/test/088-monitor-verification/smali/UnbalancedJoin.smali
+++ b/test/088-monitor-verification/smali/UnbalancedJoin.smali
@@ -5,7 +5,7 @@
 .method public static run(Ljava/lang/Object;Ljava/lang/Object;)V
    .registers 3
 
-   invoke-static {}, LMain;->assertCallerIsInterpreted()V
+   invoke-static {}, LMain;->assertIsInterpreted()V
 
    if-eqz v2, :Lnull
 
diff --git a/test/088-monitor-verification/smali/UnbalancedStraight.smali b/test/088-monitor-verification/smali/UnbalancedStraight.smali
index 68edb6c..cbb8bcc 100644
--- a/test/088-monitor-verification/smali/UnbalancedStraight.smali
+++ b/test/088-monitor-verification/smali/UnbalancedStraight.smali
@@ -5,7 +5,7 @@
 .method public static run(Ljava/lang/Object;Ljava/lang/Object;)V
    .registers 3
 
-   invoke-static {}, LMain;->assertCallerIsInterpreted()V
+   invoke-static {}, LMain;->assertIsInterpreted()V
 
    monitor-enter v1      # 1
    monitor-enter v2      # 2
diff --git a/test/088-monitor-verification/src/Main.java b/test/088-monitor-verification/src/Main.java
index fc5755b..2188055 100644
--- a/test/088-monitor-verification/src/Main.java
+++ b/test/088-monitor-verification/src/Main.java
@@ -220,7 +220,7 @@
 
     // Smali testing code.
     private static void runSmaliTests() {
-        if (!hasOatFile() || runtimeIsSoftFail() || isCallerInterpreted()) {
+        if (!hasOatFile() || runtimeIsSoftFail() || isInterpreted()) {
             // Skip test, this seems to be a non-compiled code test configuration.
             return;
         }
@@ -277,9 +277,9 @@
     }
 
     // Helpers for the smali code.
-    public static native void assertCallerIsInterpreted();
-    public static native void assertCallerIsManaged();
+    public static native void assertIsInterpreted();
+    public static native void assertIsManaged();
     public static native boolean hasOatFile();
     public static native boolean runtimeIsSoftFail();
-    public static native boolean isCallerInterpreted();
+    public static native boolean isInterpreted();
 }
diff --git a/test/449-checker-bce/expected.txt b/test/449-checker-bce/expected.txt
index e114c50..4665d7a 100644
--- a/test/449-checker-bce/expected.txt
+++ b/test/449-checker-bce/expected.txt
@@ -1 +1,2 @@
+JNI_OnLoad called
 java.lang.ArrayIndexOutOfBoundsException: length=5; index=82
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index f06c250..22829cd 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -265,6 +265,7 @@
 
   // A helper into which the actual throwing function should be inlined.
   static void constantIndexingForward6(int[] array) {
+    assertIsManaged();
     constantIndexing6(array);
   }
 
@@ -618,13 +619,17 @@
 
   static int foo() {
     try {
+      assertIsManaged();
       // This will cause AIOOBE.
       constantIndexing2(new int[3]);
     } catch (ArrayIndexOutOfBoundsException e) {
+      assertIsManaged();  // This is to ensure that single-frame deoptimization works.
+                                // Will need to be updated if constantIndexing2 is inlined.
       try {
         // This will cause AIOOBE.
         constantIndexingForward6(new int[3]);
       } catch (ArrayIndexOutOfBoundsException e2) {
+        assertIsManaged();
         return 99;
       }
     }
@@ -634,13 +639,13 @@
 
   int sum;
 
-  /// CHECK-START: void Main.foo1(int[], int, int) BCE (before)
+  /// CHECK-START: void Main.foo1(int[], int, int, boolean) BCE (before)
   /// CHECK: BoundsCheck
   /// CHECK: ArraySet
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArrayGet
 
-  /// CHECK-START: void Main.foo1(int[], int, int) BCE (after)
+  /// CHECK-START: void Main.foo1(int[], int, int, boolean) BCE (after)
   /// CHECK: Phi
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArraySet
@@ -657,25 +662,30 @@
   /// CHECK: Phi
   /// CHECK: Goto
 
-  void foo1(int[] array, int start, int end) {
+  void foo1(int[] array, int start, int end, boolean expectInterpreter) {
     // Three HDeoptimize will be added. One for
     // start >= 0, one for end <= array.length,
     // and one for null check on array (to hoist null
     // check and array.length out of loop).
     for (int i = start ; i < end; i++) {
+      if (expectInterpreter) {
+        assertIsInterpreted();
+      } else {
+        assertIsManaged();
+      }
       array[i] = 1;
       sum += array[i];
     }
   }
 
 
-  /// CHECK-START: void Main.foo2(int[], int, int) BCE (before)
+  /// CHECK-START: void Main.foo2(int[], int, int, boolean) BCE (before)
   /// CHECK: BoundsCheck
   /// CHECK: ArraySet
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArrayGet
 
-  /// CHECK-START: void Main.foo2(int[], int, int) BCE (after)
+  /// CHECK-START: void Main.foo2(int[], int, int, boolean) BCE (after)
   /// CHECK: Phi
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArraySet
@@ -692,25 +702,30 @@
   /// CHECK: Phi
   /// CHECK: Goto
 
-  void foo2(int[] array, int start, int end) {
+  void foo2(int[] array, int start, int end, boolean expectInterpreter) {
     // Three HDeoptimize will be added. One for
     // start >= 0, one for end <= array.length,
     // and one for null check on array (to hoist null
     // check and array.length out of loop).
     for (int i = start ; i <= end; i++) {
+      if (expectInterpreter) {
+        assertIsInterpreted();
+      } else {
+        assertIsManaged();
+      }
       array[i] = 1;
       sum += array[i];
     }
   }
 
 
-  /// CHECK-START: void Main.foo3(int[], int) BCE (before)
+  /// CHECK-START: void Main.foo3(int[], int, boolean) BCE (before)
   /// CHECK: BoundsCheck
   /// CHECK: ArraySet
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArrayGet
 
-  /// CHECK-START: void Main.foo3(int[], int) BCE (after)
+  /// CHECK-START: void Main.foo3(int[], int, boolean) BCE (after)
   /// CHECK: Phi
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArraySet
@@ -726,24 +741,29 @@
   /// CHECK: Phi
   /// CHECK: Goto
 
-  void foo3(int[] array, int end) {
+  void foo3(int[] array, int end, boolean expectInterpreter) {
     // Two HDeoptimize will be added. One for end < array.length,
     // and one for null check on array (to hoist null check
     // and array.length out of loop).
     for (int i = 3 ; i <= end; i++) {
+      if (expectInterpreter) {
+        assertIsInterpreted();
+      } else {
+        assertIsManaged();
+      }
       array[i] = 1;
       sum += array[i];
     }
   }
 
 
-  /// CHECK-START: void Main.foo4(int[], int) BCE (before)
+  /// CHECK-START: void Main.foo4(int[], int, boolean) BCE (before)
   /// CHECK: BoundsCheck
   /// CHECK: ArraySet
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArrayGet
 
-  /// CHECK-START: void Main.foo4(int[], int) BCE (after)
+  /// CHECK-START: void Main.foo4(int[], int, boolean) BCE (after)
   /// CHECK: Phi
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArraySet
@@ -759,18 +779,23 @@
   /// CHECK: Phi
   /// CHECK: Goto
 
-  void foo4(int[] array, int end) {
+  void foo4(int[] array, int end, boolean expectInterpreter) {
     // Two HDeoptimize will be added. One for end <= array.length,
     // and one for null check on array (to hoist null check
     // and array.length out of loop).
     for (int i = end ; i > 0; i--) {
+      if (expectInterpreter) {
+        assertIsInterpreted();
+      } else {
+        assertIsManaged();
+      }
       array[i - 1] = 1;
       sum += array[i - 1];
     }
   }
 
 
-  /// CHECK-START: void Main.foo5(int[], int) BCE (before)
+  /// CHECK-START: void Main.foo5(int[], int, boolean) BCE (before)
   /// CHECK: BoundsCheck
   /// CHECK: ArraySet
   /// CHECK: BoundsCheck
@@ -780,7 +805,7 @@
   /// CHECK: BoundsCheck
   /// CHECK: ArrayGet
 
-  /// CHECK-START: void Main.foo5(int[], int) BCE (after)
+  /// CHECK-START: void Main.foo5(int[], int, boolean) BCE (after)
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArraySet
   /// CHECK: Phi
@@ -800,7 +825,7 @@
   /// CHECK-NOT: Phi
   /// CHECK: Goto
 
-  void foo5(int[] array, int end) {
+  void foo5(int[] array, int end, boolean expectInterpreter) {
     // Bounds check in this loop can be eliminated without deoptimization.
     for (int i = array.length - 1 ; i >= 0; i--) {
       array[i] = 1;
@@ -808,6 +833,11 @@
     // One HDeoptimize will be added.
     // It's for (end - 2 <= array.length - 2).
     for (int i = end - 2 ; i > 0; i--) {
+      if (expectInterpreter) {
+        assertIsInterpreted();
+      } else {
+        assertIsManaged();
+      }
       sum += array[i - 1];
       sum += array[i];
       sum += array[i + 1];
@@ -815,7 +845,7 @@
   }
 
 
-  /// CHECK-START: void Main.foo6(int[], int, int) BCE (before)
+  /// CHECK-START: void Main.foo6(int[], int, int, boolean) BCE (before)
   /// CHECK: BoundsCheck
   /// CHECK: ArrayGet
   /// CHECK: BoundsCheck
@@ -829,7 +859,7 @@
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArraySet
 
-  /// CHECK-START: void Main.foo6(int[], int, int) BCE (after)
+  /// CHECK-START: void Main.foo6(int[], int, int, boolean) BCE (after)
   /// CHECK: Phi
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArrayGet
@@ -855,12 +885,17 @@
   /// CHECK: Goto
   /// CHECK-NOT: Deoptimize
 
-  void foo6(int[] array, int start, int end) {
+  void foo6(int[] array, int start, int end, boolean expectInterpreter) {
     // Three HDeoptimize will be added. One for
     // start >= 2, one for end <= array.length - 3,
     // and one for null check on array (to hoist null
     // check and array.length out of loop).
     for (int i = end; i >= start; i--) {
+      if (expectInterpreter) {
+        assertIsInterpreted();
+      } else {
+        assertIsManaged();
+      }
       array[i] = (array[i-2] + array[i-1] + array[i] + array[i+1] + array[i+2]) / 5;
     }
   }
@@ -948,12 +983,12 @@
   }
 
 
-  /// CHECK-START: void Main.foo9(int[]) BCE (before)
+  /// CHECK-START: void Main.foo9(int[], boolean) BCE (before)
   /// CHECK: NullCheck
   /// CHECK: BoundsCheck
   /// CHECK: ArrayGet
 
-  /// CHECK-START: void Main.foo9(int[]) BCE (after)
+  /// CHECK-START: void Main.foo9(int[], boolean) BCE (after)
   //  The loop is guaranteed to be entered. No need to transform the
   //  loop for loop body entry test.
   /// CHECK: Deoptimize
@@ -964,10 +999,15 @@
   /// CHECK-NOT: BoundsCheck
   /// CHECK: ArrayGet
 
-  void foo9(int[] array) {
+  void foo9(int[] array, boolean expectInterpreter) {
     // Two HDeoptimize will be added. One for
     // 10 <= array.length, and one for null check on array.
     for (int i = 0 ; i < 10; i++) {
+      if (expectInterpreter) {
+        assertIsInterpreted();
+      } else {
+        assertIsManaged();
+      }
       sum += array[i];
     }
   }
@@ -999,7 +1039,7 @@
   static void testUnknownBounds() {
     boolean caught = false;
     Main main = new Main();
-    main.foo1(new int[10], 0, 10);
+    main.foo1(new int[10], 0, 10, false);
     if (main.sum != 10) {
       System.out.println("foo1 failed!");
     }
@@ -1007,7 +1047,7 @@
     caught = false;
     main = new Main();
     try {
-      main.foo1(new int[10], 0, 11);
+      main.foo1(new int[10], 0, 11, true);
     } catch (ArrayIndexOutOfBoundsException e) {
       caught = true;
     }
@@ -1016,7 +1056,7 @@
     }
 
     main = new Main();
-    main.foo2(new int[10], 0, 9);
+    main.foo2(new int[10], 0, 9, false);
     if (main.sum != 10) {
       System.out.println("foo2 failed!");
     }
@@ -1024,7 +1064,7 @@
     caught = false;
     main = new Main();
     try {
-      main.foo2(new int[10], 0, 10);
+      main.foo2(new int[10], 0, 10, true);
     } catch (ArrayIndexOutOfBoundsException e) {
       caught = true;
     }
@@ -1033,7 +1073,7 @@
     }
 
     main = new Main();
-    main.foo3(new int[10], 9);
+    main.foo3(new int[10], 9, false);
     if (main.sum != 7) {
       System.out.println("foo3 failed!");
     }
@@ -1041,7 +1081,7 @@
     caught = false;
     main = new Main();
     try {
-      main.foo3(new int[10], 10);
+      main.foo3(new int[10], 10, true);
     } catch (ArrayIndexOutOfBoundsException e) {
       caught = true;
     }
@@ -1050,7 +1090,7 @@
     }
 
     main = new Main();
-    main.foo4(new int[10], 10);
+    main.foo4(new int[10], 10, false);
     if (main.sum != 10) {
       System.out.println("foo4 failed!");
     }
@@ -1058,7 +1098,7 @@
     caught = false;
     main = new Main();
     try {
-      main.foo4(new int[10], 11);
+      main.foo4(new int[10], 11, true);
     } catch (ArrayIndexOutOfBoundsException e) {
       caught = true;
     }
@@ -1067,7 +1107,7 @@
     }
 
     main = new Main();
-    main.foo5(new int[10], 10);
+    main.foo5(new int[10], 10, false);
     if (main.sum != 24) {
       System.out.println("foo5 failed!");
     }
@@ -1075,7 +1115,7 @@
     caught = false;
     main = new Main();
     try {
-      main.foo5(new int[10], 11);
+      main.foo5(new int[10], 11, true);
     } catch (ArrayIndexOutOfBoundsException e) {
       caught = true;
     }
@@ -1084,11 +1124,11 @@
     }
 
     main = new Main();
-    main.foo6(new int[10], 2, 7);
+    main.foo6(new int[10], 2, 7, false);
 
     main = new Main();
     int[] array9 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-    main.foo9(array9);
+    main.foo9(array9, false);
     if (main.sum != 45) {
       System.out.println("foo9 failed!");
     }
@@ -1104,7 +1144,7 @@
     caught = false;
     main = new Main();
     try {
-      main.foo6(new int[10], 2, 8);
+      main.foo6(new int[10], 2, 8, true);
     } catch (ArrayIndexOutOfBoundsException e) {
       caught = true;
     }
@@ -1115,7 +1155,7 @@
     caught = false;
     main = new Main();
     try {
-      main.foo6(new int[10], 1, 7);
+      main.foo6(new int[10], 1, 7, true);
     } catch (ArrayIndexOutOfBoundsException e) {
       caught = true;
     }
@@ -1152,6 +1192,15 @@
   /// CHECK: ParallelMove
 
   public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+
+    if (!compiledWithOptimizing() ||
+        !hasOatFile() ||
+        runtimeIsSoftFail() ||
+        isInterpreted()) {
+      disableStackFrameAsserts();
+    }
+
     sieve(20);
 
     int[] array = {5, 2, 3, 7, 0, 1, 6, 4};
@@ -1190,4 +1239,11 @@
     new Main().testExceptionMessage();
   }
 
+  public static native boolean compiledWithOptimizing();
+  public static native void disableStackFrameAsserts();
+  public static native void assertIsManaged();
+  public static native void assertIsInterpreted();
+  public static native boolean hasOatFile();
+  public static native boolean runtimeIsSoftFail();
+  public static native boolean isInterpreted();
 }
diff --git a/test/529-checker-unresolved/expected.txt b/test/529-checker-unresolved/expected.txt
index 358048c..1e7dbfe 100644
--- a/test/529-checker-unresolved/expected.txt
+++ b/test/529-checker-unresolved/expected.txt
@@ -3,3 +3,5 @@
 UnresolvedClass.virtualMethod()
 UnresolvedClass.interfaceMethod()
 UnresolvedClass.superMethod()
+instanceof ok
+checkcast ok
diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java
index 6f04797..5219c04 100644
--- a/test/529-checker-unresolved/src/Main.java
+++ b/test/529-checker-unresolved/src/Main.java
@@ -44,13 +44,141 @@
     super.superMethod();
   }
 
+  /// CHECK-START: void Main.callUnresolvedStaticFieldAccess() register (before)
+  /// CHECK:        UnresolvedStaticFieldSet field_type:PrimByte
+  /// CHECK:        UnresolvedStaticFieldSet field_type:PrimChar
+  /// CHECK:        UnresolvedStaticFieldSet field_type:PrimInt
+  /// CHECK:        UnresolvedStaticFieldSet field_type:PrimLong
+  /// CHECK:        UnresolvedStaticFieldSet field_type:PrimFloat
+  /// CHECK:        UnresolvedStaticFieldSet field_type:PrimDouble
+  /// CHECK:        UnresolvedStaticFieldSet field_type:PrimNot
+
+  /// CHECK:        UnresolvedStaticFieldGet field_type:PrimByte
+  /// CHECK:        UnresolvedStaticFieldGet field_type:PrimChar
+  /// CHECK:        UnresolvedStaticFieldGet field_type:PrimInt
+  /// CHECK:        UnresolvedStaticFieldGet field_type:PrimLong
+  /// CHECK:        UnresolvedStaticFieldGet field_type:PrimFloat
+  /// CHECK:        UnresolvedStaticFieldGet field_type:PrimDouble
+  /// CHECK:        UnresolvedStaticFieldGet field_type:PrimNot
+  static public void callUnresolvedStaticFieldAccess() {
+    Object o = new Object();
+    UnresolvedClass.staticByte = (byte)1;
+    UnresolvedClass.staticChar = '1';
+    UnresolvedClass.staticInt = 123456789;
+    UnresolvedClass.staticLong = 123456789123456789l;
+    UnresolvedClass.staticFloat = 123456789123456789f;
+    UnresolvedClass.staticDouble = 123456789123456789d;
+    UnresolvedClass.staticObject = o;
+
+    expectEquals((byte)1, UnresolvedClass.staticByte);
+    expectEquals('1', UnresolvedClass.staticChar);
+    expectEquals(123456789, UnresolvedClass.staticInt);
+    expectEquals(123456789123456789l, UnresolvedClass.staticLong);
+    expectEquals(123456789123456789f, UnresolvedClass.staticFloat);
+    expectEquals(123456789123456789d, UnresolvedClass.staticDouble);
+    expectEquals(o, UnresolvedClass.staticObject);
+  }
+
+  /// CHECK-START: void Main.callUnresolvedInstanceFieldAccess(UnresolvedClass) register (before)
+  /// CHECK:        UnresolvedInstanceFieldSet field_type:PrimByte
+  /// CHECK:        UnresolvedInstanceFieldSet field_type:PrimChar
+  /// CHECK:        UnresolvedInstanceFieldSet field_type:PrimInt
+  /// CHECK:        UnresolvedInstanceFieldSet field_type:PrimLong
+  /// CHECK:        UnresolvedInstanceFieldSet field_type:PrimFloat
+  /// CHECK:        UnresolvedInstanceFieldSet field_type:PrimDouble
+  /// CHECK:        UnresolvedInstanceFieldSet field_type:PrimNot
+
+  /// CHECK:        UnresolvedInstanceFieldGet field_type:PrimByte
+  /// CHECK:        UnresolvedInstanceFieldGet field_type:PrimChar
+  /// CHECK:        UnresolvedInstanceFieldGet field_type:PrimInt
+  /// CHECK:        UnresolvedInstanceFieldGet field_type:PrimLong
+  /// CHECK:        UnresolvedInstanceFieldGet field_type:PrimFloat
+  /// CHECK:        UnresolvedInstanceFieldGet field_type:PrimDouble
+  /// CHECK:        UnresolvedInstanceFieldGet field_type:PrimNot
+  static public void callUnresolvedInstanceFieldAccess(UnresolvedClass c) {
+    Object o = new Object();
+    c.instanceByte = (byte)1;
+    c.instanceChar = '1';
+    c.instanceInt = 123456789;
+    c.instanceLong = 123456789123456789l;
+    c.instanceFloat = 123456789123456789f;
+    c.instanceDouble = 123456789123456789d;
+    c.instanceObject = o;
+
+    expectEquals((byte)1, c.instanceByte);
+    expectEquals('1', c.instanceChar);
+    expectEquals(123456789, c.instanceInt);
+    expectEquals(123456789123456789l, c.instanceLong);
+    expectEquals(123456789123456789f, c.instanceFloat);
+    expectEquals(123456789123456789d, c.instanceDouble);
+    expectEquals(o, c.instanceObject);
+  }
+
+  static public void testInstanceOf(Object o) {
+    if (o instanceof UnresolvedSuperClass) {
+      System.out.println("instanceof ok");
+    }
+  }
+
+  static public UnresolvedSuperClass testCheckCast(Object o) {
+    UnresolvedSuperClass c = (UnresolvedSuperClass) o;
+    System.out.println("checkcast ok");
+    return c;
+  }
   /// CHECK-START: void Main.main(java.lang.String[]) register (before)
   /// CHECK:        InvokeUnresolved invoke_type:direct
   static public void main(String[] args) {
     UnresolvedClass c = new UnresolvedClass();
+    Main m = new Main();
     callInvokeUnresolvedStatic();
     callInvokeUnresolvedVirtual(c);
     callInvokeUnresolvedInterface(c);
-    callInvokeUnresolvedSuper(new Main());
+    callInvokeUnresolvedSuper(m);
+    callUnresolvedStaticFieldAccess();
+    callUnresolvedInstanceFieldAccess(c);
+    testInstanceOf(m);
+    testCheckCast(m);
+  }
+
+  public static void expectEquals(byte expected, byte result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(char expected, char result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+    public static void expectEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(double expected, double result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(Object expected, Object result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
   }
 }
diff --git a/test/529-checker-unresolved/src/Unresolved.java b/test/529-checker-unresolved/src/Unresolved.java
index 5bf92dd..20ac6e0 100644
--- a/test/529-checker-unresolved/src/Unresolved.java
+++ b/test/529-checker-unresolved/src/Unresolved.java
@@ -40,15 +40,21 @@
   public void interfaceMethod() {
     System.out.println("UnresolvedClass.interfaceMethod()");
   }
-}
 
-final class UnresolvedFinalClass {
-  public void directMethod() {
-    System.out.println("UnresolvedFinalClass.directMethod()");
-  }
-}
+  public static byte staticByte;
+  public static char staticChar;
+  public static int staticInt;
+  public static long staticLong;
+  public static float staticFloat;
+  public static double staticDouble;
+  public static Object staticObject;
 
-class UnresolvedAtRuntime {
-  public void unresolvedAtRuntime() { }
+  public byte instanceByte;
+  public char instanceChar;
+  public int instanceInt;
+  public long instanceLong;
+  public float instanceFloat;
+  public double instanceDouble;
+  public Object instanceObject;
 }
 
diff --git a/test/535-deopt-and-inlining/expected.txt b/test/535-deopt-and-inlining/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/535-deopt-and-inlining/expected.txt
diff --git a/test/535-deopt-and-inlining/info.txt b/test/535-deopt-and-inlining/info.txt
new file mode 100644
index 0000000..717612a
--- /dev/null
+++ b/test/535-deopt-and-inlining/info.txt
@@ -0,0 +1,2 @@
+Stress test for deoptimization and JIT, to ensure the
+stack visitor uses the right ArtMethod when deopting.
diff --git a/test/535-deopt-and-inlining/src/Main.java b/test/535-deopt-and-inlining/src/Main.java
new file mode 100644
index 0000000..c231bf0
--- /dev/null
+++ b/test/535-deopt-and-inlining/src/Main.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  public static void run() {
+    // Loop enough to get JIT compilation.
+    for (int i = 0; i < 10000; ++i) {
+      doCall(new int[0]);
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    run();
+  }
+
+  public static void doCall(int[] array) {
+    try {
+      deopt(array);
+    } catch (IndexOutOfBoundsException ioobe) {
+      // Expected
+    }
+  }
+
+  public static void deopt(int[] array) {
+    // Invoke `deopt` much more than `$inline$deopt` so that only `deopt` gets
+    // initially JITted.
+    if (call == 100) {
+      call = 0;
+      $inline$deopt(array);
+    } else {
+      call++;
+    }
+  }
+
+  public static void $inline$deopt(int[] array) {
+    array[0] = 1;
+    array[1] = 1;
+  }
+
+  static int call = 0;
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 49778cb..db16b97 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -369,6 +369,7 @@
 # This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
 # when already tracing, and writes an error message that we do not want to check for.
 TEST_ART_BROKEN_TRACING_RUN_TESTS := \
+  087-gc-after-link \
   137-cfi \
   141-class-unload \
   802-deoptimization
@@ -490,14 +491,6 @@
       $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
 endif
 
-# If ART_USE_OPTIMIZING_COMPILER is set to true, then the default core.art has been
-# compiled with the optimizing compiler.
-ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
-  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
-      default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
-      $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
-endif
-
 TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
 
 # Tests that should fail when the optimizing compiler compiles them non-debuggable.
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 042b03b..082c9b3 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -66,4 +66,54 @@
   return Runtime::Current()->IsImageDex2OatEnabled();
 }
 
+// public static native boolean compiledWithOptimizing();
+// Did we use the optimizing compiler to compile this?
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_compiledWithOptimizing(JNIEnv* env, jclass cls) {
+  ScopedObjectAccess soa(env);
+
+  mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+  const DexFile& dex_file = klass->GetDexFile();
+  const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
+  if (oat_dex_file == nullptr) {
+    // Could be JIT, which also uses optimizing, but conservatively say no.
+    return JNI_FALSE;
+  }
+  const OatFile* oat_file = oat_dex_file->GetOatFile();
+  CHECK(oat_file != nullptr);
+
+  const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+  CHECK(cmd_line != nullptr);  // Huh? This should not happen.
+
+  // Check the backend.
+  constexpr const char* kCompilerBackend = "--compiler-backend=";
+  const char* backend = strstr(cmd_line, kCompilerBackend);
+  if (backend != nullptr) {
+    // If it's set, make sure it's optimizing.
+    backend += strlen(kCompilerBackend);
+    if (strncmp(backend, "Optimizing", strlen("Optimizing")) != 0) {
+      return JNI_FALSE;
+    }
+  }
+
+  // Check the filter.
+  constexpr const char* kCompilerFilter = "--compiler-filter=";
+  const char* filter = strstr(cmd_line, kCompilerFilter);
+  if (filter != nullptr) {
+    // If it's set, make sure it's not interpret-only|verify-none|verify-at-runtime.
+    // Note: The space filter might have an impact on the test, but ignore that for now.
+    filter += strlen(kCompilerFilter);
+    constexpr const char* kInterpretOnly = "interpret-only";
+    constexpr const char* kVerifyNone = "verify-none";
+    constexpr const char* kVerifyAtRuntime = "verify-at-runtime";
+    if (strncmp(filter, kInterpretOnly, strlen(kInterpretOnly)) == 0 ||
+        strncmp(filter, kVerifyNone, strlen(kVerifyNone)) == 0 ||
+        strncmp(filter, kVerifyAtRuntime, strlen(kVerifyAtRuntime)) == 0) {
+      return JNI_FALSE;
+    }
+  }
+
+  return JNI_TRUE;
+}
+
 }  // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index d22cf52..922eae6 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -27,9 +27,20 @@
 
 namespace art {
 
-// public static native boolean isCallerInterpreted();
+static bool asserts_enabled = true;
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerInterpreted(JNIEnv* env, jclass) {
+// public static native void disableStackFrameAsserts();
+// Note: to globally disable asserts in unsupported configurations.
+
+extern "C" JNIEXPORT void JNICALL Java_Main_disableStackFrameAsserts(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                                     jclass cls ATTRIBUTE_UNUSED) {
+  asserts_enabled = false;
+}
+
+
+// public static native boolean isInterpreted();
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpreted(JNIEnv* env, jclass) {
   ScopedObjectAccess soa(env);
   NthCallerVisitor caller(soa.Self(), 1, false);
   caller.WalkStack();
@@ -37,16 +48,18 @@
   return caller.GetCurrentShadowFrame() != nullptr ? JNI_TRUE : JNI_FALSE;
 }
 
-// public static native void assertCallerIsInterpreted();
+// public static native void assertIsInterpreted();
 
-extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsInterpreted(JNIEnv* env, jclass klass) {
-  CHECK(Java_Main_isCallerInterpreted(env, klass));
+extern "C" JNIEXPORT void JNICALL Java_Main_assertIsInterpreted(JNIEnv* env, jclass klass) {
+  if (asserts_enabled) {
+    CHECK(Java_Main_isInterpreted(env, klass));
+  }
 }
 
 
-// public static native boolean isCallerManaged();
+// public static native boolean isManaged();
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerManaged(JNIEnv* env, jclass cls) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isManaged(JNIEnv* env, jclass cls) {
   ScopedObjectAccess soa(env);
 
   mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
@@ -65,10 +78,12 @@
   return caller.GetCurrentShadowFrame() != nullptr ? JNI_FALSE : JNI_TRUE;
 }
 
-// public static native void assertCallerIsManaged();
+// public static native void assertIsManaged();
 
-extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsManaged(JNIEnv* env, jclass cls) {
-  CHECK(Java_Main_isCallerManaged(env, cls));
+extern "C" JNIEXPORT void JNICALL Java_Main_assertIsManaged(JNIEnv* env, jclass cls) {
+  if (asserts_enabled) {
+    CHECK(Java_Main_isManaged(env, cls));
+  }
 }
 
 }  // namespace art
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 3c1522c..71366c1 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -16,6 +16,8 @@
 
 LOCAL_PATH := $(call my-dir)
 
+include art/build/Android.common_test.mk
+
 # --- ahat.jar ----------------
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -44,7 +46,7 @@
 
 ahat: $(LOCAL_BUILT_MODULE)
 
-# --- ahat-test.jar --------------
+# --- ahat-tests.jar --------------
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-java-files-under, test)
 LOCAL_JAR_MANIFEST := test/manifest.txt
@@ -53,6 +55,42 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE := ahat-tests
 include $(BUILD_HOST_JAVA_LIBRARY)
+AHAT_TEST_JAR := $(LOCAL_BUILT_MODULE)
 
-ahat-test: $(LOCAL_BUILT_MODULE)
-	java -jar $<
+# --- ahat-test-dump.jar --------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := ahat-test-dump
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, test-dump)
+include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+
+# Determine the location of the test-dump.jar and test-dump.hprof files.
+# These use variables set implicitly by the include of
+# BUILD_HOST_DALVIK_JAVA_LIBRARY above.
+AHAT_TEST_DUMP_JAR := $(LOCAL_BUILT_MODULE)
+AHAT_TEST_DUMP_HPROF := $(intermediates.COMMON)/test-dump.hprof
+
+# Run ahat-test-dump.jar to generate test-dump.hprof
+AHAT_TEST_DUMP_DEPENDENCIES := \
+	$(ART_HOST_EXECUTABLES) \
+	$(HOST_OUT_EXECUTABLES)/art \
+	$(HOST_CORE_IMG_OUT_BASE)$(CORE_IMG_SUFFIX)
+
+$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_ART := $(HOST_OUT_EXECUTABLES)/art
+$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_JAR := $(AHAT_TEST_DUMP_JAR)
+$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_DEPENDENCIES := $(AHAT_TEST_DUMP_DEPENDENCIES)
+$(AHAT_TEST_DUMP_HPROF): $(AHAT_TEST_DUMP_JAR) $(AHAT_TEST_DUMP_DEPENDENCIES)
+	$(PRIVATE_AHAT_TEST_ART) -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@
+
+.PHONY: ahat-test
+ahat-test: PRIVATE_AHAT_TEST_DUMP_HPROF := $(AHAT_TEST_DUMP_HPROF)
+ahat-test: PRIVATE_AHAT_TEST_JAR := $(AHAT_TEST_JAR)
+ahat-test: $(AHAT_TEST_JAR) $(AHAT_TEST_DUMP_HPROF)
+	java -Dahat.test.dump.hprof=$(PRIVATE_AHAT_TEST_DUMP_HPROF) -jar $(PRIVATE_AHAT_TEST_JAR)
+
+# Clean up local variables.
+AHAT_TEST_DUMP_DEPENDENCIES :=
+AHAT_TEST_DUMP_HPROF :=
+AHAT_TEST_DUMP_JAR :=
+AHAT_TEST_JAR :=
+
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
index 2437d03..3035ef7 100644
--- a/tools/ahat/src/AhatSnapshot.java
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -18,13 +18,18 @@
 
 import com.android.tools.perflib.heap.ClassObj;
 import com.android.tools.perflib.heap.Heap;
+import com.android.tools.perflib.heap.HprofParser;
 import com.android.tools.perflib.heap.Instance;
 import com.android.tools.perflib.heap.RootObj;
 import com.android.tools.perflib.heap.Snapshot;
 import com.android.tools.perflib.heap.StackFrame;
 import com.android.tools.perflib.heap.StackTrace;
+import com.android.tools.perflib.heap.io.HprofBuffer;
+import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -47,7 +52,22 @@
   private Site mRootSite;
   private Map<Heap, Long> mHeapSizes;
 
-  public AhatSnapshot(Snapshot snapshot) {
+  /**
+   * Create an AhatSnapshot from an hprof file.
+   */
+  public static AhatSnapshot fromHprof(File hprof) throws IOException {
+    HprofBuffer buffer = new MemoryMappedFileBuffer(hprof);
+    Snapshot snapshot = (new HprofParser(buffer)).parse();
+    snapshot.computeDominators();
+    return new AhatSnapshot(snapshot);
+  }
+
+  /**
+   * Construct an AhatSnapshot for the given perflib snapshot.
+   * Ther user is responsible for calling snapshot.computeDominators before
+   * calling this AhatSnapshot constructor.
+   */
+  private AhatSnapshot(Snapshot snapshot) {
     mSnapshot = snapshot;
     mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
     mDominated = new HashMap<Instance, List<Instance>>();
@@ -92,6 +112,11 @@
     }
   }
 
+  // Note: This method is exposed for testing purposes.
+  public ClassObj findClass(String name) {
+    return mSnapshot.findClass(name);
+  }
+
   public Instance findInstance(long id) {
     return mSnapshot.findInstance(id);
   }
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index 7ee3ff2..a6ac3b8 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -32,7 +32,7 @@
    * given name.
    */
   public static boolean isInstanceOfClass(Instance inst, String className) {
-    ClassObj cls = inst.getClassObj();
+    ClassObj cls = (inst == null) ? null : inst.getClassObj();
     return (cls != null && className.equals(cls.getClassName()));
   }
 
@@ -132,7 +132,7 @@
    * Read a field of an instance.
    * Returns null if the field value is null or if the field couldn't be read.
    */
-  private static Object getField(Instance inst, String fieldName) {
+  public static Object getField(Instance inst, String fieldName) {
     if (!(inst instanceof ClassInstance)) {
       return null;
     }
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index 2e2ddd2..1563aa0 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -16,10 +16,6 @@
 
 package com.android.ahat;
 
-import com.android.tools.perflib.heap.HprofParser;
-import com.android.tools.perflib.heap.Snapshot;
-import com.android.tools.perflib.heap.io.HprofBuffer;
-import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer;
 import com.sun.net.httpserver.HttpServer;
 import java.io.File;
 import java.io.IOException;
@@ -71,15 +67,8 @@
       return;
     }
 
-    System.out.println("Reading hprof file...");
-    HprofBuffer buffer = new MemoryMappedFileBuffer(hprof);
-    Snapshot snapshot = (new HprofParser(buffer)).parse();
-
-    System.out.println("Computing Dominators...");
-    snapshot.computeDominators();
-
-    System.out.println("Processing snapshot for ahat...");
-    AhatSnapshot ahat = new AhatSnapshot(snapshot);
+    System.out.println("Processing hprof file...");
+    AhatSnapshot ahat = AhatSnapshot.fromHprof(hprof);
 
     InetAddress loopback = InetAddress.getLoopbackAddress();
     InetSocketAddress addr = new InetSocketAddress(loopback, port);
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
new file mode 100644
index 0000000..cea1dc1
--- /dev/null
+++ b/tools/ahat/test-dump/Main.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.VMDebug;
+import java.io.IOException;
+
+/**
+ * Program used to create a heap dump for test purposes.
+ */
+public class Main {
+  // Keep a reference to the DumpedStuff instance so that it is not garbage
+  // collected before we take the heap dump.
+  public static DumpedStuff stuff;
+
+  // We will take a heap dump that includes a single instance of this
+  // DumpedStuff class. Objects stored as fields in this class can be easily
+  // found in the hprof dump by searching for the instance of the DumpedStuff
+  // class and reading the desired field.
+  public static class DumpedStuff {
+    public String basicString = "hello, world";
+    public String nullString = null;
+    public Object anObject = new Object();
+  }
+
+  public static void main(String[] args) throws IOException {
+    if (args.length < 1) {
+      System.err.println("no output file specified");
+      return;
+    }
+    String file = args[0];
+
+    // Allocate the instance of DumpedStuff.
+    stuff = new DumpedStuff();
+
+    // Take a heap dump that will include that instance of DumpedStuff.
+    System.err.println("Dumping hprof data to " + file);
+    VMDebug.dumpHprofData(file);
+  }
+}
diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java
new file mode 100644
index 0000000..7613df4
--- /dev/null
+++ b/tools/ahat/test/InstanceUtilsTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ahat;
+
+import com.android.tools.perflib.heap.Instance;
+import java.io.IOException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+
+public class InstanceUtilsTest {
+  @Test
+  public void basicString() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+    Instance str = (Instance)dump.getDumpedThing("basicString");
+    assertEquals("hello, world", InstanceUtils.asString(str));
+  }
+
+  @Test
+  public void nullString() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+    Instance obj = (Instance)dump.getDumpedThing("nullString");
+    assertNull(InstanceUtils.asString(obj));
+  }
+
+  @Test
+  public void notString() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+    Instance obj = (Instance)dump.getDumpedThing("anObject");
+    assertNotNull(obj);
+    assertNull(InstanceUtils.asString(obj));
+  }
+}
diff --git a/tools/ahat/test/TestDump.java b/tools/ahat/test/TestDump.java
new file mode 100644
index 0000000..c3a76e4
--- /dev/null
+++ b/tools/ahat/test/TestDump.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ahat;
+
+import com.android.tools.perflib.heap.ClassObj;
+import com.android.tools.perflib.heap.Field;
+import com.android.tools.perflib.heap.Instance;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * The TestDump class is used to get an AhatSnapshot for the test-dump
+ * program.
+ */
+public class TestDump {
+  // It can take on the order of a second to parse and process the test-dump
+  // hprof. To avoid repeating this overhead for each test case, we cache the
+  // loaded instance of TestDump and reuse it when possible. In theory the
+  // test cases should not be able to modify the cached snapshot in a way that
+  // is visible to other test cases.
+  private static TestDump mCachedTestDump = null;
+
+  private AhatSnapshot mSnapshot = null;
+
+  /**
+   * Load the test-dump.hprof file.
+   * The location of the file is read from the system property
+   * "ahat.test.dump.hprof", which is expected to be set on the command line.
+   * For example:
+   *   java -Dahat.test.dump.hprof=test-dump.hprof -jar ahat-tests.jar
+   *
+   * An IOException is thrown if there is a failure reading the hprof file.
+   */
+  private TestDump() throws IOException {
+      String hprof = System.getProperty("ahat.test.dump.hprof");
+      mSnapshot = AhatSnapshot.fromHprof(new File(hprof));
+  }
+
+  /**
+   * Get the AhatSnapshot for the test dump program.
+   */
+  public AhatSnapshot getAhatSnapshot() {
+    return mSnapshot;
+  }
+
+  /**
+   * Return the value of a field in the DumpedStuff instance in the
+   * snapshot for the test-dump program.
+   */
+  public Object getDumpedThing(String name) {
+    ClassObj main = mSnapshot.findClass("Main");
+    Instance stuff = null;
+    for (Map.Entry<Field, Object> fields : main.getStaticFieldValues().entrySet()) {
+      if ("stuff".equals(fields.getKey().getName())) {
+        stuff = (Instance) fields.getValue();
+      }
+    }
+    return InstanceUtils.getField(stuff, name);
+  }
+
+  /**
+   * Get the test dump.
+   * An IOException is thrown if there is an error reading the test dump hprof
+   * file.
+   * To improve performance, this returns a cached instance of the TestDump
+   * when possible.
+   */
+  public static synchronized TestDump getTestDump() throws IOException {
+    if (mCachedTestDump == null) {
+      mCachedTestDump = new TestDump();
+    }
+    return mCachedTestDump;
+  }
+}
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java
index fb53d90..bab7121 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/test/Tests.java
@@ -22,6 +22,7 @@
   public static void main(String[] args) {
     if (args.length == 0) {
       args = new String[]{
+        "com.android.ahat.InstanceUtilsTest",
         "com.android.ahat.QueryTest",
         "com.android.ahat.SortTest"
       };
diff --git a/tools/art b/tools/art
index 676d6ae..304a9d0 100644
--- a/tools/art
+++ b/tools/art
@@ -89,6 +89,7 @@
   invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
 fi
 
+# We use the PIC core image to work with perf.
 ANDROID_DATA=$ANDROID_DATA \
   ANDROID_ROOT=$ANDROID_ROOT \
   LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
@@ -97,7 +98,7 @@
   $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \
     -XXlib:$LIBART \
     -Xnorelocate \
-    -Ximage:$ANDROID_ROOT/framework/core.art \
+    -Ximage:$ANDROID_ROOT/framework/core-optimizing-pic.art \
     -Xcompiler-option --generate-debug-info \
     "$@"