Merge "Remove the unused SMP instruction set feature option."
diff --git a/Android.mk b/Android.mk
index cf3a9e7..f3ab3c1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -341,56 +341,6 @@
 
 endif  # art_test_bother
 
-########################################################################
-# oat-target and oat-target-sync rules
-
-OAT_TARGET_RULES :=
-
-# $(1): input jar or apk target location
-define declare-oat-target-target
-OUT_OAT_FILE := $(PRODUCT_OUT)/$(basename $(1)).odex
-
-ifeq ($(ONE_SHOT_MAKEFILE),)
-# ONE_SHOT_MAKEFILE is empty for a top level build and we don't want
-# to define the oat-target-* rules there because they will conflict
-# with the build/core/dex_preopt.mk defined rules.
-.PHONY: oat-target-$(1)
-oat-target-$(1):
-
-else
-.PHONY: oat-target-$(1)
-oat-target-$(1): $$(OUT_OAT_FILE)
-
-$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OAT_DEPENDENCY)
-	@mkdir -p $$(dir $$@)
-	$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
-		--boot-image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --dex-file=$(PRODUCT_OUT)/$(1) \
-		--dex-location=/$(1) --oat-file=$$@ \
-		--instruction-set=$(DEX2OAT_TARGET_ARCH) \
-		--instruction-set-variant=$(DEX2OAT_TARGET_CPU_VARIANT) \
-		--instruction-set-features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
-		--android-root=$(PRODUCT_OUT)/system --include-patch-information \
-		--runtime-arg -Xnorelocate
-
-endif
-
-OAT_TARGET_RULES += oat-target-$(1)
-endef
-
-$(foreach file,\
-  $(filter-out\
-    $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(LIBART_TARGET_BOOT_JARS))),\
-    $(wildcard $(TARGET_OUT_APPS)/*.apk) $(wildcard $(TARGET_OUT_JAVA_LIBRARIES)/*.jar)),\
-  $(eval $(call declare-oat-target-target,$(subst $(PRODUCT_OUT)/,,$(file)))))
-
-.PHONY: oat-target
-oat-target: $(ART_TARGET_DEPENDENCIES) $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) $(OAT_TARGET_RULES)
-
-.PHONY: oat-target-sync
-oat-target-sync: oat-target
-	$(TEST_ART_ADB_ROOT_AND_REMOUNT)
-	adb sync
-
 ####################################################################################################
 # Fake packages to ensure generation of libopenjdkd when one builds with mm/mmm/mmma.
 #
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index bd7f900..5bdfbc7 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -332,7 +332,7 @@
 	  (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(4) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
 	    valgrind --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
 	    --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
-	    --num-callers=50 \
+	    --num-callers=50 --show-mismatched-frees=no \
 	    $$(PRIVATE_TARGET_EXE) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
 	  && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
 	      && $$(call ART_TEST_PASSED,$$@)) \
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 86d92ff..4180e0e 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -487,7 +487,7 @@
   EXPECT_EQ(72U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(20U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+  EXPECT_EQ(163 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
             sizeof(QuickEntryPoints));
 }
 
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 8a7f6d3..1dd526f 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3936,7 +3936,6 @@
   } else {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   }
   locations->SetOut(Location::RegisterLocation(R0));
 }
@@ -3954,7 +3953,7 @@
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
   }
 }
 
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5c33fe1..240e39d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4738,7 +4738,6 @@
     locations->AddTemp(LocationFrom(kArtMethodRegister));
   } else {
     locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
-    locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
   }
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
 }
@@ -4756,7 +4755,7 @@
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
   }
 }
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 00ad3e3..cf4d94d 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3948,7 +3948,6 @@
   } else {
     InvokeRuntimeCallingConventionARMVIXL calling_convention;
     locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
-    locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
   }
   locations->SetOut(LocationFrom(r0));
 }
@@ -3970,7 +3969,7 @@
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
   }
 }
 
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 01e0dac..29f8b2a 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5900,7 +5900,6 @@
     locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument));
   } else {
     locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   }
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
 }
@@ -5917,7 +5916,7 @@
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
   }
 }
 
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 36690c0..dd3f0fe 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3841,7 +3841,6 @@
     locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument));
   } else {
     locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   }
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
 }
@@ -3859,7 +3858,7 @@
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
   }
 }
 
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0abe855..786bc50 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4150,7 +4150,6 @@
   } else {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   }
 }
 
@@ -4166,7 +4165,7 @@
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
     DCHECK(!codegen_->IsLeafMethod());
   }
 }
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 903844f..06b48c4 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4038,7 +4038,6 @@
     locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument));
   } else {
     locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   }
   locations->SetOut(Location::RegisterLocation(RAX));
 }
@@ -4055,7 +4054,7 @@
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
     DCHECK(!codegen_->IsLeafMethod());
   }
 }
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 879b4ce..e3f3df0 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include <functional>
+#include <memory>
 
 #include "arch/instruction_set.h"
 #include "arch/arm/instruction_set_features_arm.h"
@@ -299,8 +300,8 @@
                     bool has_result,
                     Expected expected) {
   CompilerOptions compiler_options;
-  CodeGenerator* codegen = target_config.CreateCodeGenerator(graph, compiler_options);
-  RunCode(codegen, graph, hook_before_codegen, has_result, expected);
+  std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph, compiler_options));
+  RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected);
 }
 
 #ifdef ART_ENABLE_CODEGEN_arm
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 437d35c..f8d37bd 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -28,7 +28,6 @@
 TEST_F(GVNTest, LocalFieldElimination) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   HGraph* graph = CreateGraph(&allocator);
   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -45,53 +44,53 @@
   entry->AddSuccessor(block);
 
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   HInstruction* to_remove = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(43),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   HInstruction* different_offset = block->GetLastInstruction();
   // Kill the value.
   block->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
                                                            parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimNot,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   HInstruction* use_after_kill = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HExit());
@@ -113,7 +112,6 @@
 TEST_F(GVNTest, GlobalFieldElimination) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   HGraph* graph = CreateGraph(&allocator);
   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -129,13 +127,13 @@
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimBoolean,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
 
   block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
@@ -152,33 +150,33 @@
   else_->AddSuccessor(join);
 
   then->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                          nullptr,
                                                           Primitive::kPrimBoolean,
                                                           MemberOffset(42),
                                                           false,
                                                           kUnknownFieldIndex,
                                                           kUnknownClassDefIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache,
                                                           0));
   then->AddInstruction(new (&allocator) HGoto());
   else_->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimBoolean,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   else_->AddInstruction(new (&allocator) HGoto());
   join->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                          nullptr,
                                                           Primitive::kPrimBoolean,
                                                           MemberOffset(42),
                                                           false,
                                                           kUnknownFieldIndex,
                                                           kUnknownClassDefIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache,
                                                           0));
   join->AddInstruction(new (&allocator) HExit());
 
@@ -196,7 +194,6 @@
 TEST_F(GVNTest, LoopFieldElimination) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   HGraph* graph = CreateGraph(&allocator);
   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -213,13 +210,13 @@
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                           nullptr,
                                                            Primitive::kPrimBoolean,
                                                            MemberOffset(42),
                                                            false,
                                                            kUnknownFieldIndex,
                                                            kUnknownClassDefIndex,
                                                            graph->GetDexFile(),
-                                                           dex_cache,
                                                            0));
   block->AddInstruction(new (&allocator) HGoto());
 
@@ -236,13 +233,13 @@
   loop_body->AddSuccessor(loop_header);
 
   loop_header->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                                 nullptr,
                                                                  Primitive::kPrimBoolean,
                                                                  MemberOffset(42),
                                                                  false,
                                                                  kUnknownFieldIndex,
                                                                  kUnknownClassDefIndex,
                                                                  graph->GetDexFile(),
-                                                                 dex_cache,
                                                                  0));
   HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
   loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
@@ -251,35 +248,35 @@
   // and the body to be GVN'ed.
   loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
                                                                parameter,
+                                                               nullptr,
                                                                Primitive::kPrimBoolean,
                                                                MemberOffset(42),
                                                                false,
                                                                kUnknownFieldIndex,
                                                                kUnknownClassDefIndex,
                                                                graph->GetDexFile(),
-                                                               dex_cache,
                                                                0));
   HInstruction* field_set = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                               nullptr,
                                                                Primitive::kPrimBoolean,
                                                                MemberOffset(42),
                                                                false,
                                                                kUnknownFieldIndex,
                                                                kUnknownClassDefIndex,
                                                                graph->GetDexFile(),
-                                                               dex_cache,
                                                                0));
   HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HGoto());
 
   exit->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+                                                          nullptr,
                                                           Primitive::kPrimBoolean,
                                                           MemberOffset(42),
                                                           false,
                                                           kUnknownFieldIndex,
                                                           kUnknownClassDefIndex,
                                                           graph->GetDexFile(),
-                                                          dex_cache,
                                                           0));
   HInstruction* field_get_in_exit = exit->GetLastInstruction();
   exit->AddInstruction(new (&allocator) HExit());
@@ -319,7 +316,6 @@
 TEST_F(GVNTest, LoopSideEffects) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
 
   static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC();
 
@@ -376,13 +372,13 @@
     // Make one block with a side effect.
     entry->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
                                                              parameter,
+                                                             nullptr,
                                                              Primitive::kPrimNot,
                                                              MemberOffset(42),
                                                              false,
                                                              kUnknownFieldIndex,
                                                              kUnknownClassDefIndex,
                                                              graph->GetDexFile(),
-                                                             dex_cache,
                                                              0));
 
     SideEffectsAnalysis side_effects(graph);
@@ -401,13 +397,13 @@
     outer_loop_body->InsertInstructionBefore(
         new (&allocator) HInstanceFieldSet(parameter,
                                            parameter,
+                                           nullptr,
                                            Primitive::kPrimNot,
                                            MemberOffset(42),
                                            false,
                                            kUnknownFieldIndex,
                                            kUnknownClassDefIndex,
                                            graph->GetDexFile(),
-                                           dex_cache,
                                            0),
         outer_loop_body->GetLastInstruction());
 
@@ -427,13 +423,13 @@
     inner_loop_body->InsertInstructionBefore(
         new (&allocator) HInstanceFieldSet(parameter,
                                            parameter,
+                                           nullptr,
                                            Primitive::kPrimNot,
                                            MemberOffset(42),
                                            false,
                                            kUnknownFieldIndex,
                                            kUnknownClassDefIndex,
                                            graph->GetDexFile(),
-                                           dex_cache,
                                            0),
         inner_loop_body->GetLastInstruction());
 
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 3b83e95..c970e5c 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -429,13 +429,13 @@
   DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
   HInstanceFieldGet* result = new (graph_->GetArena()) HInstanceFieldGet(
       receiver,
+      field,
       Primitive::kPrimNot,
       field->GetOffset(),
       field->IsVolatile(),
       field->GetDexFieldIndex(),
       field->GetDeclaringClass()->GetDexClassDefIndex(),
       *field->GetDexFile(),
-      handles_->NewHandle(field->GetDexCache()),
       dex_pc);
   // The class of a field is effectively final, and does not have any memory dependencies.
   result->SetSideEffects(SideEffects::None());
@@ -618,6 +618,9 @@
     } else {
       one_target_inlined = true;
 
+      VLOG(compiler) << "Polymorphic call to " << ArtMethod::PrettyMethod(resolved_method)
+                     << " has inlined " << ArtMethod::PrettyMethod(method);
+
       // If we have inlined all targets before, and this receiver is the last seen,
       // we deoptimize instead of keeping the original invoke instruction.
       bool deoptimize = all_targets_inlined &&
@@ -655,6 +658,7 @@
                    << " of its targets could be inlined";
     return false;
   }
+
   MaybeRecordStat(kInlinedPolymorphicCall);
 
   // Run type propagation to get the guards typed.
@@ -1161,13 +1165,13 @@
   DCHECK(resolved_field != nullptr);
   HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
       obj,
+      resolved_field,
       resolved_field->GetTypeAsPrimitiveType(),
       resolved_field->GetOffset(),
       resolved_field->IsVolatile(),
       field_index,
       resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
       *dex_cache->GetDexFile(),
-      dex_cache,
       // Read barrier generates a runtime call in slow path and we need a valid
       // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
       /* dex_pc */ 0);
@@ -1190,13 +1194,13 @@
   HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
       obj,
       value,
+      resolved_field,
       resolved_field->GetTypeAsPrimitiveType(),
       resolved_field->GetOffset(),
       resolved_field->IsVolatile(),
       field_index,
       resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
       *dex_cache->GetDexFile(),
-      dex_cache,
       // Read barrier generates a runtime call in slow path and we need a valid
       // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
       /* dex_pc */ 0);
@@ -1424,15 +1428,6 @@
         return false;
       }
 
-      if (current->IsNewInstance() &&
-          (current->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectWithAccessCheck)) {
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because it is using an entrypoint"
-                       << " with access checks";
-        // Allocation entrypoint does not handle inlined frames.
-        return false;
-      }
-
       if (current->IsNewArray() &&
           (current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) {
         VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
@@ -1579,6 +1574,13 @@
                                     /* declared_can_be_null */ true,
                                     return_replacement)) {
         return true;
+      } else if (return_replacement->IsInstanceFieldGet()) {
+        HInstanceFieldGet* field_get = return_replacement->AsInstanceFieldGet();
+        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+        if (field_get->GetFieldInfo().GetField() ==
+              class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0)) {
+          return true;
+        }
       }
     } else if (return_replacement->IsInstanceOf()) {
       // Inlining InstanceOf into an If may put a tighter bound on reference types.
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index af8e2c8..009d549 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -917,11 +917,11 @@
   bool finalizable;
   bool needs_access_check = NeedsAccessCheck(type_index, dex_cache, &finalizable);
 
-  // Only the non-resolved entrypoint handles the finalizable class case. If we
+  // Only the access check entrypoint handles the finalizable class case. If we
   // need access checks, then we haven't resolved the method and the class may
   // again be finalizable.
   QuickEntrypointEnum entrypoint = (finalizable || needs_access_check)
-      ? kQuickAllocObject
+      ? kQuickAllocObjectWithChecks
       : kQuickAllocObjectInitialized;
 
   if (outer_dex_cache.Get() != dex_cache.Get()) {
@@ -946,7 +946,6 @@
 
   AppendInstruction(new (arena_) HNewInstance(
       cls,
-      graph_->GetCurrentMethod(),
       dex_pc,
       type_index,
       *dex_compilation_unit_->GetDexFile(),
@@ -1235,13 +1234,13 @@
       uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
       field_set = new (arena_) HInstanceFieldSet(object,
                                                  value,
+                                                 resolved_field,
                                                  field_type,
                                                  resolved_field->GetOffset(),
                                                  resolved_field->IsVolatile(),
                                                  field_index,
                                                  class_def_index,
                                                  *dex_file_,
-                                                 dex_compilation_unit_->GetDexCache(),
                                                  dex_pc);
     }
     AppendInstruction(field_set);
@@ -1256,13 +1255,13 @@
     } else {
       uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
       field_get = new (arena_) HInstanceFieldGet(object,
+                                                 resolved_field,
                                                  field_type,
                                                  resolved_field->GetOffset(),
                                                  resolved_field->IsVolatile(),
                                                  field_index,
                                                  class_def_index,
                                                  *dex_file_,
-                                                 dex_compilation_unit_->GetDexCache(),
                                                  dex_pc);
     }
     AppendInstruction(field_get);
@@ -1311,9 +1310,9 @@
 }
 
 void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
-                                                     uint32_t dex_pc,
-                                                     bool is_put,
-                                                     Primitive::Type field_type) {
+                                                           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();
 
@@ -1400,23 +1399,23 @@
     DCHECK_EQ(HPhi::ToPhiType(value->GetType()), HPhi::ToPhiType(field_type));
     AppendInstruction(new (arena_) HStaticFieldSet(cls,
                                                    value,
+                                                   resolved_field,
                                                    field_type,
                                                    resolved_field->GetOffset(),
                                                    resolved_field->IsVolatile(),
                                                    field_index,
                                                    class_def_index,
                                                    *dex_file_,
-                                                   dex_cache_,
                                                    dex_pc));
   } else {
     AppendInstruction(new (arena_) HStaticFieldGet(cls,
+                                                   resolved_field,
                                                    field_type,
                                                    resolved_field->GetOffset(),
                                                    resolved_field->IsVolatile(),
                                                    field_index,
                                                    class_def_index,
                                                    *dex_file_,
-                                                   dex_cache_,
                                                    dex_pc));
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
   }
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 439e3b6..911bfb9 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1118,7 +1118,66 @@
   VisitCondition(condition);
 }
 
+// Recognize the following pattern:
+// obj.getClass() ==/!= Foo.class
+// And replace it with a constant value if the type of `obj` is statically known.
+static bool RecognizeAndSimplifyClassCheck(HCondition* condition) {
+  HInstruction* input_one = condition->InputAt(0);
+  HInstruction* input_two = condition->InputAt(1);
+  HLoadClass* load_class = input_one->IsLoadClass()
+      ? input_one->AsLoadClass()
+      : input_two->AsLoadClass();
+  if (load_class == nullptr) {
+    return false;
+  }
+
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  if (!class_rti.IsValid()) {
+    // Unresolved class.
+    return false;
+  }
+
+  HInstanceFieldGet* field_get = (load_class == input_one)
+      ? input_two->AsInstanceFieldGet()
+      : input_one->AsInstanceFieldGet();
+  if (field_get == nullptr) {
+    return false;
+  }
+
+  HInstruction* receiver = field_get->InputAt(0);
+  ReferenceTypeInfo receiver_type = receiver->GetReferenceTypeInfo();
+  if (!receiver_type.IsExact()) {
+    return false;
+  }
+
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+    DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+    if (field_get->GetFieldInfo().GetField() != field) {
+      return false;
+    }
+
+    // We can replace the compare.
+    int value = 0;
+    if (receiver_type.IsEqual(class_rti)) {
+      value = condition->IsEqual() ? 1 : 0;
+    } else {
+      value = condition->IsNotEqual() ? 1 : 0;
+    }
+    condition->ReplaceWith(condition->GetBlock()->GetGraph()->GetIntConstant(value));
+    return true;
+  }
+}
+
 void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
+  if (condition->IsEqual() || condition->IsNotEqual()) {
+    if (RecognizeAndSimplifyClassCheck(condition)) {
+      return;
+    }
+  }
+
   // Reverse condition if left is constant. Our code generators prefer constant
   // on the right hand side.
   if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) {
@@ -1843,11 +1902,11 @@
   // so create the HArrayLength, HBoundsCheck and HArrayGet.
   HArrayLength* length = new (arena) HArrayLength(str, dex_pc, /* is_string_length */ true);
   invoke->GetBlock()->InsertInstructionBefore(length, invoke);
-  HBoundsCheck* bounds_check =
-      new (arena) HBoundsCheck(index, length, dex_pc, invoke->GetDexMethodIndex());
+  HBoundsCheck* bounds_check = new (arena) HBoundsCheck(
+      index, length, dex_pc, invoke->GetDexMethodIndex());
   invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke);
-  HArrayGet* array_get =
-      new (arena) HArrayGet(str, index, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true);
+  HArrayGet* array_get = new (arena) HArrayGet(
+      str, bounds_check, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true);
   invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, array_get);
   bounds_check->CopyEnvironmentFrom(invoke->GetEnvironment());
   GetGraph()->SetHasBoundsChecks(true);
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 8c34dc6..5bcfa4c 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -111,20 +111,19 @@
   BuildLoop();
 
   // Populate the loop with instructions: set/get field with different types.
-  ScopedNullHandle<mirror::DexCache> dex_cache;
   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
+                                                                nullptr,
                                                                 Primitive::kPrimLong,
                                                                 MemberOffset(10),
                                                                 false,
                                                                 kUnknownFieldIndex,
                                                                 kUnknownClassDefIndex,
                                                                 graph_->GetDexFile(),
-                                                                dex_cache,
                                                                 0);
   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
-      parameter_, int_constant_, Primitive::kPrimInt, MemberOffset(20),
-      false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), dex_cache, 0);
+      parameter_, int_constant_, nullptr, Primitive::kPrimInt, MemberOffset(20),
+      false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), 0);
   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
 
   EXPECT_EQ(get_field->GetBlock(), loop_body_);
@@ -140,24 +139,24 @@
   // Populate the loop with instructions: set/get field with same types.
   ScopedNullHandle<mirror::DexCache> dex_cache;
   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
+                                                                nullptr,
                                                                 Primitive::kPrimLong,
                                                                 MemberOffset(10),
                                                                 false,
                                                                 kUnknownFieldIndex,
                                                                 kUnknownClassDefIndex,
                                                                 graph_->GetDexFile(),
-                                                                dex_cache,
                                                                 0);
   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(parameter_,
                                                                 get_field,
+                                                                nullptr,
                                                                 Primitive::kPrimLong,
                                                                 MemberOffset(10),
                                                                 false,
                                                                 kUnknownFieldIndex,
                                                                 kUnknownClassDefIndex,
                                                                 graph_->GetDexFile(),
-                                                                dex_cache,
                                                                 0);
   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index afa17ce..db1b277 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -171,6 +171,7 @@
   friend class HGraph;
   friend class HInstruction;
   friend class HInstructionIterator;
+  friend class HInstructionIteratorHandleChanges;
   friend class HBackwardInstructionIterator;
 
   DISALLOW_COPY_AND_ASSIGN(HInstructionList);
@@ -2312,6 +2313,9 @@
 };
 std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
 
+// Iterates over the instructions, while preserving the next instruction
+// in case the current instruction gets removed from the list by the user
+// of this iterator.
 class HInstructionIterator : public ValueObject {
  public:
   explicit HInstructionIterator(const HInstructionList& instructions)
@@ -2333,6 +2337,28 @@
   DISALLOW_COPY_AND_ASSIGN(HInstructionIterator);
 };
 
+// Iterates over the instructions without saving the next instruction,
+// therefore handling changes in the graph potentially made by the user
+// of this iterator.
+class HInstructionIteratorHandleChanges : public ValueObject {
+ public:
+  explicit HInstructionIteratorHandleChanges(const HInstructionList& instructions)
+      : instruction_(instructions.first_instruction_) {
+  }
+
+  bool Done() const { return instruction_ == nullptr; }
+  HInstruction* Current() const { return instruction_; }
+  void Advance() {
+    instruction_ = instruction_->GetNext();
+  }
+
+ private:
+  HInstruction* instruction_;
+
+  DISALLOW_COPY_AND_ASSIGN(HInstructionIteratorHandleChanges);
+};
+
+
 class HBackwardInstructionIterator : public ValueObject {
  public:
   explicit HBackwardInstructionIterator(const HInstructionList& instructions)
@@ -3748,10 +3774,9 @@
   DISALLOW_COPY_AND_ASSIGN(HCompare);
 };
 
-class HNewInstance FINAL : public HExpression<2> {
+class HNewInstance FINAL : public HExpression<1> {
  public:
   HNewInstance(HInstruction* cls,
-               HCurrentMethod* current_method,
                uint32_t dex_pc,
                dex::TypeIndex type_index,
                const DexFile& dex_file,
@@ -3765,7 +3790,6 @@
     SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
     SetPackedFlag<kFlagFinalizable>(finalizable);
     SetRawInputAt(0, cls);
-    SetRawInputAt(1, current_method);
   }
 
   dex::TypeIndex GetTypeIndex() const { return type_index_; }
@@ -5056,60 +5080,62 @@
   DISALLOW_COPY_AND_ASSIGN(HNullCheck);
 };
 
+// Embeds an ArtField and all the information required by the compiler. We cache
+// that information to avoid requiring the mutator lock every time we need it.
 class FieldInfo : public ValueObject {
  public:
-  FieldInfo(MemberOffset field_offset,
+  FieldInfo(ArtField* field,
+            MemberOffset field_offset,
             Primitive::Type field_type,
             bool is_volatile,
             uint32_t index,
             uint16_t declaring_class_def_index,
-            const DexFile& dex_file,
-            Handle<mirror::DexCache> dex_cache)
-      : field_offset_(field_offset),
+            const DexFile& dex_file)
+      : field_(field),
+        field_offset_(field_offset),
         field_type_(field_type),
         is_volatile_(is_volatile),
         index_(index),
         declaring_class_def_index_(declaring_class_def_index),
-        dex_file_(dex_file),
-        dex_cache_(dex_cache) {}
+        dex_file_(dex_file) {}
 
+  ArtField* GetField() const { return field_; }
   MemberOffset GetFieldOffset() const { return field_offset_; }
   Primitive::Type GetFieldType() const { return field_type_; }
   uint32_t GetFieldIndex() const { return index_; }
   uint16_t GetDeclaringClassDefIndex() const { return declaring_class_def_index_;}
   const DexFile& GetDexFile() const { return dex_file_; }
   bool IsVolatile() const { return is_volatile_; }
-  Handle<mirror::DexCache> GetDexCache() const { return dex_cache_; }
 
  private:
+  ArtField* const field_;
   const MemberOffset field_offset_;
   const Primitive::Type field_type_;
   const bool is_volatile_;
   const uint32_t index_;
   const uint16_t declaring_class_def_index_;
   const DexFile& dex_file_;
-  const Handle<mirror::DexCache> dex_cache_;
 };
 
 class HInstanceFieldGet FINAL : public HExpression<1> {
  public:
   HInstanceFieldGet(HInstruction* value,
+                    ArtField* field,
                     Primitive::Type field_type,
                     MemberOffset field_offset,
                     bool is_volatile,
                     uint32_t field_idx,
                     uint16_t declaring_class_def_index,
                     const DexFile& dex_file,
-                    Handle<mirror::DexCache> dex_cache,
                     uint32_t dex_pc)
       : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetRawInputAt(0, value);
   }
 
@@ -5145,22 +5171,22 @@
  public:
   HInstanceFieldSet(HInstruction* object,
                     HInstruction* value,
+                    ArtField* field,
                     Primitive::Type field_type,
                     MemberOffset field_offset,
                     bool is_volatile,
                     uint32_t field_idx,
                     uint16_t declaring_class_def_index,
                     const DexFile& dex_file,
-                    Handle<mirror::DexCache> dex_cache,
                     uint32_t dex_pc)
       : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetPackedFlag<kFlagValueCanBeNull>(true);
     SetRawInputAt(0, object);
     SetRawInputAt(1, value);
@@ -5762,7 +5788,6 @@
       : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         string_index_(string_index) {
-    SetPackedFlag<kFlagIsInDexCache>(false);
     SetPackedField<LoadKindField>(LoadKind::kDexCacheViaMethod);
     load_data_.dex_file_ = &dex_file;
   }
@@ -5789,7 +5814,6 @@
   const DexFile& GetDexFile() const;
 
   dex::StringIndex GetStringIndex() const {
-    DCHECK(HasStringReference(GetLoadKind()) || /* For slow paths. */ !IsInDexCache());
     return string_index_;
   }
 
@@ -5814,7 +5838,7 @@
         load_kind == LoadKind::kJitTableAddress) {
       return false;
     }
-    return !IsInDexCache();
+    return true;
   }
 
   bool NeedsDexCacheOfDeclaringClass() const OVERRIDE {
@@ -5828,15 +5852,6 @@
     return SideEffects::CanTriggerGC();
   }
 
-  bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
-
-  void MarkInDexCache() {
-    SetPackedFlag<kFlagIsInDexCache>(true);
-    DCHECK(!NeedsEnvironment());
-    RemoveEnvironment();
-    SetSideEffects(SideEffects::None());
-  }
-
   void AddSpecialInput(HInstruction* special_input);
 
   using HInstruction::GetInputRecords;  // Keep the const version visible.
@@ -5852,8 +5867,7 @@
   DECLARE_INSTRUCTION(LoadString);
 
  private:
-  static constexpr size_t kFlagIsInDexCache = kNumberOfGenericPackedBits;
-  static constexpr size_t kFieldLoadKind = kFlagIsInDexCache + 1;
+  static constexpr size_t kFieldLoadKind = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldLoadKindSize =
       MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
   static constexpr size_t kNumberOfLoadStringPackedBits = kFieldLoadKind + kFieldLoadKindSize;
@@ -5949,22 +5963,22 @@
 class HStaticFieldGet FINAL : public HExpression<1> {
  public:
   HStaticFieldGet(HInstruction* cls,
+                  ArtField* field,
                   Primitive::Type field_type,
                   MemberOffset field_offset,
                   bool is_volatile,
                   uint32_t field_idx,
                   uint16_t declaring_class_def_index,
                   const DexFile& dex_file,
-                  Handle<mirror::DexCache> dex_cache,
                   uint32_t dex_pc)
       : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetRawInputAt(0, cls);
   }
 
@@ -5997,22 +6011,22 @@
  public:
   HStaticFieldSet(HInstruction* cls,
                   HInstruction* value,
+                  ArtField* field,
                   Primitive::Type field_type,
                   MemberOffset field_offset,
                   bool is_volatile,
                   uint32_t field_idx,
                   uint16_t declaring_class_def_index,
                   const DexFile& dex_file,
-                  Handle<mirror::DexCache> dex_cache,
                   uint32_t dex_pc)
       : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
-        field_info_(field_offset,
+        field_info_(field,
+                    field_offset,
                     field_type,
                     is_volatile,
                     field_idx,
                     declaring_class_def_index,
-                    dex_file,
-                    dex_cache) {
+                    dex_file) {
     SetPackedFlag<kFlagValueCanBeNull>(true);
     SetRawInputAt(0, cls);
     SetRawInputAt(1, value);
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index f9ac3a0..db7c1fb 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -134,39 +134,6 @@
   }
 }
 
-void PrepareForRegisterAllocation::VisitNewInstance(HNewInstance* instruction) {
-  HLoadClass* load_class = instruction->InputAt(0)->AsLoadClass();
-  const bool has_only_one_use = load_class->HasOnlyOneNonEnvironmentUse();
-  // Change the entrypoint to kQuickAllocObject if either:
-  // - the class is finalizable (only kQuickAllocObject handles finalizable classes),
-  // - the class needs access checks (we do not know if it's finalizable),
-  // - or the load class has only one use.
-  if (instruction->IsFinalizable() || has_only_one_use || load_class->NeedsAccessCheck()) {
-    instruction->SetEntrypoint(kQuickAllocObject);
-    instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex().index_), 0);
-    if (has_only_one_use) {
-      // We've just removed the only use of the HLoadClass. Since we don't run DCE after this pass,
-      // do it manually if possible.
-      if (!load_class->CanThrow()) {
-        // If the load class can not throw, it has no side effects and can be removed if there is
-        // only one use.
-        load_class->GetBlock()->RemoveInstruction(load_class);
-      } else if (!instruction->GetEnvironment()->IsFromInlinedInvoke() &&
-          CanMoveClinitCheck(load_class, instruction)) {
-        // The allocation entry point that deals with access checks does not work with inlined
-        // methods, so we need to check whether this allocation comes from an inlined method.
-        // We also need to make the same check as for moving clinit check, whether the HLoadClass
-        // has the clinit check responsibility or not (HLoadClass can throw anyway).
-        // If it needed access checks, we delegate the access check to the allocation.
-        if (load_class->NeedsAccessCheck()) {
-          instruction->SetEntrypoint(kQuickAllocObjectWithAccessCheck);
-        }
-        load_class->GetBlock()->RemoveInstruction(load_class);
-      }
-    }
-  }
-}
-
 bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition,
                                                       HInstruction* user) const {
   if (condition->GetNext() != user) {
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index a679148..c128227 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -44,7 +44,6 @@
   void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
   void VisitCondition(HCondition* condition) OVERRIDE;
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
-  void VisitNewInstance(HNewInstance* instruction) OVERRIDE;
 
   bool CanMoveClinitCheck(HInstruction* input, HInstruction* user) const;
   bool CanEmitConditionAt(HCondition* condition, HInstruction* user) const;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 33b3875..f8a4469 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -76,6 +76,7 @@
       worklist_(worklist),
       is_first_run_(is_first_run) {}
 
+  void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE;
   void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
   void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
@@ -151,38 +152,6 @@
   instruction->Accept(&visitor);
 }
 
-void ReferenceTypePropagation::Run() {
-  worklist_.reserve(kDefaultWorklistSize);
-
-  // To properly propagate type info we need to visit in the dominator-based order.
-  // Reverse post order guarantees a node's dominators are visited first.
-  // We take advantage of this order in `VisitBasicBlock`.
-  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
-    VisitBasicBlock(block);
-  }
-
-  ProcessWorklist();
-  ValidateTypes();
-}
-
-void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
-  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
-  // Handle Phis first as there might be instructions in the same block who depend on them.
-  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-    VisitPhi(it.Current()->AsPhi());
-  }
-
-  // Handle instructions.
-  for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-    HInstruction* instr = it.Current();
-    instr->Accept(&visitor);
-  }
-
-  // Add extra nodes to bound types.
-  BoundTypeForIfNotNull(block);
-  BoundTypeForIfInstanceOf(block);
-}
-
 // Check if we should create a bound type for the given object at the specified
 // position. Because of inlining and the fact we run RTP more than once and we
 // might have a HBoundType already. If we do, we should not create a new one.
@@ -225,6 +194,153 @@
   return false;
 }
 
+// Helper method to bound the type of `receiver` for all instructions dominated
+// by `start_block`, or `start_instruction` if `start_block` is null. The new
+// bound type will have its upper bound be `class_rti`.
+static void BoundTypeIn(HInstruction* receiver,
+                        HBasicBlock* start_block,
+                        HInstruction* start_instruction,
+                        const ReferenceTypeInfo& class_rti) {
+  // 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;
+  DCHECK(!receiver->IsLoadClass()) << "We should not replace HLoadClass instructions";
+  const HUseList<HInstruction*>& uses = receiver->GetUses();
+  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+    HInstruction* user = it->GetUser();
+    size_t index = it->GetIndex();
+    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+    ++it;
+    bool dominates = (start_instruction != nullptr)
+        ? start_instruction->StrictlyDominates(user)
+        : start_block->Dominates(user->GetBlock());
+    if (!dominates) {
+      continue;
+    }
+    if (bound_type == nullptr) {
+      ScopedObjectAccess soa(Thread::Current());
+      HInstruction* insert_point = (start_instruction != nullptr)
+          ? start_instruction->GetNext()
+          : start_block->GetFirstInstruction();
+      if (ShouldCreateBoundType(
+            insert_point, receiver, class_rti, start_instruction, start_block)) {
+        bound_type = new (receiver->GetBlock()->GetGraph()->GetArena()) HBoundType(receiver);
+        bound_type->SetUpperBound(class_rti, /* bound_can_be_null */ false);
+        start_block->InsertInstructionBefore(bound_type, insert_point);
+        // To comply with the RTP algorithm, don't type the bound type just yet, it will
+        // be handled in RTPVisitor::VisitBoundType.
+      } else {
+        // We already have a bound type on the position we would need to insert
+        // the new one. The existing bound type should dominate all the users
+        // (dchecked) so there's no need to continue.
+        break;
+      }
+    }
+    user->ReplaceInput(bound_type, index);
+  }
+  // If the receiver is a null check, also bound the type of the actual
+  // receiver.
+  if (receiver->IsNullCheck()) {
+    BoundTypeIn(receiver->InputAt(0), start_block, start_instruction, class_rti);
+  }
+}
+
+// Recognize the patterns:
+// if (obj.shadow$_klass_ == Foo.class) ...
+// deoptimize if (obj.shadow$_klass_ == Foo.class)
+static void BoundTypeForClassCheck(HInstruction* check) {
+  if (!check->IsIf() && !check->IsDeoptimize()) {
+    return;
+  }
+  HInstruction* compare = check->InputAt(0);
+  if (!compare->IsEqual() && !compare->IsNotEqual()) {
+    return;
+  }
+  HInstruction* input_one = compare->InputAt(0);
+  HInstruction* input_two = compare->InputAt(1);
+  HLoadClass* load_class = input_one->IsLoadClass()
+      ? input_one->AsLoadClass()
+      : input_two->AsLoadClass();
+  if (load_class == nullptr) {
+    return;
+  }
+
+  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+  if (!class_rti.IsValid()) {
+    // We have loaded an unresolved class. Don't bother bounding the type.
+    return;
+  }
+
+  HInstanceFieldGet* field_get = (load_class == input_one)
+      ? input_two->AsInstanceFieldGet()
+      : input_one->AsInstanceFieldGet();
+  if (field_get == nullptr) {
+    return;
+  }
+  HInstruction* receiver = field_get->InputAt(0);
+  ReferenceTypeInfo receiver_type = receiver->GetReferenceTypeInfo();
+  if (receiver_type.IsExact()) {
+    // If we already know the receiver type, don't bother updating its users.
+    return;
+  }
+
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+    DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+    if (field_get->GetFieldInfo().GetField() != field) {
+      return;
+    }
+  }
+
+  if (check->IsIf()) {
+    HBasicBlock* trueBlock = check->IsEqual()
+        ? check->AsIf()->IfTrueSuccessor()
+        : check->AsIf()->IfFalseSuccessor();
+    BoundTypeIn(receiver, trueBlock, /* start_instruction */ nullptr, class_rti);
+  } else {
+    DCHECK(check->IsDeoptimize());
+    if (check->IsEqual()) {
+      BoundTypeIn(receiver, check->GetBlock(), check, class_rti);
+    }
+  }
+}
+
+void ReferenceTypePropagation::Run() {
+  worklist_.reserve(kDefaultWorklistSize);
+
+  // To properly propagate type info we need to visit in the dominator-based order.
+  // Reverse post order guarantees a node's dominators are visited first.
+  // We take advantage of this order in `VisitBasicBlock`.
+  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+    VisitBasicBlock(block);
+  }
+
+  ProcessWorklist();
+  ValidateTypes();
+}
+
+void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
+  // Handle Phis first as there might be instructions in the same block who depend on them.
+  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+    VisitPhi(it.Current()->AsPhi());
+  }
+
+  // Handle instructions. Since RTP may add HBoundType instructions just after the
+  // last visited instruction, use `HInstructionIteratorHandleChanges` iterator.
+  for (HInstructionIteratorHandleChanges it(block->GetInstructions()); !it.Done(); it.Advance()) {
+    HInstruction* instr = it.Current();
+    instr->Accept(&visitor);
+  }
+
+  // Add extra nodes to bound types.
+  BoundTypeForIfNotNull(block);
+  BoundTypeForIfInstanceOf(block);
+  BoundTypeForClassCheck(block->GetLastInstruction());
+}
+
 void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
   HIf* ifInstruction = block->GetLastInstruction()->AsIf();
   if (ifInstruction == nullptr) {
@@ -254,40 +370,14 @@
 
   // 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;
   HBasicBlock* notNullBlock = ifInput->IsNotEqual()
       ? ifInstruction->IfTrueSuccessor()
       : ifInstruction->IfFalseSuccessor();
 
-  const HUseList<HInstruction*>& uses = obj->GetUses();
-  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
-    HInstruction* user = it->GetUser();
-    size_t index = it->GetIndex();
-    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
-    ++it;
-    if (notNullBlock->Dominates(user->GetBlock())) {
-      if (bound_type == nullptr) {
-        ScopedObjectAccess soa(Thread::Current());
-        HInstruction* insert_point = notNullBlock->GetFirstInstruction();
-        ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
-            handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
-        if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
-          bound_type = new (graph_->GetArena()) HBoundType(obj);
-          bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false);
-          if (obj->GetReferenceTypeInfo().IsValid()) {
-            bound_type->SetReferenceTypeInfo(obj->GetReferenceTypeInfo());
-          }
-          notNullBlock->InsertInstructionBefore(bound_type, insert_point);
-        } else {
-          // We already have a bound type on the position we would need to insert
-          // the new one. The existing bound type should dominate all the users
-          // (dchecked) so there's no need to continue.
-          break;
-        }
-      }
-      user->ReplaceInput(bound_type, index);
-    }
-  }
+  ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
+      handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
+
+  BoundTypeIn(obj, notNullBlock, /* start_instruction */ nullptr, object_rti);
 }
 
 // Returns true if one of the patterns below has been recognized. If so, the
@@ -378,15 +468,10 @@
 
   HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
   ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
-  {
-    if (!class_rti.IsValid()) {
-      // He have loaded an unresolved class. Don't bother bounding the type.
-      return;
-    }
+  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;
 
   HInstruction* obj = instanceOf->InputAt(0);
   if (obj->GetReferenceTypeInfo().IsExact() && !obj->IsPhi()) {
@@ -398,33 +483,14 @@
     // input.
     return;
   }
-  DCHECK(!obj->IsLoadClass()) << "We should not replace HLoadClass instructions";
-  const HUseList<HInstruction*>& uses = obj->GetUses();
-  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
-    HInstruction* user = it->GetUser();
-    size_t index = it->GetIndex();
-    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
-    ++it;
-    if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
-      if (bound_type == nullptr) {
-        ScopedObjectAccess soa(Thread::Current());
-        HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
-        if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
-          bound_type = new (graph_->GetArena()) HBoundType(obj);
-          bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
-          bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
-                                    /* InstanceOf fails for null. */ false);
-          instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
-        } else {
-          // We already have a bound type on the position we would need to insert
-          // the new one. The existing bound type should dominate all the users
-          // (dchecked) so there's no need to continue.
-          break;
-        }
-      }
-      user->ReplaceInput(bound_type, index);
+
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    if (!class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
+      class_rti = ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false);
     }
   }
+  BoundTypeIn(obj, instanceOfTrueBlock, /* start_instruction */ nullptr, class_rti);
 }
 
 void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* instr,
@@ -464,6 +530,10 @@
   }
 }
 
+void ReferenceTypePropagation::RTPVisitor::VisitDeoptimize(HDeoptimize* instr) {
+  BoundTypeForClassCheck(instr);
+}
+
 void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr,
                                                                    dex::TypeIndex type_idx,
                                                                    const DexFile& dex_file,
@@ -515,16 +585,9 @@
   ScopedObjectAccess soa(Thread::Current());
   ObjPtr<mirror::Class> klass;
 
-  // The field index is unknown only during tests.
-  if (info.GetFieldIndex() != kUnknownFieldIndex) {
-    ClassLinker* cl = Runtime::Current()->GetClassLinker();
-    ArtField* field = cl->GetResolvedField(info.GetFieldIndex(),
-                                           MakeObjPtr(info.GetDexCache().Get()));
-    // TODO: There are certain cases where we can't resolve the field.
-    // b/21914925 is open to keep track of a repro case for this issue.
-    if (field != nullptr) {
-      klass = field->GetType<false>();
-    }
+  // The field is unknown only during tests.
+  if (info.GetField() != nullptr) {
+    klass = info.GetField()->GetType<false>();
   }
 
   SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 559f409..2227872 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -492,7 +492,6 @@
                                   HInstruction** input2) {
   HGraph* graph = CreateGraph(allocator);
   HBasicBlock* entry = new (allocator) HBasicBlock(graph);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
@@ -504,13 +503,13 @@
   entry->AddSuccessor(block);
 
   HInstruction* test = new (allocator) HInstanceFieldGet(parameter,
+                                                         nullptr,
                                                          Primitive::kPrimBoolean,
                                                          MemberOffset(22),
                                                          false,
                                                          kUnknownFieldIndex,
                                                          kUnknownClassDefIndex,
                                                          graph->GetDexFile(),
-                                                         dex_cache,
                                                          0);
   block->AddInstruction(test);
   block->AddInstruction(new (allocator) HIf(test));
@@ -531,22 +530,22 @@
   *phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
   join->AddPhi(*phi);
   *input1 = new (allocator) HInstanceFieldGet(parameter,
+                                              nullptr,
                                               Primitive::kPrimInt,
                                               MemberOffset(42),
                                               false,
                                               kUnknownFieldIndex,
                                               kUnknownClassDefIndex,
                                               graph->GetDexFile(),
-                                              dex_cache,
                                               0);
   *input2 = new (allocator) HInstanceFieldGet(parameter,
+                                              nullptr,
                                               Primitive::kPrimInt,
                                               MemberOffset(42),
                                               false,
                                               kUnknownFieldIndex,
                                               kUnknownClassDefIndex,
                                               graph->GetDexFile(),
-                                              dex_cache,
                                               0);
   then->AddInstruction(*input1);
   else_->AddInstruction(*input2);
@@ -654,7 +653,6 @@
                                 HInstruction** field,
                                 HInstruction** ret) {
   HGraph* graph = CreateGraph(allocator);
-  ScopedNullHandle<mirror::DexCache> dex_cache;
   HBasicBlock* entry = new (allocator) HBasicBlock(graph);
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
@@ -667,13 +665,13 @@
   entry->AddSuccessor(block);
 
   *field = new (allocator) HInstanceFieldGet(parameter,
+                                             nullptr,
                                              Primitive::kPrimInt,
                                              MemberOffset(42),
                                              false,
                                              kUnknownFieldIndex,
                                              kUnknownClassDefIndex,
                                              graph->GetDexFile(),
-                                             dex_cache,
                                              0);
   block->AddInstruction(*field);
   *ret = new (allocator) HReturn(*field);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 9fdeccf..ca26c30 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -270,7 +270,6 @@
 
 void HSharpening::ProcessLoadString(HLoadString* load_string) {
   DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
-  DCHECK(!load_string->IsInDexCache());
 
   const DexFile& dex_file = load_string->GetDexFile();
   dex::StringIndex string_index = load_string->GetStringIndex();
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index ab4f9e9..a3fce02 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5610,7 +5610,7 @@
   " 214:	ecbd 8a10 	vpop	{s16-s31}\n",
   " 218:	e8bd 8de0 	ldmia.w	sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
   " 21c:	4660      	mov	r0, ip\n",
-  " 21e:	f8d9 c2b0 	ldr.w	ip, [r9, #688]	; 0x2b0\n",
+  " 21e:	f8d9 c2ac 	ldr.w	ip, [r9, #684]	; 0x2ac\n",
   " 222:	47e0      	blx	ip\n",
   nullptr
 };
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index e716cdb..4f06a91 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -524,7 +524,7 @@
                                          /* src */ "LMyThreadSet;",
                                          /* is_strict */ true,
                                          /* is_assignable */ true));
-  ASSERT_TRUE(HasAssignable("Ljava/util/Collection;", "LMyThreadSet;", true));
+  ASSERT_TRUE(HasAssignable("Ljava/util/Collection;", "Ljava/util/Set;", true));
 }
 
 TEST_F(VerifierDepsTest, Assignable_BothArrays_Resolved) {
@@ -539,26 +539,6 @@
   ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
 }
 
-// We test that VerifierDeps does not try to optimize by storing assignability
-// of the component types. This is due to the fact that the component type may
-// be an erroneous class, even though the array type has resolved status.
-
-TEST_F(VerifierDepsTest, Assignable_ArrayToInterface1) {
-  ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/io/Serializable;",
-                                         /* src */ "[Ljava/util/TimeZone;",
-                                         /* is_strict */ true,
-                                         /* is_assignable */ true));
-  ASSERT_TRUE(HasAssignable("Ljava/io/Serializable;", "[Ljava/util/TimeZone;", true));
-}
-
-TEST_F(VerifierDepsTest, Assignable_ArrayToInterface2) {
-  ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/io/Serializable;",
-                                         /* src */ "[LMyThreadSet;",
-                                         /* is_strict */ true,
-                                         /* is_assignable */ true));
-  ASSERT_TRUE(HasAssignable("Ljava/io/Serializable;", "[LMyThreadSet;", true));
-}
-
 TEST_F(VerifierDepsTest, NotAssignable_BothInBoot) {
   ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/lang/Exception;",
                                          /* src */ "Ljava/util/SimpleTimeZone;",
@@ -1083,7 +1063,7 @@
 TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) {
   ASSERT_TRUE(VerifyMethod("InvokeSuper_ThisAssignable"));
   ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface"));
-  ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "LMain;", true));
+  ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "Ljava/lang/Thread;", true));
   ASSERT_TRUE(HasMethod("interface",
                         "Ljava/lang/Runnable;",
                         "run",
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ece81e3..21b03eb 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1095,9 +1095,6 @@
         compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
     key_value_store_->Put(OatHeader::kCompilerFilter,
         CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
-    key_value_store_->Put(OatHeader::kHasPatchInfoKey,
-        compiler_options_->GetIncludePatchInformation() ? OatHeader::kTrueValue
-                                                        : OatHeader::kFalseValue);
   }
 
   // Parse the arguments from the command line. In case of an unrecognized option or impossible
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index a71ab4b..4d4ebdc 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1124,28 +1124,23 @@
 // Generate the allocation entrypoints for each allocator.
 GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
-ENTRY art_quick_alloc_object_rosalloc
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_RESOLVED_OBJECT(_rosalloc, RosAlloc).
+ENTRY art_quick_alloc_object_resolved_rosalloc
     // Fast path rosalloc allocation.
-    // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current
-    // r2, r3, r12: free.
-    ldr    r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32]    // Load dex cache resolved types array
-                                                              // Load the class (r2)
-    ldr    r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-    cbz    r2, .Lart_quick_alloc_object_rosalloc_slow_path    // Check null class
-
+    // r0: type/return value, r9: Thread::Current
+    // r1, r2, r3, r12: free.
     ldr    r3, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]     // Check if the thread local
                                                               // allocation stack has room.
                                                               // TODO: consider using ldrd.
     ldr    r12, [r9, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET]
     cmp    r3, r12
-    bhs    .Lart_quick_alloc_object_rosalloc_slow_path
+    bhs    .Lart_quick_alloc_object_resolved_rosalloc_slow_path
 
-    ldr    r3, [r2, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET]  // Load the object size (r3)
+    ldr    r3, [r0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET]  // Load the object size (r3)
     cmp    r3, #ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE        // Check if the size is for a thread
                                                               // local allocation. Also does the
                                                               // initialized and finalizable checks.
-    bhs    .Lart_quick_alloc_object_rosalloc_slow_path
+    bhs    .Lart_quick_alloc_object_resolved_rosalloc_slow_path
                                                               // Compute the rosalloc bracket index
                                                               // from the size. Since the size is
                                                               // already aligned we can combine the
@@ -1159,7 +1154,7 @@
                                                               // Load the free list head (r3). This
                                                               // will be the return val.
     ldr    r3, [r12, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
-    cbz    r3, .Lart_quick_alloc_object_rosalloc_slow_path
+    cbz    r3, .Lart_quick_alloc_object_resolved_rosalloc_slow_path
     // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1.
     ldr    r1, [r3, #ROSALLOC_SLOT_NEXT_OFFSET]               // Load the next pointer of the head
                                                               // and update the list head with the
@@ -1172,8 +1167,8 @@
 #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
 #error "Class pointer needs to overwrite next pointer."
 #endif
-    POISON_HEAP_REF r2
-    str    r2, [r3, #MIRROR_OBJECT_CLASS_OFFSET]
+    POISON_HEAP_REF r0
+    str    r0, [r3, #MIRROR_OBJECT_CLASS_OFFSET]
                                                               // Fence. This is "ish" not "ishst" so
                                                               // that it also ensures ordering of
                                                               // the class status load with respect
@@ -1204,20 +1199,20 @@
     mov    r0, r3                                             // Set the return value and return.
     bx     lr
 
-.Lart_quick_alloc_object_rosalloc_slow_path:
+.Lart_quick_alloc_object_resolved_rosalloc_slow_path:
     SETUP_SAVE_REFS_ONLY_FRAME r2     @ save callee saves in case of GC
-    mov    r2, r9                     @ pass Thread::Current
-    bl     artAllocObjectFromCodeRosAlloc     @ (uint32_t type_idx, Method* method, Thread*)
+    mov    r1, r9                     @ pass Thread::Current
+    bl     artAllocObjectFromCodeResolvedRosAlloc     @ (mirror::Class* cls, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-END art_quick_alloc_object_rosalloc
+END art_quick_alloc_object_resolved_rosalloc
 
-// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
+// The common fast path code for art_quick_alloc_object_resolved_tlab
+// and art_quick_alloc_object_resolved_region_tlab.
 //
-// r0: type_idx/return value, r1: ArtMethod*, r2: class, r9: Thread::Current, r3, r12: free.
-// Need to preserve r0 and r1 to the slow path.
-.macro ALLOC_OBJECT_TLAB_FAST_PATH slowPathLabel
-    cbz    r2, \slowPathLabel                                 // Check null class
+// r0: type r9: Thread::Current, r1, r2, r3, r12: free.
+// Need to preserve r0 to the slow path.
+.macro ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH slowPathLabel
                                                               // Load thread_local_pos (r12) and
                                                               // thread_local_end (r3) with ldrd.
                                                               // Check constraints for ldrd.
@@ -1232,14 +1227,14 @@
     // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1.
                                                               // Reload old thread_local_pos (r0)
                                                               // for the return value.
-    ldr    r0, [r9, #THREAD_LOCAL_POS_OFFSET]
-    add    r1, r0, r3
+    ldr    r2, [r9, #THREAD_LOCAL_POS_OFFSET]
+    add    r1, r2, r3
     str    r1, [r9, #THREAD_LOCAL_POS_OFFSET]                 // Store new thread_local_pos.
     ldr    r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET]             // Increment thread_local_objects.
     add    r1, r1, #1
     str    r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET]
-    POISON_HEAP_REF r2
-    str    r2, [r0, #MIRROR_OBJECT_CLASS_OFFSET]              // Store the class pointer.
+    POISON_HEAP_REF r0
+    str    r0, [r2, #MIRROR_OBJECT_CLASS_OFFSET]              // Store the class pointer.
                                                               // Fence. This is "ish" not "ishst" so
                                                               // that the code after this allocation
                                                               // site will see the right values in
@@ -1247,71 +1242,46 @@
                                                               // Alternatively we could use "ishst"
                                                               // if we use load-acquire for the
                                                               // object size load.)
+    mov    r0, r2
     dmb    ish
     bx     lr
 .endm
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB).
-ENTRY art_quick_alloc_object_tlab
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_RESOLVED_OBJECT(_tlab, TLAB).
+ENTRY art_quick_alloc_object_resolved_tlab
     // Fast path tlab allocation.
-    // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current
-    // r2, r3, r12: free.
+    // r0: type, r9: Thread::Current
+    // r1, r2, r3, r12: free.
 #if defined(USE_READ_BARRIER)
     mvn    r0, #0                                             // Read barrier not supported here.
     bx     lr                                                 // Return -1.
 #endif
-    ldr    r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32]    // Load dex cache resolved types array
-                                                              // Load the class (r2)
-    ldr    r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-    ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path
-.Lart_quick_alloc_object_tlab_slow_path:
+    ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path
+.Lart_quick_alloc_object_resolved_tlab_slow_path:
     SETUP_SAVE_REFS_ONLY_FRAME r2                             // Save callee saves in case of GC.
-    mov    r2, r9                                             // Pass Thread::Current.
-    bl     artAllocObjectFromCodeTLAB    // (uint32_t type_idx, Method* method, Thread*)
+    mov    r1, r9                                             // Pass Thread::Current.
+    bl     artAllocObjectFromCodeResolvedTLAB                 // (mirror::Class* klass, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-END art_quick_alloc_object_tlab
+END art_quick_alloc_object_resolved_tlab
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
-ENTRY art_quick_alloc_object_region_tlab
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+ENTRY art_quick_alloc_object_resolved_region_tlab
     // Fast path tlab allocation.
-    // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current, r2, r3, r12: free.
+    // r0: type, r9: Thread::Current, r1, r2, r3, r12: free.
 #if !defined(USE_READ_BARRIER)
     eor    r0, r0, r0                                         // Read barrier must be enabled here.
     sub    r0, r0, #1                                         // Return -1.
     bx     lr
 #endif
-    ldr    r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32]    // Load dex cache resolved types array
-                                                              // Load the class (r2)
-    ldr    r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-                                                              // Read barrier for class load.
-    ldr    r3, [r9, #THREAD_IS_GC_MARKING_OFFSET]
-    cbnz   r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking
-.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit:
-    ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path
-.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking:
-    cbz    r2, .Lart_quick_alloc_object_region_tlab_slow_path  // Null check for loading lock word.
-    // Check lock word for mark bit, if marked do the allocation.
-    ldr r3, [r2, MIRROR_OBJECT_LOCK_WORD_OFFSET]
-    ands r3, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
-    bne .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
-.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path:
-                                                              // The read barrier slow path. Mark
-                                                              // the class.
-    push   {r0, r1, r3, lr}                                   // Save registers. r3 is pushed only
-                                                              // to align sp by 16 bytes.
-    mov    r0, r2                                             // Pass the class as the first param.
-    bl     artReadBarrierMark
-    mov    r2, r0                                             // Get the (marked) class back.
-    pop    {r0, r1, r3, lr}
-    b      .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
-.Lart_quick_alloc_object_region_tlab_slow_path:
+    ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path
+.Lart_quick_alloc_object_resolved_region_tlab_slow_path:
     SETUP_SAVE_REFS_ONLY_FRAME r2                             // Save callee saves in case of GC.
-    mov    r2, r9                                             // Pass Thread::Current.
-    bl     artAllocObjectFromCodeRegionTLAB    // (uint32_t type_idx, Method* method, Thread*)
+    mov    r1, r9                                             // Pass Thread::Current.
+    bl     artAllocObjectFromCodeResolvedRegionTLAB           // (mirror::Class* klass, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-END art_quick_alloc_object_region_tlab
+END art_quick_alloc_object_resolved_region_tlab
 
     /*
      * Called by managed code when the value in rSUSPEND has been decremented to 0.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index b88515f..8b1e038 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1669,7 +1669,6 @@
 // Generate the allocation entrypoints for each allocator.
 GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_REGION_TLAB_ALLOCATORS
 // Comment out allocators that have arm64 specific asm.
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) implemented in asm
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
@@ -1682,27 +1681,23 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
-ENTRY art_quick_alloc_object_rosalloc
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc).
+ENTRY art_quick_alloc_object_resolved_rosalloc
     // Fast path rosalloc allocation.
-    // x0: type_idx/return value, x1: ArtMethod*, xSELF(x19): Thread::Current
-    // x2-x7: free.
-    ldr    x2, [x1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64]    // Load dex cache resolved types array
-                                                              // Load the class (x2)
-    ldr    w2, [x2, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-    cbz    x2, .Lart_quick_alloc_object_rosalloc_slow_path    // Check null class
+    // x0: type, xSELF(x19): Thread::Current
+    // x1-x7: free.
     ldr    x3, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]  // Check if the thread local
                                                               // allocation stack has room.
                                                               // ldp won't work due to large offset.
     ldr    x4, [xSELF, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET]
     cmp    x3, x4
-    bhs    .Lart_quick_alloc_object_rosalloc_slow_path
-    ldr    w3, [x2, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET]  // Load the object size (x3)
+    bhs    .Lart_quick_alloc_object_resolved_rosalloc_slow_path
+    ldr    w3, [x0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET]  // Load the object size (x3)
     cmp    x3, #ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE        // Check if the size is for a thread
                                                               // local allocation. Also does the
                                                               // finalizable and initialization
                                                               // checks.
-    bhs    .Lart_quick_alloc_object_rosalloc_slow_path
+    bhs    .Lart_quick_alloc_object_resolved_rosalloc_slow_path
                                                               // Compute the rosalloc bracket index
                                                               // from the size. Since the size is
                                                               // already aligned we can combine the
@@ -1715,7 +1710,7 @@
                                                               // Load the free list head (x3). This
                                                               // will be the return val.
     ldr    x3, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
-    cbz    x3, .Lart_quick_alloc_object_rosalloc_slow_path
+    cbz    x3, .Lart_quick_alloc_object_resolved_rosalloc_slow_path
     // "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1.
     ldr    x1, [x3, #ROSALLOC_SLOT_NEXT_OFFSET]               // Load the next pointer of the head
                                                               // and update the list head with the
@@ -1728,8 +1723,8 @@
 #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
 #error "Class pointer needs to overwrite next pointer."
 #endif
-    POISON_HEAP_REF w2
-    str    w2, [x3, #MIRROR_OBJECT_CLASS_OFFSET]
+    POISON_HEAP_REF w0
+    str    w0, [x3, #MIRROR_OBJECT_CLASS_OFFSET]
                                                               // Fence. This is "ish" not "ishst" so
                                                               // that it also ensures ordering of
                                                               // the object size load with respect
@@ -1759,13 +1754,13 @@
 
     mov    x0, x3                                             // Set the return value and return.
     ret
-.Lart_quick_alloc_object_rosalloc_slow_path:
-    SETUP_SAVE_REFS_ONLY_FRAME             // save callee saves in case of GC
-    mov    x2, xSELF                       // pass Thread::Current
-    bl     artAllocObjectFromCodeRosAlloc  // (uint32_t type_idx, Method* method, Thread*)
+.Lart_quick_alloc_object_resolved_rosalloc_slow_path:
+    SETUP_SAVE_REFS_ONLY_FRAME                      // save callee saves in case of GC
+    mov    x1, xSELF                                // pass Thread::Current
+    bl     artAllocObjectFromCodeResolvedRosAlloc   // (mirror::Class* klass, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-END art_quick_alloc_object_rosalloc
+END art_quick_alloc_object_resolved_rosalloc
 
 
 // The common fast path code for art_quick_alloc_array_region_tlab.
@@ -1834,16 +1829,6 @@
     ret
 .endm
 
-// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
-//
-// x0: type_idx/return value, x1: ArtMethod*, x2: Class*, xSELF(x19): Thread::Current
-// x3-x7: free.
-// Need to preserve x0 and x1 to the slow path.
-.macro ALLOC_OBJECT_TLAB_FAST_PATH slowPathLabel
-    cbz    x2, \slowPathLabel                                 // Check null class
-    ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED \slowPathLabel
-.endm
-
 // TODO: delete ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED since it is the same as
 // ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED.
 .macro ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED slowPathLabel
@@ -1853,20 +1838,18 @@
 .macro ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED slowPathLabel
     ldr    x4, [xSELF, #THREAD_LOCAL_POS_OFFSET]
     ldr    x5, [xSELF, #THREAD_LOCAL_END_OFFSET]
-    ldr    w7, [x2, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET]  // Load the object size (x7).
+    ldr    w7, [x0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET]  // Load the object size (x7).
     add    x6, x4, x7                                         // Add object size to tlab pos.
     cmp    x6, x5                                             // Check if it fits, overflow works
                                                               // since the tlab pos and end are 32
                                                               // bit values.
     bhi    \slowPathLabel
-    // "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1.
-    mov    x0, x4
     str    x6, [xSELF, #THREAD_LOCAL_POS_OFFSET]              // Store new thread_local_pos.
     ldr    x5, [xSELF, #THREAD_LOCAL_OBJECTS_OFFSET]          // Increment thread_local_objects.
     add    x5, x5, #1
     str    x5, [xSELF, #THREAD_LOCAL_OBJECTS_OFFSET]
-    POISON_HEAP_REF w2
-    str    w2, [x0, #MIRROR_OBJECT_CLASS_OFFSET]              // Store the class pointer.
+    POISON_HEAP_REF w0
+    str    w0, [x4, #MIRROR_OBJECT_CLASS_OFFSET]              // Store the class pointer.
                                                               // Fence. This is "ish" not "ishst" so
                                                               // that the code after this allocation
                                                               // site will see the right values in
@@ -1874,91 +1857,52 @@
                                                               // Alternatively we could use "ishst"
                                                               // if we use load-acquire for the
                                                               // object size load.)
+    mov    x0, x4
     dmb    ish
     ret
 .endm
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB).
-ENTRY art_quick_alloc_object_tlab
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB).
+ENTRY art_quick_alloc_object_resolved_tlab
     // Fast path tlab allocation.
-    // x0: type_idx/return value, x1: ArtMethod*, xSELF(x19): Thread::Current
-    // x2-x7: free.
+    // x0: type, xSELF(x19): Thread::Current
+    // x1-x7: free.
 #if defined(USE_READ_BARRIER)
     mvn    x0, xzr                                            // Read barrier not supported here.
     ret                                                       // Return -1.
 #endif
-    ldr    x2, [x1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64]    // Load dex cache resolved types array
-                                                              // Load the class (x2)
-    ldr    w2, [x2, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-    ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path
-.Lart_quick_alloc_object_tlab_slow_path:
-    SETUP_SAVE_REFS_ONLY_FRAME           // Save callee saves in case of GC.
-    mov    x2, xSELF                     // Pass Thread::Current.
-    bl     artAllocObjectFromCodeTLAB    // (uint32_t type_idx, Method* method, Thread*)
+    ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_object_resolved_tlab_slow_path
+.Lart_quick_alloc_object_resolved_tlab_slow_path:
+    SETUP_SAVE_REFS_ONLY_FRAME                    // Save callee saves in case of GC.
+    mov    x1, xSELF                              // Pass Thread::Current.
+    bl     artAllocObjectFromCodeResolvedTLAB     // (mirror::Class*, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-END art_quick_alloc_object_tlab
+END art_quick_alloc_object_resolved_tlab
 
 // The common code for art_quick_alloc_object_*region_tlab
-.macro GENERATE_ALLOC_OBJECT_REGION_TLAB name, entrypoint, fast_path, is_resolved, read_barrier
+.macro GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB name, entrypoint, fast_path
 ENTRY \name
     // Fast path region tlab allocation.
-    // x0: type_idx/resolved class/return value, x1: ArtMethod*, xSELF(x19): Thread::Current
-    // If is_resolved is 1 then x0 is the resolved type, otherwise it is the index.
-    // x2-x7: free.
+    // x0: type, xSELF(x19): Thread::Current
+    // x1-x7: free.
 #if !defined(USE_READ_BARRIER)
     mvn    x0, xzr                                            // Read barrier must be enabled here.
     ret                                                       // Return -1.
 #endif
-.if \is_resolved
-    mov    x2, x0 // class is actually stored in x0 already
-.else
-    ldr    x2, [x1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64]    // Load dex cache resolved types array
-                                                              // Load the class (x2)
-    ldr    w2, [x2, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-    // If the class is null, go slow path. The check is required to read the lock word.
-    cbz    w2, .Lslow_path\name
-.endif
-.if \read_barrier
-    // Most common case: GC is not marking.
-    ldr    w3, [xSELF, #THREAD_IS_GC_MARKING_OFFSET]
-    cbnz   x3, .Lmarking\name
-.endif
 .Ldo_allocation\name:
     \fast_path .Lslow_path\name
-.Lmarking\name:
-.if \read_barrier
-    // GC is marking, check the lock word of the class for the mark bit.
-    // Class is not null, check mark bit in lock word.
-    ldr    w3, [x2, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
-    // If the bit is not zero, do the allocation.
-    tbnz    w3, #LOCK_WORD_MARK_BIT_SHIFT, .Ldo_allocation\name
-                                                              // The read barrier slow path. Mark
-                                                              // the class.
-    SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32                   // Save registers (x0, x1, lr).
-    SAVE_REG xLR, 24                                          // Align sp by 16 bytes.
-    mov    x0, x2                                             // Pass the class as the first param.
-    bl     artReadBarrierMark
-    mov    x2, x0                                             // Get the (marked) class back.
-    RESTORE_REG xLR, 24
-    RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32                // Restore registers.
-    b      .Ldo_allocation\name
-.endif
 .Lslow_path\name:
     SETUP_SAVE_REFS_ONLY_FRAME                 // Save callee saves in case of GC.
-    mov    x2, xSELF                           // Pass Thread::Current.
-    bl     \entrypoint                         // (uint32_t type_idx, Method* method, Thread*)
+    mov    x1, xSELF                           // Pass Thread::Current.
+    bl     \entrypoint                         // (mirror::Class*, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \name
 .endm
 
-// Use ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED since the null check is already done in GENERATE_ALLOC_OBJECT_TLAB.
-GENERATE_ALLOC_OBJECT_REGION_TLAB art_quick_alloc_object_region_tlab, artAllocObjectFromCodeRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED, 0, 1
-// No read barrier for the resolved or initialized cases since the caller is responsible for the
-// read barrier due to the to-space invariant.
-GENERATE_ALLOC_OBJECT_REGION_TLAB art_quick_alloc_object_resolved_region_tlab, artAllocObjectFromCodeResolvedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED, 1, 0
-GENERATE_ALLOC_OBJECT_REGION_TLAB art_quick_alloc_object_initialized_region_tlab, artAllocObjectFromCodeInitializedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED, 1, 0
+GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB art_quick_alloc_object_resolved_region_tlab, artAllocObjectFromCodeResolvedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED
+GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB art_quick_alloc_object_initialized_region_tlab, artAllocObjectFromCodeInitializedRegionTLAB, ALLOC_OBJECT_TLAB_FAST_PATH_INITIALIZED
 
 // TODO: We could use this macro for the normal tlab allocator too.
 
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3e8cdc9..964ea56 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1831,116 +1831,10 @@
 // Generate the allocation entrypoints for each allocator.
 GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
-ENTRY art_quick_alloc_object_rosalloc
 
-    # Fast path rosalloc allocation
-    # a0: type_idx
-    # a1: ArtMethod*
-    # s1: Thread::Current
-    # -----------------------------
-    # t0: class
-    # t1: object size
-    # t2: rosalloc run
-    # t3: thread stack top offset
-    # t4: thread stack bottom offset
-    # v0: free list head
-    #
-    # t5, t6 : temps
-
-    lw    $t0, ART_METHOD_DEX_CACHE_TYPES_OFFSET_32($a1)       # Load dex cache resolved types
-                                                               # array.
-
-    sll   $t5, $a0, COMPRESSED_REFERENCE_SIZE_SHIFT            # Shift the value.
-    addu  $t5, $t0, $t5                                        # Compute the index.
-    lw    $t0, 0($t5)                                          # Load class (t0).
-    beqz  $t0, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    li    $t6, MIRROR_CLASS_STATUS_INITIALIZED
-    lw    $t5, MIRROR_CLASS_STATUS_OFFSET($t0)                 # Check class status.
-    bne   $t5, $t6, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    # Add a fake dependence from the following access flag and size loads to the status load. This
-    # is to prevent those loads from being reordered above the status load and reading wrong values.
-    xor   $t5, $t5, $t5
-    addu  $t0, $t0, $t5
-
-    lw    $t5, MIRROR_CLASS_ACCESS_FLAGS_OFFSET($t0)           # Check if access flags has
-    li    $t6, ACCESS_FLAGS_CLASS_IS_FINALIZABLE               # kAccClassIsFinalizable.
-    and   $t6, $t5, $t6
-    bnez  $t6, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    lw    $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)        # Check if thread local allocation
-    lw    $t4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1)        # stack has any room left.
-    bgeu  $t3, $t4, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    lw    $t1, MIRROR_CLASS_OBJECT_SIZE_OFFSET($t0)            # Load object size (t1).
-    li    $t5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE          # Check if size is for a thread local
-                                                               # allocation.
-    bgtu  $t1, $t5, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    # Compute the rosalloc bracket index from the size. Allign up the size by the rosalloc bracket
-    # quantum size and divide by the quantum size and subtract by 1.
-
-    addiu $t1, $t1, -1                                         # Decrease obj size and shift right
-    srl   $t1, $t1, ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT        # by quantum.
-
-    sll   $t2, $t1, POINTER_SIZE_SHIFT
-    addu  $t2, $t2, $s1
-    lw    $t2, THREAD_ROSALLOC_RUNS_OFFSET($t2)                # Load rosalloc run (t2).
-
-    # Load the free list head (v0).
-    # NOTE: this will be the return val.
-
-    lw    $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
-    beqz  $v0, .Lart_quick_alloc_object_rosalloc_slow_path
-    nop
-
-    # Load the next pointer of the head and update the list head with the next pointer.
-
-    lw    $t5, ROSALLOC_SLOT_NEXT_OFFSET($v0)
-    sw    $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
-
-    # Store the class pointer in the header. This also overwrites the first pointer. The offsets are
-    # asserted to match.
-
-#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
-#error "Class pointer needs to overwrite next pointer."
-#endif
-
-    POISON_HEAP_REF $t0
-    sw    $t0, MIRROR_OBJECT_CLASS_OFFSET($v0)
-
-    # Push the new object onto the thread local allocation stack and increment the thread local
-    # allocation stack top.
-
-    sw    $v0, 0($t3)
-    addiu $t3, $t3, COMPRESSED_REFERENCE_SIZE
-    sw    $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)
-
-    # Decrement the size of the free list.
-
-    lw    $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
-    addiu $t5, $t5, -1
-    sw    $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
-
-    sync                                                          # Fence.
-
-    jalr  $zero, $ra
-    nop
-
-  .Lart_quick_alloc_object_rosalloc_slow_path:
-
-    SETUP_SAVE_REFS_ONLY_FRAME
-    la    $t9, artAllocObjectFromCodeRosAlloc
-    jalr  $t9
-    move  $a2, $s1                                                # Pass self as argument.
-    RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-
-END art_quick_alloc_object_rosalloc
-
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 
     /*
      * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 0861d2d..2a18d53 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1775,107 +1775,9 @@
 // Generate the allocation entrypoints for each allocator.
 GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
-ENTRY art_quick_alloc_object_rosalloc
-
-    # Fast path rosalloc allocation
-    # a0: type_idx
-    # a1: ArtMethod*
-    # s1: Thread::Current
-    # -----------------------------
-    # t0: class
-    # t1: object size
-    # t2: rosalloc run
-    # t3: thread stack top offset
-    # a4: thread stack bottom offset
-    # v0: free list head
-    #
-    # a5, a6 : temps
-
-    ld     $t0, ART_METHOD_DEX_CACHE_TYPES_OFFSET_64($a1)   # Load dex cache resolved types array.
-
-    dsll   $a5, $a0, COMPRESSED_REFERENCE_SIZE_SHIFT        # Shift the value.
-    daddu  $a5, $t0, $a5                                    # Compute the index.
-    lwu    $t0, 0($a5)                                      # Load class (t0).
-    beqzc  $t0, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    li     $a6, MIRROR_CLASS_STATUS_INITIALIZED
-    lwu    $a5, MIRROR_CLASS_STATUS_OFFSET($t0)             # Check class status.
-    bnec   $a5, $a6, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    # Add a fake dependence from the following access flag and size loads to the status load. This
-    # is to prevent those loads from being reordered above the status load and reading wrong values.
-    xor    $a5, $a5, $a5
-    daddu  $t0, $t0, $a5
-
-    lwu    $a5, MIRROR_CLASS_ACCESS_FLAGS_OFFSET($t0)       # Check if access flags has
-    li     $a6, ACCESS_FLAGS_CLASS_IS_FINALIZABLE           # kAccClassIsFinalizable.
-    and    $a6, $a5, $a6
-    bnezc  $a6, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    ld     $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)    # Check if thread local allocation stack
-    ld     $a4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1)    # has any room left.
-    bgeuc  $t3, $a4, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    lwu    $t1, MIRROR_CLASS_OBJECT_SIZE_OFFSET($t0)        # Load object size (t1).
-    li     $a5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE      # Check if size is for a thread local
-                                                            # allocation.
-    bltuc  $a5, $t1, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    # Compute the rosalloc bracket index from the size. Allign up the size by the rosalloc bracket
-    # quantum size and divide by the quantum size and subtract by 1.
-    daddiu $t1, $t1, -1                                     # Decrease obj size and shift right by
-    dsrl   $t1, $t1, ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT    # quantum.
-
-    dsll   $t2, $t1, POINTER_SIZE_SHIFT
-    daddu  $t2, $t2, $s1
-    ld     $t2, THREAD_ROSALLOC_RUNS_OFFSET($t2)            # Load rosalloc run (t2).
-
-    # Load the free list head (v0).
-    # NOTE: this will be the return val.
-    ld     $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
-    beqzc  $v0, .Lart_quick_alloc_object_rosalloc_slow_path
-
-    # Load the next pointer of the head and update the list head with the next pointer.
-    ld     $a5, ROSALLOC_SLOT_NEXT_OFFSET($v0)
-    sd     $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
-
-    # Store the class pointer in the header. This also overwrites the first pointer. The offsets are
-    # asserted to match.
-
-#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
-#error "Class pointer needs to overwrite next pointer."
-#endif
-
-    POISON_HEAP_REF $t0
-    sw     $t0, MIRROR_OBJECT_CLASS_OFFSET($v0)
-
-    # Push the new object onto the thread local allocation stack and increment the thread local
-    # allocation stack top.
-    sd     $v0, 0($t3)
-    daddiu $t3, $t3, COMPRESSED_REFERENCE_SIZE
-    sd     $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)
-
-    # Decrement the size of the free list.
-    lw     $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
-    addiu  $a5, $a5, -1
-    sw     $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
-
-    sync                                         # Fence.
-
-    jalr   $zero, $ra
-    .cpreturn                                    # Restore gp from t8 in branch delay slot.
-
-.Lart_quick_alloc_object_rosalloc_slow_path:
-    SETUP_SAVE_REFS_ONLY_FRAME
-    jal    artAllocObjectFromCodeRosAlloc
-    move   $a2 ,$s1                              # Pass self as argument.
-    RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-
-END art_quick_alloc_object_rosalloc
-
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 
     /*
      * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index db2fdca..abd9046 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -15,15 +15,13 @@
  */
 
 .macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix
-// Called by managed code to allocate an object.
-TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an object of a resolved class.
-TWO_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an object of an initialized class.
-TWO_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an object when the caller doesn't know whether it has access
 // to the created type.
-TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check\c_suffix, artAllocObjectFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array.
 THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array of a resolve class.
@@ -61,14 +59,12 @@
 // Generate the allocation entrypoints for each allocator. This is used as an alternative to
 // GNERATE_ALL_ALLOC_ENTRYPOINTS for selectively implementing allocation fast paths in
 // hand-written assembly.
-#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(c_suffix, cxx_suffix) \
-  TWO_ARG_DOWNCALL art_quick_alloc_object ## c_suffix, artAllocObjectFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(c_suffix, cxx_suffix) \
-  TWO_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+  ONE_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(c_suffix, cxx_suffix) \
-  TWO_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+  ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
-  TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check ## c_suffix, artAllocObjectFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+  ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(c_suffix, cxx_suffix) \
   THREE_ARG_DOWNCALL art_quick_alloc_array ## c_suffix, artAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(c_suffix, cxx_suffix) \
@@ -93,8 +89,7 @@
 
 .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR
 // This is to be separately defined for each architecture to allow a hand-written assembly fast path.
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
@@ -109,8 +104,7 @@
 
 .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_TLAB_ALLOCATOR
 // This is to be separately defined for each architecture to allow a hand-written assembly fast path.
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
@@ -129,7 +123,6 @@
 .endm
 
 .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
@@ -142,7 +135,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
@@ -156,8 +148,7 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
 
 // This is to be separately defined for each architecture to allow a hand-written assembly fast path.
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc)
@@ -169,7 +160,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
@@ -182,7 +172,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
@@ -195,7 +184,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
@@ -208,7 +196,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
@@ -221,7 +208,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region)
@@ -234,7 +220,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
@@ -247,7 +232,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented)
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 9e385f8..ee65fa8 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1062,12 +1062,8 @@
 
   EXPECT_FALSE(self->IsExceptionPending());
   {
-    // Use an arbitrary method from c to use as referrer
-    size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex().index_),    // type_idx
-                            // arbitrary
-                            reinterpret_cast<size_t>(c->GetVirtualMethod(0, kRuntimePointerSize)),
-                            0U,
-                            StubTest::GetEntrypoint(self, kQuickAllocObject),
+    size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 0u, 0U,
+                            StubTest::GetEntrypoint(self, kQuickAllocObjectWithChecks),
                             self);
 
     EXPECT_FALSE(self->IsExceptionPending());
@@ -1078,8 +1074,6 @@
   }
 
   {
-    // We can use null in the second argument as we do not need a method here (not used in
-    // resolved/initialized cases)
     size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 0u, 0U,
                             StubTest::GetEntrypoint(self, kQuickAllocObjectResolved),
                             self);
@@ -1092,8 +1086,6 @@
   }
 
   {
-    // We can use null in the second argument as we do not need a method here (not used in
-    // resolved/initialized cases)
     size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 0u, 0U,
                             StubTest::GetEntrypoint(self, kQuickAllocObjectInitialized),
                             self);
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index c6f4c03..62c29cf 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -956,52 +956,42 @@
 // Generate the allocation entrypoints for each allocator.
 GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
-DEFINE_FUNCTION art_quick_alloc_object_rosalloc
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc).
+DEFINE_FUNCTION art_quick_alloc_object_resolved_rosalloc
     // Fast path rosalloc allocation.
-    // eax: uint32_t type_idx/return value, ecx: ArtMethod*
-    // ebx, edx: free
-    PUSH edi
-    movl ART_METHOD_DEX_CACHE_TYPES_OFFSET_32(%ecx), %edx  // Load dex cache resolved types array
-                                                        // Load the class (edx)
-    movl 0(%edx, %eax, COMPRESSED_REFERENCE_SIZE), %edx
-    testl %edx, %edx                                    // Check null class
-    jz   .Lart_quick_alloc_object_rosalloc_slow_path
-
+    // eax: type/return value
+    // ecx, ebx, edx: free
     movl %fs:THREAD_SELF_OFFSET, %ebx                   // ebx = thread
                                                         // Check if the thread local allocation
                                                         // stack has room
-    movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %edi
-    cmpl THREAD_LOCAL_ALLOC_STACK_END_OFFSET(%ebx), %edi
-    jae  .Lart_quick_alloc_object_rosalloc_slow_path
+    movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %ecx
+    cmpl THREAD_LOCAL_ALLOC_STACK_END_OFFSET(%ebx), %ecx
+    jae  .Lart_quick_alloc_object_resolved_rosalloc_slow_path
 
-    movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%edx), %edi  // Load the object size (edi)
+    movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%eax), %ecx  // Load the object size (ecx)
                                                         // Check if the size is for a thread
                                                         // local allocation. Also does the
                                                         // finalizable and initialization check.
-    cmpl LITERAL(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), %edi
-    ja   .Lart_quick_alloc_object_rosalloc_slow_path
-    shrl LITERAL(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT), %edi // Calculate the rosalloc bracket index
+    cmpl LITERAL(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), %ecx
+    ja   .Lart_quick_alloc_object_resolved_rosalloc_slow_path
+    shrl LITERAL(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT), %ecx // Calculate the rosalloc bracket index
                                                             // from object size.
                                                         // Load thread local rosalloc run (ebx)
                                                         // Subtract __SIZEOF_POINTER__ to subtract
                                                         // one from edi as there is no 0 byte run
                                                         // and the size is already aligned.
-    movl (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)(%ebx, %edi, __SIZEOF_POINTER__), %ebx
+    movl (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)(%ebx, %ecx, __SIZEOF_POINTER__), %ebx
                                                         // Load free_list head (edi),
                                                         // this will be the return value.
-    movl (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx), %edi
-    test %edi, %edi
-    jz   .Lart_quick_alloc_object_rosalloc_slow_path
+    movl (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx), %ecx
+    jecxz   .Lart_quick_alloc_object_resolved_rosalloc_slow_path
                                                         // Point of no slow path. Won't go to
-                                                        // the slow path from here on. Ok to
-                                                        // clobber eax and ecx.
-    movl %edi, %eax
+                                                        // the slow path from here on.
                                                         // Load the next pointer of the head
                                                         // and update head of free list with
                                                         // next pointer
-    movl ROSALLOC_SLOT_NEXT_OFFSET(%eax), %edi
-    movl %edi, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx)
+    movl ROSALLOC_SLOT_NEXT_OFFSET(%ecx), %edx
+    movl %edx, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%ebx)
                                                         // Decrement size of free list by 1
     decl (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)(%ebx)
                                                         // Store the class pointer in the
@@ -1011,141 +1001,104 @@
 #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
 #error "Class pointer needs to overwrite next pointer."
 #endif
-    POISON_HEAP_REF edx
-    movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%eax)
+    POISON_HEAP_REF eax
+    movl %eax, MIRROR_OBJECT_CLASS_OFFSET(%ecx)
     movl %fs:THREAD_SELF_OFFSET, %ebx                   // ebx = thread
                                                         // Push the new object onto the thread
                                                         // local allocation stack and
                                                         // increment the thread local
                                                         // allocation stack top.
-    movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %edi
-    movl %eax, (%edi)
-    addl LITERAL(COMPRESSED_REFERENCE_SIZE), %edi
-    movl %edi, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx)
+    movl THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx), %eax
+    movl %ecx, (%eax)
+    addl LITERAL(COMPRESSED_REFERENCE_SIZE), %eax
+    movl %eax, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%ebx)
                                                         // No fence needed for x86.
-    POP edi
+    movl %ecx, %eax                                     // Move object to return register
     ret
-.Lart_quick_alloc_object_rosalloc_slow_path:
-    POP edi
+.Lart_quick_alloc_object_resolved_rosalloc_slow_path:
     SETUP_SAVE_REFS_ONLY_FRAME ebx, ebx          // save ref containing registers for GC
     // Outgoing argument set up
-    PUSH eax                      // alignment padding
+    subl LITERAL(8), %esp                       // alignment padding
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    PUSH ecx
     PUSH eax
-    call SYMBOL(artAllocObjectFromCodeRosAlloc)  // cxx_name(arg0, arg1, Thread*)
+    call SYMBOL(artAllocObjectFromCodeResolvedRosAlloc)  // cxx_name(arg0, Thread*)
     addl LITERAL(16), %esp                       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_SAVE_REFS_ONLY_FRAME                 // restore frame up to return address
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER      // return or deliver exception
-END_FUNCTION art_quick_alloc_object_rosalloc
+END_FUNCTION art_quick_alloc_object_resolved_rosalloc
 
-// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
+// The common fast path code for art_quick_alloc_object_resolved_tlab
+// and art_quick_alloc_object_resolved_region_tlab.
 //
-// EAX: type_idx/return_value, ECX: ArtMethod*, EDX: the class.
-MACRO1(ALLOC_OBJECT_TLAB_FAST_PATH, slowPathLabel)
-    testl %edx, %edx                                    // Check null class
-    jz   VAR(slowPathLabel)
+// EAX: type/return_value
+MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH, slowPathLabel)
     movl %fs:THREAD_SELF_OFFSET, %ebx                   // ebx = thread
     movl THREAD_LOCAL_END_OFFSET(%ebx), %edi            // Load thread_local_end.
     subl THREAD_LOCAL_POS_OFFSET(%ebx), %edi            // Compute the remaining buffer size.
-    movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%edx), %esi  // Load the object size.
-    cmpl %edi, %esi                                     // Check if it fits.
+    movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%eax), %ecx  // Load the object size.
+    cmpl %edi, %ecx                                     // Check if it fits.
     ja   VAR(slowPathLabel)
-    movl THREAD_LOCAL_POS_OFFSET(%ebx), %eax            // Load thread_local_pos
+    movl THREAD_LOCAL_POS_OFFSET(%ebx), %edx            // Load thread_local_pos
                                                         // as allocated object.
-    addl %eax, %esi                                     // Add the object size.
-    movl %esi, THREAD_LOCAL_POS_OFFSET(%ebx)            // Update thread_local_pos.
+    addl %edx, %ecx                                     // Add the object size.
+    movl %ecx, THREAD_LOCAL_POS_OFFSET(%ebx)            // Update thread_local_pos.
     incl THREAD_LOCAL_OBJECTS_OFFSET(%ebx)              // Increase thread_local_objects.
                                                         // Store the class pointer in the header.
                                                         // No fence needed for x86.
-    POISON_HEAP_REF edx
-    movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%eax)
+    POISON_HEAP_REF eax
+    movl %eax, MIRROR_OBJECT_CLASS_OFFSET(%edx)
+    movl %edx, %eax
     POP edi
-    POP esi
     ret                                                 // Fast path succeeded.
 END_MACRO
 
-// The common slow path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
-MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name)
+// The common slow path code for art_quick_alloc_object_resolved_tlab
+// and art_quick_alloc_object_resolved_region_tlab.
+MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_SLOW_PATH, cxx_name)
     POP edi
-    POP esi
     SETUP_SAVE_REFS_ONLY_FRAME ebx, ebx                 // save ref containing registers for GC
     // Outgoing argument set up
-    PUSH eax                                            // alignment padding
+    subl LITERAL(8), %esp                               // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
     pushl %fs:THREAD_SELF_OFFSET                        // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    PUSH ecx
     PUSH eax
-    call CALLVAR(cxx_name)                              // cxx_name(arg0, arg1, Thread*)
+    call CALLVAR(cxx_name)                              // cxx_name(arg0, Thread*)
     addl LITERAL(16), %esp
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_SAVE_REFS_ONLY_FRAME                        // restore frame up to return address
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER             // return or deliver exception
 END_MACRO
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). May be called
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be called
 // for CC if the GC is not marking.
-DEFINE_FUNCTION art_quick_alloc_object_tlab
+DEFINE_FUNCTION art_quick_alloc_object_resolved_tlab
     // Fast path tlab allocation.
-    // EAX: uint32_t type_idx/return value, ECX: ArtMethod*.
-    // EBX, EDX: free.
-    PUSH esi
+    // EAX: type
+    // EBX, ECX, EDX: free.
     PUSH edi
-    movl ART_METHOD_DEX_CACHE_TYPES_OFFSET_32(%ecx), %edx   // Load dex cache resolved types array
-    // Might need to break down into multiple instructions to get the base address in a register.
-                                                            // Load the class
-    movl 0(%edx, %eax, COMPRESSED_REFERENCE_SIZE), %edx
-    ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path
-.Lart_quick_alloc_object_tlab_slow_path:
-    ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeTLAB
-END_FUNCTION art_quick_alloc_object_tlab
+    ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path
+.Lart_quick_alloc_object_resolved_tlab_slow_path:
+    ALLOC_OBJECT_RESOLVED_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedTLAB
+END_FUNCTION art_quick_alloc_object_resolved_tlab
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB).
-DEFINE_FUNCTION art_quick_alloc_object_region_tlab
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB).
+DEFINE_FUNCTION art_quick_alloc_object_resolved_region_tlab
     // Fast path region tlab allocation.
-    // EAX: uint32_t type_idx/return value, ECX: ArtMethod*.
-    // EBX, EDX: free.
+    // EAX: type/return value
+    // EBX, ECX, EDX: free.
 #if !defined(USE_READ_BARRIER)
     int3
     int3
 #endif
-    PUSH esi
     PUSH edi
-    movl ART_METHOD_DEX_CACHE_TYPES_OFFSET_32(%ecx), %edx   // Load dex cache resolved types array
-    // Might need to break down into multiple instructions to get the base address in a register.
-                                                            // Load the class
-    movl 0(%edx, %eax, COMPRESSED_REFERENCE_SIZE), %edx
-                                                            // Read barrier for class load.
-    cmpl LITERAL(0), %fs:THREAD_IS_GC_MARKING_OFFSET
-    jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
-    // Null check so that we can load the lock word.
-    testl %edx, %edx
-    jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
-    // Check the mark bit, if it is 1 return.
-    testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx)
-    jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path
-.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit:
-    ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path
-.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path:
-    // The read barrier slow path. Mark the class.
-    PUSH eax
-    PUSH ecx
-    // Outgoing argument set up
-    subl MACRO_LITERAL(8), %esp                             // Alignment padding
-    CFI_ADJUST_CFA_OFFSET(8)
-    PUSH edx                                                // Pass the class as the first param.
-    call SYMBOL(artReadBarrierMark)                         // cxx_name(mirror::Object* obj)
-    movl %eax, %edx
-    addl MACRO_LITERAL(12), %esp
-    CFI_ADJUST_CFA_OFFSET(-12)
-    POP ecx
-    POP eax
-    jmp .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
-.Lart_quick_alloc_object_region_tlab_slow_path:
-    ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeRegionTLAB
-END_FUNCTION art_quick_alloc_object_region_tlab
+    ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path
+.Lart_quick_alloc_object_resolved_region_tlab_slow_path:
+    ALLOC_OBJECT_RESOLVED_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedRegionTLAB
+END_FUNCTION art_quick_alloc_object_resolved_region_tlab
+
 
 DEFINE_FUNCTION art_quick_resolve_string
     SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 4c46b08..facd563 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -983,7 +983,6 @@
 
 // Comment out allocators that have x86_64 specific asm.
 // Region TLAB:
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
@@ -996,11 +995,9 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
 // Normal TLAB:
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
@@ -1009,29 +1006,25 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
-DEFINE_FUNCTION art_quick_alloc_object_rosalloc
+
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc).
+DEFINE_FUNCTION art_quick_alloc_object_resolved_rosalloc
     // Fast path rosalloc allocation.
-    // RDI: type_idx, RSI: ArtMethod*, RAX: return value
-    // RDX, RCX, R8, R9: free.
-    movq   ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx  // Load dex cache resolved types array
-                                                             // Load the class (edx)
-    movl   0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
-    testl  %edx, %edx                                      // Check null class
-    jz     .Lart_quick_alloc_object_rosalloc_slow_path
+    // RDI: mirror::Class*, RAX: return value
+    // RSI, RDX, RCX, R8, R9: free.
                                                            // Check if the thread local
                                                            // allocation stack has room.
     movq   %gs:THREAD_SELF_OFFSET, %r8                     // r8 = thread
     movq   THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET(%r8), %rcx  // rcx = alloc stack top.
     cmpq   THREAD_LOCAL_ALLOC_STACK_END_OFFSET(%r8), %rcx
-    jae    .Lart_quick_alloc_object_rosalloc_slow_path
+    jae    .Lart_quick_alloc_object_resolved_rosalloc_slow_path
                                                            // Load the object size
-    movl   MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdx), %eax
+    movl   MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdi), %eax
                                                            // Check if the size is for a thread
                                                            // local allocation. Also does the
                                                            // initialized and finalizable checks.
     cmpl   LITERAL(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), %eax
-    ja     .Lart_quick_alloc_object_rosalloc_slow_path
+    ja     .Lart_quick_alloc_object_resolved_rosalloc_slow_path
                                                            // Compute the rosalloc bracket index
                                                            // from the size.
     shrq   LITERAL(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT), %rax
@@ -1045,7 +1038,7 @@
                                                            // will be the return val.
     movq   (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)(%r9), %rax
     testq  %rax, %rax
-    jz     .Lart_quick_alloc_object_rosalloc_slow_path
+    jz     .Lart_quick_alloc_object_resolved_rosalloc_slow_path
     // "Point of no slow path". Won't go to the slow path from here on. OK to clobber rdi and rsi.
                                                            // Push the new object onto the thread
                                                            // local allocation stack and
@@ -1066,17 +1059,17 @@
 #if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
 #error "Class pointer needs to overwrite next pointer."
 #endif
-    POISON_HEAP_REF edx
-    movl   %edx, MIRROR_OBJECT_CLASS_OFFSET(%rax)
+    POISON_HEAP_REF edi
+    movl   %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax)
                                                            // Decrement the size of the free list
     decl   (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)(%r9)
                                                            // No fence necessary for x86.
     ret
-.Lart_quick_alloc_object_rosalloc_slow_path:
+.Lart_quick_alloc_object_resolved_rosalloc_slow_path:
     SETUP_SAVE_REFS_ONLY_FRAME                             // save ref containing registers for GC
     // Outgoing argument set up
-    movq %gs:THREAD_SELF_OFFSET, %rdx                      // pass Thread::Current()
-    call SYMBOL(artAllocObjectFromCodeRosAlloc)            // cxx_name(arg0, arg1, Thread*)
+    movq %gs:THREAD_SELF_OFFSET, %rsi                      // pass Thread::Current()
+    call SYMBOL(artAllocObjectFromCodeResolvedRosAlloc)    // cxx_name(arg0, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME                           // restore frame up to return address
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER                // return or deliver exception
 END_FUNCTION art_quick_alloc_object_rosalloc
@@ -1095,19 +1088,19 @@
 // TODO: delete ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH since it is the same as
 // ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH.
 //
-// RDI: type_idx, RSI: ArtMethod*, RDX/EDX: the class, RAX: return value.
-// RCX: scratch, r8: Thread::Current().
+// RDI: the class, RAX: return value.
+// RCX, RSI, RDX: scratch, r8: Thread::Current().
 MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH, slowPathLabel)
     ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH(RAW_VAR(slowPathLabel))
 END_MACRO
 
 // The fast path code for art_quick_alloc_object_initialized_region_tlab.
 //
-// RDI: type_idx, RSI: ArtMethod*, RDX/EDX: the class, RAX: return value.
-// RCX: scratch, r8: Thread::Current().
+// RDI: the class, RSI: ArtMethod*, RAX: return value.
+// RCX, RSI, RDX: scratch, r8: Thread::Current().
 MACRO1(ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH, slowPathLabel)
     movq %gs:THREAD_SELF_OFFSET, %r8                           // r8 = thread
-    movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdx), %ecx // Load the object size.
+    movl MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET(%rdi), %ecx // Load the object size.
     movq THREAD_LOCAL_POS_OFFSET(%r8), %rax
     addq %rax, %rcx                                            // Add size to pos, note that these
                                                                // are both 32 bit ints, overflow
@@ -1120,8 +1113,8 @@
                                                                // Store the class pointer in the
                                                                // header.
                                                                // No fence needed for x86.
-    POISON_HEAP_REF edx
-    movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%rax)
+    POISON_HEAP_REF edi
+    movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax)
     ret                                                        // Fast path succeeded.
 END_MACRO
 
@@ -1164,12 +1157,14 @@
     ret                                                        // Fast path succeeded.
 END_MACRO
 
-// The common slow path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
+
+// The common slow path code for art_quick_alloc_object_{resolved, initialized}_tlab
+// and art_quick_alloc_object_{resolved, initialized}_region_tlab.
 MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name)
     SETUP_SAVE_REFS_ONLY_FRAME                             // save ref containing registers for GC
     // Outgoing argument set up
-    movq %gs:THREAD_SELF_OFFSET, %rdx                      // pass Thread::Current()
-    call CALLVAR(cxx_name)                                 // cxx_name(arg0, arg1, Thread*)
+    movq %gs:THREAD_SELF_OFFSET, %rsi                      // pass Thread::Current()
+    call CALLVAR(cxx_name)                                 // cxx_name(arg0, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME                           // restore frame up to return address
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER                // return or deliver exception
 END_MACRO
@@ -1184,26 +1179,11 @@
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER                    // return or deliver exception
 END_MACRO
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). May be
-// called with CC if the GC is not active.
-DEFINE_FUNCTION art_quick_alloc_object_tlab
-    // RDI: uint32_t type_idx, RSI: ArtMethod*
-    // RDX, RCX, R8, R9: free. RAX: return val.
-    movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx  // Load dex cache resolved types array
-    // Might need to break down into multiple instructions to get the base address in a register.
-                                                               // Load the class
-    movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
-    ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path
-.Lart_quick_alloc_object_tlab_slow_path:
-    ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeTLAB
-END_FUNCTION art_quick_alloc_object_tlab
-
 // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be
 // called with CC if the GC is not active.
 DEFINE_FUNCTION art_quick_alloc_object_resolved_tlab
-    // RDI: mirror::Class* klass, RSI: ArtMethod*
-    // RDX, RCX, R8, R9: free. RAX: return val.
-    movq %rdi, %rdx
+    // RDI: mirror::Class* klass
+    // RDX, RSI, RCX, R8, R9: free. RAX: return val.
     ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path
 .Lart_quick_alloc_object_resolved_tlab_slow_path:
     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedTLAB
@@ -1212,9 +1192,8 @@
 // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB).
 // May be called with CC if the GC is not active.
 DEFINE_FUNCTION art_quick_alloc_object_initialized_tlab
-    // RDI: mirror::Class* klass, RSI: ArtMethod*
-    // RDX, RCX, R8, R9: free. RAX: return val.
-    movq %rdi, %rdx
+    // RDI: mirror::Class* klass
+    // RDX, RSI, RCX, R8, R9: free. RAX: return val.
     ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_tlab_slow_path
 .Lart_quick_alloc_object_initialized_tlab_slow_path:
     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedTLAB
@@ -1292,49 +1271,12 @@
     ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedRegionTLAB
 END_FUNCTION art_quick_alloc_array_resolved_region_tlab
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB).
-DEFINE_FUNCTION art_quick_alloc_object_region_tlab
-    // Fast path region tlab allocation.
-    // RDI: uint32_t type_idx, RSI: ArtMethod*
-    // RDX, RCX, R8, R9: free. RAX: return val.
-    ASSERT_USE_READ_BARRIER
-    movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx  // Load dex cache resolved types array
-    movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx    // Load the class
-    // Null check so that we can load the lock word.
-    testl %edx, %edx
-    jz .Lart_quick_alloc_object_region_tlab_slow_path
-    // Since we have allocation entrypoint switching, we know the GC is marking.
-    // Check the mark bit, if it is 0, do the read barrier mark.
-    testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx)
-    jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path
-.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit:
-    // Use resolved one since we already did the null check.
-    ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path
-.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path:
-    // The read barrier slow path. Mark the class.
-    PUSH rdi
-    PUSH rsi
-    subq LITERAL(8), %rsp // 16 byte alignment
-    // Outgoing argument set up
-    movq %rdx, %rdi                                            // Pass the class as the first param.
-    call SYMBOL(artReadBarrierMark)                            // cxx_name(mirror::Object* obj)
-    movq %rax, %rdx
-    addq LITERAL(8), %rsp
-    POP rsi
-    POP rdi
-    jmp .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
-.Lart_quick_alloc_object_region_tlab_slow_path:
-    ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeRegionTLAB
-END_FUNCTION art_quick_alloc_object_region_tlab
-
 // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB).
 DEFINE_FUNCTION art_quick_alloc_object_resolved_region_tlab
     // Fast path region tlab allocation.
-    // RDI: mirror::Class* klass, RSI: ArtMethod*
-    // RDX, RCX, R8, R9: free. RAX: return val.
+    // RDI: mirror::Class* klass
+    // RDX, RSI, RCX, R8, R9: free. RAX: return val.
     ASSERT_USE_READ_BARRIER
-    // No read barrier since the caller is responsible for that.
-    movq %rdi, %rdx
     ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path
 .Lart_quick_alloc_object_resolved_region_tlab_slow_path:
     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedRegionTLAB
@@ -1343,10 +1285,9 @@
 // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB).
 DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab
     // Fast path region tlab allocation.
-    // RDI: mirror::Class* klass, RSI: ArtMethod*
-    // RDX, RCX, R8, R9: free. RAX: return val.
+    // RDI: mirror::Class* klass
+    // RDX, RSI, RCX, R8, R9: free. RAX: return val.
     ASSERT_USE_READ_BARRIER
-    movq %rdi, %rdx
     // No read barrier since the caller is responsible for that.
     ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_region_tlab_slow_path
 .Lart_quick_alloc_object_initialized_region_tlab_slow_path:
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index e4972da..bfdddf7 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -98,7 +98,7 @@
 ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET,
             art::Thread::ThreadLocalEndOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.thread_local_objects.
-#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + 2 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
             art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.mterp_current_ibase.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5b8d4e4..8586b78 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4280,9 +4280,10 @@
 
 void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
   // Create constructor for Proxy that must initialize the method.
-  CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u);
+  CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 23u);
+
   ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked(
-      2, image_pointer_size_);
+      8, image_pointer_size_);
   DCHECK_EQ(std::string(proxy_constructor->GetName()), "<init>");
   // Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
   // constructor method.
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 14c9c21..469c45c 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -127,43 +127,21 @@
       self->GetManagedStack()->GetTopQuickFrame(), type, true /* do_caller_check */);
 }
 
-template <const bool kAccessCheck>
-ALWAYS_INLINE
-inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx,
-                                       ArtMethod* method,
-                                       Thread* self,
-                                       bool* slow_path) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  PointerSize pointer_size = class_linker->GetImagePointerSize();
-  mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
-  if (UNLIKELY(klass == nullptr)) {
-    klass = class_linker->ResolveType(type_idx, method);
+ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(mirror::Class* klass,
+                                                     Thread* self,
+                                                     bool* slow_path)
+    REQUIRES_SHARED(Locks::mutator_lock_)
+    REQUIRES(!Roles::uninterruptible_) {
+  if (UNLIKELY(!klass->IsInstantiable())) {
+    self->ThrowNewException("Ljava/lang/InstantiationError;", klass->PrettyDescriptor().c_str());
     *slow_path = true;
-    if (klass == nullptr) {
-      DCHECK(self->IsExceptionPending());
-      return nullptr;  // Failure
-    } else {
-      DCHECK(!self->IsExceptionPending());
-    }
+    return nullptr;  // Failure
   }
-  if (kAccessCheck) {
-    if (UNLIKELY(!klass->IsInstantiable())) {
-      self->ThrowNewException("Ljava/lang/InstantiationError;", klass->PrettyDescriptor().c_str());
-      *slow_path = true;
-      return nullptr;  // Failure
-    }
-    if (UNLIKELY(klass->IsClassClass())) {
-      ThrowIllegalAccessError(nullptr, "Class %s is inaccessible",
-                              klass->PrettyDescriptor().c_str());
-      *slow_path = true;
-      return nullptr;  // Failure
-    }
-    mirror::Class* referrer = method->GetDeclaringClass();
-    if (UNLIKELY(!referrer->CanAccess(klass))) {
-      ThrowIllegalAccessErrorClass(referrer, klass);
-      *slow_path = true;
-      return nullptr;  // Failure
-    }
+  if (UNLIKELY(klass->IsClassClass())) {
+    ThrowIllegalAccessError(nullptr, "Class %s is inaccessible",
+                            klass->PrettyDescriptor().c_str());
+    *slow_path = true;
+    return nullptr;  // Failure
   }
   if (UNLIKELY(!klass->IsInitialized())) {
     StackHandleScope<1> hs(self);
@@ -191,7 +169,9 @@
 ALWAYS_INLINE
 inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass,
                                                           Thread* self,
-                                                          bool* slow_path) {
+                                                          bool* slow_path)
+    REQUIRES_SHARED(Locks::mutator_lock_)
+    REQUIRES(!Roles::uninterruptible_) {
   if (UNLIKELY(!klass->IsInitialized())) {
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> h_class(hs.NewHandle(klass));
@@ -213,18 +193,15 @@
   return klass;
 }
 
-// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
-// cannot be resolved, throw an error. If it can, use it to create an instance.
-// When verification/compiler hasn't been able to verify access, optionally perform an access
-// check.
-template <bool kAccessCheck, bool kInstrumented>
+// Allocate an instance of klass. Throws InstantationError if klass is not instantiable,
+// or IllegalAccessError if klass is j.l.Class. Performs a clinit check too.
+template <bool kInstrumented>
 ALWAYS_INLINE
-inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx,
-                                           ArtMethod* method,
+inline mirror::Object* AllocObjectFromCode(mirror::Class* klass,
                                            Thread* self,
                                            gc::AllocatorType allocator_type) {
   bool slow_path = false;
-  mirror::Class* klass = CheckObjectAlloc<kAccessCheck>(type_idx, method, self, &slow_path);
+  klass = CheckObjectAlloc(klass, self, &slow_path);
   if (UNLIKELY(slow_path)) {
     if (klass == nullptr) {
       return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 7cc136e..4794610 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -45,27 +45,10 @@
 class ScopedObjectAccessAlreadyRunnable;
 class Thread;
 
-template <const bool kAccessCheck>
-ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx,
-                                                     ArtMethod* method,
-                                                     Thread* self,
-                                                     bool* slow_path)
-    REQUIRES_SHARED(Locks::mutator_lock_)
-    REQUIRES(!Roles::uninterruptible_);
-
-ALWAYS_INLINE inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass,
-                                                                        Thread* self,
-                                                                        bool* slow_path)
-    REQUIRES_SHARED(Locks::mutator_lock_)
-    REQUIRES(!Roles::uninterruptible_);
-
 // Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
 // cannot be resolved, throw an error. If it can, use it to create an instance.
-// When verification/compiler hasn't been able to verify access, optionally perform an access
-// check.
-template <bool kAccessCheck, bool kInstrumented>
-ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx,
-                                                         ArtMethod* method,
+template <bool kInstrumented>
+ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(mirror::Class* klass,
                                                          Thread* self,
                                                          gc::AllocatorType allocator_type)
     REQUIRES_SHARED(Locks::mutator_lock_)
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 82bb8e5..2d06508 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -29,87 +29,58 @@
 
 static constexpr bool kUseTlabFastPath = true;
 
+template <bool kInitialized,
+          bool kFinalize,
+          bool kInstrumented,
+          gc::AllocatorType allocator_type>
+static ALWAYS_INLINE inline mirror::Object* artAllocObjectFromCode(
+    mirror::Class* klass,
+    Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  DCHECK(klass != nullptr);
+  if (kUseTlabFastPath && !kInstrumented && allocator_type == gc::kAllocatorTypeTLAB) {
+    if (kInitialized || klass->IsInitialized()) {
+      if (!kFinalize || !klass->IsFinalizable()) {
+        size_t byte_count = klass->GetObjectSize();
+        byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment);
+        mirror::Object* obj;
+        if (LIKELY(byte_count < self->TlabSize())) {
+          obj = self->AllocTlab(byte_count);
+          DCHECK(obj != nullptr) << "AllocTlab can't fail";
+          obj->SetClass(klass);
+          if (kUseBakerReadBarrier) {
+            obj->AssertReadBarrierState();
+          }
+          QuasiAtomic::ThreadFenceForConstructor();
+          return obj;
+        }
+      }
+    }
+  }
+  if (kInitialized) {
+    return AllocObjectFromCodeInitialized<kInstrumented>(klass, self, allocator_type);
+  } else if (!kFinalize) {
+    return AllocObjectFromCodeResolved<kInstrumented>(klass, self, allocator_type);
+  } else {
+    return AllocObjectFromCode<kInstrumented>(klass, self, allocator_type);
+  }
+}
+
 #define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR_INST(suffix, suffix2, instrumented_bool, allocator_type) \
-extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \
-    uint32_t type_idx, ArtMethod* method, Thread* self) \
+extern "C" mirror::Object* artAllocObjectFromCodeWithChecks##suffix##suffix2( \
+    mirror::Class* klass, Thread* self) \
     REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \
-    mirror::Class* klass = method->GetDexCacheResolvedType<false>(dex::TypeIndex(type_idx), \
-                                                                  kRuntimePointerSize); \
-    if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \
-      size_t byte_count = klass->GetObjectSize(); \
-      byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
-      mirror::Object* obj; \
-      if (LIKELY(byte_count < self->TlabSize())) { \
-        obj = self->AllocTlab(byte_count); \
-        DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
-        obj->SetClass(klass); \
-        if (kUseBakerReadBarrier) { \
-          obj->AssertReadBarrierState(); \
-        } \
-        QuasiAtomic::ThreadFenceForConstructor(); \
-        return obj; \
-      } \
-    } \
-  } \
-  return AllocObjectFromCode<false, instrumented_bool>(dex::TypeIndex(type_idx), \
-                                                       method, \
-                                                       self, \
-                                                       allocator_type); \
+  return artAllocObjectFromCode<false, true, instrumented_bool, allocator_type>(klass, self); \
 } \
 extern "C" mirror::Object* artAllocObjectFromCodeResolved##suffix##suffix2( \
-    mirror::Class* klass, ArtMethod* method ATTRIBUTE_UNUSED, Thread* self) \
+    mirror::Class* klass, Thread* self) \
     REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \
-    if (LIKELY(klass->IsInitialized())) { \
-      size_t byte_count = klass->GetObjectSize(); \
-      byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
-      mirror::Object* obj; \
-      if (LIKELY(byte_count < self->TlabSize())) { \
-        obj = self->AllocTlab(byte_count); \
-        DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
-        obj->SetClass(klass); \
-        if (kUseBakerReadBarrier) { \
-          obj->AssertReadBarrierState(); \
-        } \
-        QuasiAtomic::ThreadFenceForConstructor(); \
-        return obj; \
-      } \
-    } \
-  } \
-  return AllocObjectFromCodeResolved<instrumented_bool>(klass, self, allocator_type); \
+  return artAllocObjectFromCode<false, false, instrumented_bool, allocator_type>(klass, self); \
 } \
 extern "C" mirror::Object* artAllocObjectFromCodeInitialized##suffix##suffix2( \
-    mirror::Class* klass, ArtMethod* method ATTRIBUTE_UNUSED, Thread* self) \
+    mirror::Class* klass, Thread* self) \
     REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \
-    size_t byte_count = klass->GetObjectSize(); \
-    byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
-    mirror::Object* obj; \
-    if (LIKELY(byte_count < self->TlabSize())) { \
-      obj = self->AllocTlab(byte_count); \
-      DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
-      obj->SetClass(klass); \
-      if (kUseBakerReadBarrier) { \
-        obj->AssertReadBarrierState(); \
-      } \
-      QuasiAtomic::ThreadFenceForConstructor(); \
-      return obj; \
-    } \
-  } \
-  return AllocObjectFromCodeInitialized<instrumented_bool>(klass, self, allocator_type); \
-} \
-extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, ArtMethod* method, Thread* self) \
-    REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  return AllocObjectFromCode<true, instrumented_bool>(dex::TypeIndex(type_idx), \
-                                                      method, \
-                                                      self, \
-                                                      allocator_type); \
+  return artAllocObjectFromCode<true, false, instrumented_bool, allocator_type>(klass, self); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \
     uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
@@ -220,10 +191,9 @@
 extern "C" void* art_quick_alloc_array##suffix(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, int32_t, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_object##suffix(uint32_t type_idx, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass); \
+extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass); \
+extern "C" void* art_quick_alloc_object_with_checks##suffix(mirror::Class* klass); \
 extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_string_from_bytes##suffix(void*, int32_t, int32_t, int32_t); \
@@ -233,9 +203,9 @@
 extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirror::Class* klass, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass); \
+extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirror::Class* klass); \
+extern "C" void* art_quick_alloc_object_with_checks##suffix##_instrumented(mirror::Class* klass); \
 extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_string_from_bytes##suffix##_instrumented(void*, int32_t, int32_t, int32_t); \
@@ -246,10 +216,9 @@
     qpoints->pAllocArray = art_quick_alloc_array##suffix##_instrumented; \
     qpoints->pAllocArrayResolved = art_quick_alloc_array_resolved##suffix##_instrumented; \
     qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix##_instrumented; \
-    qpoints->pAllocObject = art_quick_alloc_object##suffix##_instrumented; \
     qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \
     qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \
-    qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix##_instrumented; \
+    qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \
     qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \
     qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \
     qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \
@@ -259,10 +228,9 @@
     qpoints->pAllocArray = art_quick_alloc_array##suffix; \
     qpoints->pAllocArrayResolved = art_quick_alloc_array_resolved##suffix; \
     qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix; \
-    qpoints->pAllocObject = art_quick_alloc_object##suffix; \
     qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \
     qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \
-    qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix; \
+    qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \
     qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \
     qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \
     qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index a1c5082..0911aeb 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -23,10 +23,9 @@
   V(AllocArray, void*, uint32_t, int32_t, ArtMethod*) \
   V(AllocArrayResolved, void*, mirror::Class*, int32_t, ArtMethod*) \
   V(AllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
-  V(AllocObject, void*, uint32_t, ArtMethod*) \
-  V(AllocObjectResolved, void*, mirror::Class*, ArtMethod*) \
-  V(AllocObjectInitialized, void*, mirror::Class*, ArtMethod*) \
-  V(AllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*) \
+  V(AllocObjectResolved, void*, mirror::Class*) \
+  V(AllocObjectInitialized, void*, mirror::Class*) \
+  V(AllocObjectWithChecks, void*, mirror::Class*) \
   V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \
   V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
   V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 1283660..6866abb 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -122,9 +122,9 @@
 
     // Skip across the entrypoints structures.
 
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_start, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_objects, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(size_t));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*));
@@ -156,13 +156,13 @@
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArray, pAllocArrayResolved, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayResolved, pAllocArrayWithAccessCheck,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayWithAccessCheck, pAllocObject, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObject, pAllocObjectResolved, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayWithAccessCheck, pAllocObjectResolved,
+                         sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectResolved, pAllocObjectInitialized,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithAccessCheck,
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithAccessCheck, pCheckAndAllocArray,
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray,
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck,
                          sizeof(void*));
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index b889913..e1117e6 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -58,7 +58,7 @@
                                      bool measure_read_barrier_slow_path)
     : GarbageCollector(heap,
                        name_prefix + (name_prefix.empty() ? "" : " ") +
-                       "concurrent copying + mark sweep"),
+                       "concurrent copying"),
       region_space_(nullptr), gc_barrier_(new Barrier(0)),
       gc_mark_stack_(accounting::ObjectStack::Create("concurrent copying gc mark stack",
                                                      kDefaultGcMarkStackSize,
@@ -832,7 +832,6 @@
       static constexpr uint64_t kEmptyCheckpointTimeoutMs = 600 * 1000;  // 10 minutes.
       bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointTimeoutMs);
       if (timed_out) {
-        Runtime* runtime = Runtime::Current();
         std::ostringstream ss;
         ss << "Empty checkpoint timeout\n";
         ss << "Barrier count " << barrier->GetCount(self) << "\n";
@@ -843,8 +842,30 @@
         ss << "\n";
         Locks::mutator_lock_->Dump(ss);
         ss << "\n";
-        runtime->GetThreadList()->Dump(ss);
-        LOG(FATAL) << ss.str();
+        LOG(FATAL_WITHOUT_ABORT) << ss.str();
+        // Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
+        // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
+        {
+          ReaderMutexLock mu0(self, *Locks::mutator_lock_);
+          MutexLock mu1(self, *Locks::thread_list_lock_);
+          for (Thread* thread : thread_list->GetList()) {
+            uint32_t tid = thread->GetThreadId();
+            bool is_in_runnable_thread_ids =
+                std::find(runnable_thread_ids.begin(), runnable_thread_ids.end(), tid) !=
+                runnable_thread_ids.end();
+            if (is_in_runnable_thread_ids &&
+                thread->ReadFlag(kEmptyCheckpointRequest)) {
+              // Found a runnable thread that hasn't responded to the empty checkpoint request.
+              // Assume it's stuck and safe to dump its stack.
+              thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+            }
+          }
+        }
+        LOG(FATAL_WITHOUT_ABORT)
+            << "Dumped runnable threads that haven't responded to empty checkpoint.";
+        // Now use ThreadList::Dump() to dump more threads, noting it may get stuck.
+        thread_list->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+        LOG(FATAL) << "Dumped all threads.";
       }
     } else {
       barrier->Increment(self, barrier_count);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index a815b83..f2aa5a7 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -89,7 +89,7 @@
 
 SemiSpace::SemiSpace(Heap* heap, bool generational, const std::string& name_prefix)
     : GarbageCollector(heap,
-                       name_prefix + (name_prefix.empty() ? "" : " ") + "marksweep + semispace"),
+                       name_prefix + (name_prefix.empty() ? "" : " ") + "semispace"),
       mark_stack_(nullptr),
       is_large_object_space_immune_(false),
       to_space_(nullptr),
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index b0d7fb2..d7dfcd4 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -508,9 +508,8 @@
             gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
             obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
           } else {
-            obj = AllocObjectFromCode<do_access_check, true>(
-                dex::TypeIndex(inst->VRegB_21c()),
-                shadow_frame.GetMethod(),
+            obj = AllocObjectFromCode<true>(
+                c.Ptr(),
                 self,
                 Runtime::Current()->GetHeap()->GetCurrentAllocator());
           }
diff --git a/runtime/interpreter/mterp/arm64/footer.S b/runtime/interpreter/mterp/arm64/footer.S
index 6ffbd3f..388fc8d 100644
--- a/runtime/interpreter/mterp/arm64/footer.S
+++ b/runtime/interpreter/mterp/arm64/footer.S
@@ -267,13 +267,7 @@
     b MterpDone
 MterpReturn:
     ldr     x2, [xFP, #OFF_FP_RESULT_REGISTER]
-    ldr     lr, [xSELF, #THREAD_FLAGS_OFFSET]
     str     x0, [x2]
-    mov     x0, xSELF
-    ands    lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
-    b.eq    check2
-    bl      MterpSuspendCheck                       // (self)
-check2:
     mov     x0, #1                                  // signal return to caller.
 MterpDone:
 /*
diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S
index 64772c8..312fa9c 100644
--- a/runtime/interpreter/mterp/mips64/footer.S
+++ b/runtime/interpreter/mterp/mips64/footer.S
@@ -222,13 +222,7 @@
  */
 MterpReturn:
     ld      a2, OFF_FP_RESULT_REGISTER(rFP)
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)
     sd      a0, 0(a2)
-    move    a0, rSELF
-    and     ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
-    beqzc   ra, check2
-    jal     MterpSuspendCheck                       # (self)
-check2:
     li      v0, 1                                   # signal return to caller.
 MterpDone:
 /*
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index c8c1563..369c261 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -375,10 +375,9 @@
       gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
       obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
     } else {
-      obj = AllocObjectFromCode<false, true>(dex::TypeIndex(inst->VRegB_21c()),
-                                             shadow_frame->GetMethod(),
-                                             self,
-                                             Runtime::Current()->GetHeap()->GetCurrentAllocator());
+      obj = AllocObjectFromCode<true>(c,
+                                      self,
+                                      Runtime::Current()->GetHeap()->GetCurrentAllocator());
     }
   }
   if (UNLIKELY(obj == nullptr)) {
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index 34d99a8..681790d 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -7246,13 +7246,7 @@
     b MterpDone
 MterpReturn:
     ldr     x2, [xFP, #OFF_FP_RESULT_REGISTER]
-    ldr     lr, [xSELF, #THREAD_FLAGS_OFFSET]
     str     x0, [x2]
-    mov     x0, xSELF
-    ands    lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
-    b.eq    check2
-    bl      MterpSuspendCheck                       // (self)
-check2:
     mov     x0, #1                                  // signal return to caller.
 MterpDone:
 /*
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 037787f..bf09666 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -12293,13 +12293,7 @@
  */
 MterpReturn:
     ld      a2, OFF_FP_RESULT_REGISTER(rFP)
-    lw      ra, THREAD_FLAGS_OFFSET(rSELF)
     sd      a0, 0(a2)
-    move    a0, rSELF
-    and     ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
-    beqzc   ra, check2
-    jal     MterpSuspendCheck                       # (self)
-check2:
     li      v0, 1                                   # signal return to caller.
 MterpDone:
 /*
diff --git a/runtime/oat.cc b/runtime/oat.cc
index cebe765..1a07cdc 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -469,10 +469,6 @@
   return IsKeyEnabled(OatHeader::kPicKey);
 }
 
-bool OatHeader::HasPatchInfo() const {
-  return IsKeyEnabled(OatHeader::kHasPatchInfoKey);
-}
-
 bool OatHeader::IsDebuggable() const {
   return IsKeyEnabled(OatHeader::kDebuggableKey);
 }
diff --git a/runtime/oat.h b/runtime/oat.h
index 0f4cbbb..dc103e2 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,13 +32,12 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '9', '3', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '9', '5', '\0' };  // alloc entrypoints change
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDex2OatHostKey = "dex2oat-host";
   static constexpr const char* kPicKey = "pic";
-  static constexpr const char* kHasPatchInfoKey = "has-patch-info";
   static constexpr const char* kDebuggableKey = "debuggable";
   static constexpr const char* kNativeDebuggableKey = "native-debuggable";
   static constexpr const char* kCompilerFilter = "compiler-filter";
@@ -110,7 +109,6 @@
 
   size_t GetHeaderSize() const;
   bool IsPic() const;
-  bool HasPatchInfo() const;
   bool IsDebuggable() const;
   bool IsNativeDebuggable() const;
   CompilerFilter::Filter GetCompilerFilter() const;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 0bf7136..38df427 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -710,7 +710,7 @@
       return false;
     }
 #ifdef ART_TARGET_ANDROID
-    android_dlextinfo extinfo;
+    android_dlextinfo extinfo = {};
     extinfo.flags = ANDROID_DLEXT_FORCE_LOAD |                  // Force-load, don't reuse handle
                                                                 //   (open oat files multiple
                                                                 //    times).
@@ -1438,10 +1438,6 @@
   method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
 }
 
-bool OatFile::HasPatchInfo() const {
-  return GetOatHeader().HasPatchInfo();
-}
-
 bool OatFile::IsPic() const {
   return GetOatHeader().IsPic();
   // TODO: Check against oat_patches. b/18144996
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 29add5b..62d99fb 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -104,8 +104,6 @@
     return is_executable_;
   }
 
-  bool HasPatchInfo() const;
-
   bool IsPic() const;
 
   // Indicates whether the oat file was compiled with full debugging capability.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index ee7cf9d..f12a5e7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -203,10 +203,6 @@
     case kDex2OatForRelocation:
     case kDex2OatForFilter:
       return GenerateOatFile(error_msg);
-
-    case kPatchoatForRelocation: {
-      return RelocateOatFile(info.Filename(), error_msg);
-    }
   }
   UNREACHABLE();
 }
@@ -420,58 +416,6 @@
 }
 
 OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::RelocateOatFile(const std::string* input_file, std::string* error_msg) {
-  CHECK(error_msg != nullptr);
-
-  if (input_file == nullptr) {
-    *error_msg = "Patching of oat file for dex location " + dex_location_
-      + " not attempted because the input file name could not be determined.";
-    return kUpdateNotAttempted;
-  }
-  const std::string& input_file_name = *input_file;
-
-  if (oat_.Filename() == nullptr) {
-    *error_msg = "Patching of oat file for dex location " + dex_location_
-      + " not attempted because the oat file name could not be determined.";
-    return kUpdateNotAttempted;
-  }
-  const std::string& oat_file_name = *oat_.Filename();
-
-  const ImageInfo* image_info = GetImageInfo();
-  Runtime* runtime = Runtime::Current();
-  if (image_info == nullptr) {
-    *error_msg = "Patching of oat file " + oat_file_name
-      + " not attempted because no image location was found.";
-    return kUpdateNotAttempted;
-  }
-
-  if (!runtime->IsDex2OatEnabled()) {
-    *error_msg = "Patching of oat file " + oat_file_name
-      + " not attempted because dex2oat is disabled";
-    return kUpdateNotAttempted;
-  }
-
-  std::vector<std::string> argv;
-  argv.push_back(runtime->GetPatchoatExecutable());
-  argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(isa_)));
-  argv.push_back("--input-oat-file=" + input_file_name);
-  argv.push_back("--output-oat-file=" + oat_file_name);
-  argv.push_back("--patched-image-location=" + image_info->location);
-
-  std::string command_line(android::base::Join(argv, ' '));
-  if (!Exec(argv, error_msg)) {
-    // Manually delete the file. This ensures there is no garbage left over if
-    // the process unexpectedly died.
-    unlink(oat_file_name.c_str());
-    return kUpdateFailed;
-  }
-
-  // Mark that the oat file has changed and we should try to reload.
-  oat_.Reset();
-  return kUpdateSucceeded;
-}
-
-OatFileAssistant::ResultOfAttemptToUpdate
 OatFileAssistant::GenerateOatFile(std::string* error_msg) {
   CHECK(error_msg != nullptr);
 
@@ -852,13 +796,7 @@
     return kNoDexOptNeeded;
   }
 
-  if (filter_okay && Status() == kOatRelocationOutOfDate && HasPatchInfo()) {
-    return kPatchoatForRelocation;
-  }
-
   if (oat_file_assistant_->HasOriginalDexFiles()) {
-    // Run dex2oat for relocation if we didn't have the patch info necessary
-    // to use patchoat.
     if (filter_okay && Status() == kOatRelocationOutOfDate) {
       return kDex2OatForRelocation;
     }
@@ -921,11 +859,6 @@
   return (file != nullptr && file->IsExecutable());
 }
 
-bool OatFileAssistant::OatFileInfo::HasPatchInfo() {
-  const OatFile* file = GetFile();
-  return (file != nullptr && file->HasPatchInfo());
-}
-
 void OatFileAssistant::OatFileInfo::Reset() {
   load_attempted_ = false;
   file_.reset();
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index bed1edc..588a698 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -67,14 +67,9 @@
     kDex2OatForFilter = 3,
 
     // dex2oat should be run to update the apk/jar because the existing code
-    // is not relocated to match the boot image and does not have the
-    // necessary patch information to use patchoat.
+    // is not relocated to match the boot image.
     // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_RELOCATION
     kDex2OatForRelocation = 4,
-
-    // patchoat should be run to update the apk/jar.
-    // Matches Java: dalvik.system.DexFile.PATCHOAT_FOR_RELOCATION
-    kPatchoatForRelocation = 5,
   };
 
   enum OatStatus {
@@ -237,15 +232,6 @@
   // Returns the status of the oat file for the dex location.
   OatStatus OatFileStatus();
 
-  // Generates the oat file by relocation from the named input file.
-  // This does not check the current status before attempting to relocate the
-  // oat file.
-  //
-  // If the result is not kUpdateSucceeded, the value of error_msg will be set
-  // to a string describing why there was a failure or the update was not
-  // attempted. error_msg must not be null.
-  ResultOfAttemptToUpdate RelocateOatFile(const std::string* input_file, std::string* error_msg);
-
   // Generate the oat file from the dex file using the current runtime
   // compiler options.
   // This does not check the current status before attempting to generate the
@@ -328,8 +314,6 @@
     // given target_compilation_filter.
     // profile_changed should be true to indicate the profile has recently
     // changed for this dex location.
-    // If patchoat is needed, this function will return the kPatchOatNeeded
-    // status, not the kSelfPatchOatNeeded status.
     DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
                                  bool profile_changed);
 
@@ -341,9 +325,6 @@
     // Returns true if the file is opened executable.
     bool IsExecutable();
 
-    // Returns true if the file has patch info required to run patchoat.
-    bool HasPatchInfo();
-
     // Clear any cached information about the file that depends on the
     // contents of the file. This does not reset the provided filename.
     void Reset();
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 26dbaab..afa804c 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -85,7 +85,6 @@
                           CompilerFilter::Filter filter,
                           bool relocate,
                           bool pic,
-                          bool with_patch_info,
                           bool with_alternate_image) {
     std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
     std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
@@ -111,10 +110,6 @@
       args.push_back("--compile-pic");
     }
 
-    if (with_patch_info) {
-      args.push_back("--include-patch-information");
-    }
-
     std::string image_location = GetImageLocation();
     if (with_alternate_image) {
       args.push_back("--boot-image=" + GetImageLocation2());
@@ -139,7 +134,6 @@
                                                      &error_msg));
     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
     EXPECT_EQ(pic, odex_file->IsPic());
-    EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo());
     EXPECT_EQ(filter, odex_file->GetCompilerFilter());
 
     std::unique_ptr<ImageHeader> image_header(
@@ -181,7 +175,6 @@
                        filter,
                        /*relocate*/false,
                        /*pic*/false,
-                       /*with_patch_info*/true,
                        /*with_alternate_image*/false);
   }
 
@@ -193,21 +186,6 @@
                        filter,
                        /*relocate*/false,
                        /*pic*/true,
-                       /*with_patch_info*/false,
-                       /*with_alternate_image*/false);
-  }
-
-  // Generate a non-PIC odex file without patch information for the purposes
-  // of test.  The generated odex file will be un-relocated.
-  void GenerateNoPatchOdexForTest(const std::string& dex_location,
-                                  const std::string& odex_location,
-                                  CompilerFilter::Filter filter) {
-    GenerateOatForTest(dex_location,
-                       odex_location,
-                       filter,
-                       /*relocate*/false,
-                       /*pic*/false,
-                       /*with_patch_info*/false,
                        /*with_alternate_image*/false);
   }
 
@@ -216,7 +194,6 @@
                           CompilerFilter::Filter filter,
                           bool relocate,
                           bool pic,
-                          bool with_patch_info,
                           bool with_alternate_image) {
     std::string oat_location;
     std::string error_msg;
@@ -227,7 +204,6 @@
                        filter,
                        relocate,
                        pic,
-                       with_patch_info,
                        with_alternate_image);
   }
 
@@ -237,7 +213,6 @@
                        filter,
                        /*relocate*/true,
                        /*pic*/false,
-                       /*with_patch_info*/false,
                        /*with_alternate_image*/false);
   }
 
@@ -519,7 +494,6 @@
                      CompilerFilter::kSpeed,
                      /*relocate*/true,
                      /*pic*/false,
-                     /*with_patch_info*/false,
                      /*with_alternate_image*/true);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
@@ -548,7 +522,6 @@
                      CompilerFilter::kVerifyAtRuntime,
                      /*relocate*/true,
                      /*pic*/false,
-                     /*with_patch_info*/false,
                      /*with_alternate_image*/true);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
@@ -564,7 +537,6 @@
 }
 
 // Case: We have a DEX file and an ODEX file, but no OAT file.
-// Expect: The status is kPatchOatNeeded.
 TEST_F(OatFileAssistantTest, DexOdexNoOat) {
   std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
   std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
@@ -578,7 +550,7 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
+  EXPECT_EQ(-OatFileAssistant::kDex2OatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -591,15 +563,14 @@
   ASSERT_TRUE(oat_file.get() != nullptr);
 }
 
-// Case: We have a stripped DEX file and an ODEX file, but no OAT file.
-// Expect: The status is kPatchOatNeeded
+// Case: We have a stripped DEX file and a PIC ODEX file, but no OAT file.
 TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) {
   std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
   std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex";
 
   // Create the dex and odex files
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+  GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Strip the dex file
   Copy(GetStrippedDexSrc1(), dex_location);
@@ -607,28 +578,14 @@
   // Verify the status.
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
+  EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
   EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
-  // Make the oat file up to date.
-  std::string error_msg;
-  Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
-  ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
-
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
-  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
-  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
-
   // Verify we can load the dex files from it.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -638,8 +595,7 @@
   EXPECT_EQ(1u, dex_files.size());
 }
 
-// Case: We have a stripped DEX file, an ODEX file, and an out-of-date OAT file.
-// Expect: The status is kPatchOatNeeded.
+// Case: We have a stripped DEX file, a PIC ODEX file, and an out-of-date OAT file.
 TEST_F(OatFileAssistantTest, StrippedDexOdexOat) {
   std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
   std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex";
@@ -650,7 +606,7 @@
 
   // Create the odex file
   Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+  GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
 
   // Strip the dex file.
   Copy(GetStrippedDexSrc1(), dex_location);
@@ -660,30 +616,14 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,  // Can't run dex2oat because dex file is stripped.
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
-  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
-  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
-
-  // Make the oat file up to date.
-  std::string error_msg;
-  Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
-  ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
-
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,  // Can't run dex2oat because dex file is stripped.
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
-  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
@@ -732,90 +672,9 @@
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 }
 
-// Case: We have a DEX file, no ODEX file and an OAT file that needs
-// relocation.
-// Expect: The status is kSelfPatchOatNeeded.
-TEST_F(OatFileAssistantTest, SelfRelocation) {
-  std::string dex_location = GetScratchDir() + "/SelfRelocation.jar";
-  std::string oat_location = GetOdexDir() + "/SelfRelocation.oat";
-
-  // Create the dex and odex files
-  Copy(GetDexSrc1(), dex_location);
-  GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
-
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      oat_location.c_str(), kRuntimeISA, true);
-
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
-  EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
-  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
-  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
-
-  // Make the oat file up to date.
-  std::string error_msg;
-  Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
-  ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
-
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
-  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
-  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
-
-  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
-  ASSERT_TRUE(oat_file.get() != nullptr);
-  EXPECT_TRUE(oat_file->IsExecutable());
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
-  EXPECT_EQ(1u, dex_files.size());
-}
-
-// Case: We have a DEX file, no ODEX file and an OAT file that needs
-// relocation but doesn't have patch info.
-// Expect: The status is kDex2OatNeeded, because we can't run patchoat.
-TEST_F(OatFileAssistantTest, NoSelfRelocation) {
-  std::string dex_location = GetScratchDir() + "/NoSelfRelocation.jar";
-  std::string oat_location = GetOdexDir() + "/NoSelfRelocation.oat";
-
-  // Create the dex and odex files
-  Copy(GetDexSrc1(), dex_location);
-  GenerateNoPatchOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
-
-  OatFileAssistant oat_file_assistant(dex_location.c_str(),
-      oat_location.c_str(), kRuntimeISA, true);
-
-  EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
-  // Make the oat file up to date.
-  std::string error_msg;
-  Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
-  ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
-  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
-  ASSERT_TRUE(oat_file.get() != nullptr);
-  EXPECT_TRUE(oat_file->IsExecutable());
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
-  EXPECT_EQ(1u, dex_files.size());
-}
-
 // Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
 // OAT files both have patch delta of 0.
-// Expect: It shouldn't crash, and status is kSelfPatchOatNeeded.
+// Expect: It shouldn't crash.
 TEST_F(OatFileAssistantTest, OdexOatOverlap) {
   std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
   std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -833,10 +692,10 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
       oat_location.c_str(), kRuntimeISA, true);
 
-  // kPatchoatForRelocation is expected rather than -kPatchoatForRelocation
+  // kDex2OatForRelocation is expected rather than -kDex2OatForRelocation
   // based on the assumption that the oat location is more up-to-date than the odex
   // location, even if they both need relocation.
-  EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -1285,7 +1144,6 @@
     {OatFileAssistant::kDex2OatForBootImage, "DEX2OAT_FOR_BOOT_IMAGE"},
     {OatFileAssistant::kDex2OatForFilter, "DEX2OAT_FOR_FILTER"},
     {OatFileAssistant::kDex2OatForRelocation, "DEX2OAT_FOR_RELOCATION"},
-    {OatFileAssistant::kPatchoatForRelocation, "PATCHOAT_FOR_RELOCATION"}
   };
 
   ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 0f9fbb2..be06dd7 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -21,8 +21,11 @@
            "object_tagging.cc",
            "OpenjdkJvmTi.cc",
            "ti_class.cc",
+           "ti_field.cc",
            "ti_heap.cc",
            "ti_method.cc",
+           "ti_object.cc",
+           "ti_properties.cc",
            "ti_stack.cc",
            "ti_redefine.cc",
            "transform.cc"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 5f97b60..936049f 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -47,8 +47,11 @@
 #include "thread-inl.h"
 #include "thread_list.h"
 #include "ti_class.h"
+#include "ti_field.h"
 #include "ti_heap.h"
 #include "ti_method.h"
+#include "ti_object.h"
+#include "ti_properties.h"
 #include "ti_redefine.h"
 #include "ti_stack.h"
 #include "transform.h"
@@ -537,7 +540,7 @@
   }
 
   static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::GetClassStatus(env, klass, status_ptr);
   }
 
   static jvmtiError GetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr) {
@@ -545,28 +548,28 @@
   }
 
   static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::GetClassModifiers(env, klass, modifiers_ptr);
   }
 
   static jvmtiError GetClassMethods(jvmtiEnv* env,
                                     jclass klass,
                                     jint* method_count_ptr,
                                     jmethodID** methods_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::GetClassMethods(env, klass, method_count_ptr, methods_ptr);
   }
 
   static jvmtiError GetClassFields(jvmtiEnv* env,
                                    jclass klass,
                                    jint* field_count_ptr,
                                    jfieldID** fields_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::GetClassFields(env, klass, field_count_ptr, fields_ptr);
   }
 
   static jvmtiError GetImplementedInterfaces(jvmtiEnv* env,
                                              jclass klass,
                                              jint* interface_count_ptr,
                                              jclass** interfaces_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::GetImplementedInterfaces(env, klass, interface_count_ptr, interfaces_ptr);
   }
 
   static jvmtiError GetClassVersionNumbers(jvmtiEnv* env,
@@ -585,23 +588,23 @@
   }
 
   static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::IsInterface(env, klass, is_interface_ptr);
   }
 
   static jvmtiError IsArrayClass(jvmtiEnv* env,
                                  jclass klass,
                                  jboolean* is_array_class_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::IsArrayClass(env, klass, is_array_class_ptr);
   }
 
   static jvmtiError IsModifiableClass(jvmtiEnv* env,
                                       jclass klass,
                                       jboolean* is_modifiable_class_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return Redefiner::IsModifiableClass(env, klass, is_modifiable_class_ptr);
   }
 
   static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ClassUtil::GetClassLoader(env, klass, classloader_ptr);
   }
 
   static jvmtiError GetSourceDebugExtension(jvmtiEnv* env,
@@ -621,11 +624,11 @@
   }
 
   static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ObjectUtil::GetObjectSize(env, object, size_ptr);
   }
 
   static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ObjectUtil::GetObjectHashCode(env, object, hash_code_ptr);
   }
 
   static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
@@ -640,28 +643,28 @@
                                  char** name_ptr,
                                  char** signature_ptr,
                                  char** generic_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return FieldUtil::GetFieldName(env, klass, field, name_ptr, signature_ptr, generic_ptr);
   }
 
   static jvmtiError GetFieldDeclaringClass(jvmtiEnv* env,
                                            jclass klass,
                                            jfieldID field,
                                            jclass* declaring_class_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return FieldUtil::GetFieldDeclaringClass(env, klass, field, declaring_class_ptr);
   }
 
   static jvmtiError GetFieldModifiers(jvmtiEnv* env,
                                       jclass klass,
                                       jfieldID field,
                                       jint* modifiers_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return FieldUtil::GetFieldModifiers(env, klass, field, modifiers_ptr);
   }
 
   static jvmtiError IsFieldSynthetic(jvmtiEnv* env,
                                      jclass klass,
                                      jfieldID field,
                                      jboolean* is_synthetic_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return FieldUtil::IsFieldSynthetic(env, klass, field, is_synthetic_ptr);
   }
 
   static jvmtiError GetMethodName(jvmtiEnv* env,
@@ -687,13 +690,13 @@
   static jvmtiError GetMaxLocals(jvmtiEnv* env,
                                  jmethodID method,
                                  jint* max_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::GetMaxLocals(env, method, max_ptr);
   }
 
   static jvmtiError GetArgumentsSize(jvmtiEnv* env,
                                      jmethodID method,
                                      jint* size_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::GetArgumentsSize(env, method, size_ptr);
   }
 
   static jvmtiError GetLineNumberTable(jvmtiEnv* env,
@@ -707,7 +710,7 @@
                                       jmethodID method,
                                       jlocation* start_location_ptr,
                                       jlocation* end_location_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::GetMethodLocation(env, method, start_location_ptr, end_location_ptr);
   }
 
   static jvmtiError GetLocalVariableTable(jvmtiEnv* env,
@@ -725,15 +728,15 @@
   }
 
   static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::IsMethodNative(env, method, is_native_ptr);
   }
 
   static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::IsMethodSynthetic(env, method, is_synthetic_ptr);
   }
 
   static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::IsMethodObsolete(env, method, is_obsolete_ptr);
   }
 
   static jvmtiError SetNativeMethodPrefix(jvmtiEnv* env, const char* prefix) {
@@ -838,19 +841,28 @@
   static jvmtiError GetExtensionFunctions(jvmtiEnv* env,
                                           jint* extension_count_ptr,
                                           jvmtiExtensionFunctionInfo** extensions) {
-    return ERR(NOT_IMPLEMENTED);
+    // We do not have any extension functions.
+    *extension_count_ptr = 0;
+    *extensions = nullptr;
+
+    return ERR(NONE);
   }
 
   static jvmtiError GetExtensionEvents(jvmtiEnv* env,
                                        jint* extension_count_ptr,
                                        jvmtiExtensionEventInfo** extensions) {
-    return ERR(NOT_IMPLEMENTED);
+    // We do not have any extension events.
+    *extension_count_ptr = 0;
+    *extensions = nullptr;
+
+    return ERR(NONE);
   }
 
   static jvmtiError SetExtensionEventCallback(jvmtiEnv* env,
                                               jint extension_event_index,
                                               jvmtiExtensionEvent callback) {
-    return ERR(NOT_IMPLEMENTED);
+    // We do not have any extension events, so any call is illegal.
+    return ERR(ILLEGAL_ARGUMENT);
   }
 
   static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) {
@@ -1023,15 +1035,15 @@
   }
 
   static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return PropertiesUtil::GetSystemProperties(env, count_ptr, property_ptr);
   }
 
   static jvmtiError GetSystemProperty(jvmtiEnv* env, const char* property, char** value_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return PropertiesUtil::GetSystemProperty(env, property, value_ptr);
   }
 
   static jvmtiError SetSystemProperty(jvmtiEnv* env, const char* property, const char* value) {
-    return ERR(NOT_IMPLEMENTED);
+    return PropertiesUtil::SetSystemProperty(env, property, value);
   }
 
   static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) {
@@ -1066,8 +1078,15 @@
     ENSURE_NON_NULL(name_ptr);
     switch (error) {
 #define ERROR_CASE(e) case (JVMTI_ERROR_ ## e) : do { \
-          *name_ptr = const_cast<char*>("JVMTI_ERROR_"#e); \
-          return OK; \
+          jvmtiError res = CopyString(env, \
+                                      "JVMTI_ERROR_"#e, \
+                                      reinterpret_cast<unsigned char**>(name_ptr)); \
+          if (res != OK) { \
+            *name_ptr = nullptr; \
+            return res; \
+          } else { \
+            return OK; \
+          } \
         } while (false)
       ERROR_CASE(NONE);
       ERROR_CASE(INVALID_THREAD);
@@ -1119,8 +1138,15 @@
       ERROR_CASE(INVALID_ENVIRONMENT);
 #undef ERROR_CASE
       default: {
-        *name_ptr = const_cast<char*>("JVMTI_ERROR_UNKNOWN");
-        return ERR(ILLEGAL_ARGUMENT);
+        jvmtiError res = CopyString(env,
+                                    "JVMTI_ERROR_UNKNOWN",
+                                    reinterpret_cast<unsigned char**>(name_ptr));
+        if (res != OK) {
+          *name_ptr = nullptr;
+          return res;
+        } else {
+          return ERR(ILLEGAL_ARGUMENT);
+        }
       }
     }
   }
@@ -1165,7 +1191,7 @@
                                     reinterpret_cast<uint8_t*>(dex_file),
                                     &error);
     if (ret != OK) {
-      LOG(ERROR) << "FAILURE TO REDEFINE " << error;
+      LOG(WARNING) << "FAILURE TO REDEFINE " << error;
     }
     return ret;
   }
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 48b29a3..5eadc5a 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -105,9 +105,10 @@
 
 using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>;
 
+template <typename T>
 ALWAYS_INLINE
-static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, unsigned char* mem) {
-  return JvmtiUniquePtr(mem, JvmtiDeleter(env));
+static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
+  return JvmtiUniquePtr(reinterpret_cast<unsigned char*>(mem), JvmtiDeleter(env));
 }
 
 ALWAYS_INLINE
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index de2076a..0d1704c 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -32,11 +32,146 @@
 #include "ti_class.h"
 
 #include "art_jvmti.h"
+#include "jni_internal.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace openjdkjvmti {
 
+jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
+                                     jclass jklass,
+                                     jint* field_count_ptr,
+                                     jfieldID** fields_ptr) {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+
+  if (field_count_ptr == nullptr || fields_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::IterationRange<art::StrideIterator<art::ArtField>> ifields = klass->GetIFields();
+  art::IterationRange<art::StrideIterator<art::ArtField>> sfields = klass->GetSFields();
+  size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
+
+  unsigned char* out_ptr;
+  jvmtiError allocError = env->Allocate(array_size * sizeof(jfieldID), &out_ptr);
+  if (allocError != ERR(NONE)) {
+    return allocError;
+  }
+  jfieldID* field_array = reinterpret_cast<jfieldID*>(out_ptr);
+
+  size_t array_idx = 0;
+  for (art::ArtField& field : sfields) {
+    field_array[array_idx] = art::jni::EncodeArtField(&field);
+    ++array_idx;
+  }
+  for (art::ArtField& field : ifields) {
+    field_array[array_idx] = art::jni::EncodeArtField(&field);
+    ++array_idx;
+  }
+
+  *field_count_ptr = static_cast<jint>(array_size);
+  *fields_ptr = field_array;
+
+  return ERR(NONE);
+}
+
+jvmtiError ClassUtil::GetClassMethods(jvmtiEnv* env,
+                                      jclass jklass,
+                                      jint* method_count_ptr,
+                                      jmethodID** methods_ptr) {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+
+  if (method_count_ptr == nullptr || methods_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  size_t array_size = klass->NumDeclaredVirtualMethods() + klass->NumDirectMethods();
+  unsigned char* out_ptr;
+  jvmtiError allocError = env->Allocate(array_size * sizeof(jmethodID), &out_ptr);
+  if (allocError != ERR(NONE)) {
+    return allocError;
+  }
+  jmethodID* method_array = reinterpret_cast<jmethodID*>(out_ptr);
+
+  if (art::kIsDebugBuild) {
+    size_t count = 0;
+    for (auto& m ATTRIBUTE_UNUSED : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+      count++;
+    }
+    CHECK_EQ(count, klass->NumDirectMethods() + klass->NumDeclaredVirtualMethods());
+  }
+
+  size_t array_idx = 0;
+  for (auto& m : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+    method_array[array_idx] = art::jni::EncodeArtMethod(&m);
+    ++array_idx;
+  }
+
+  *method_count_ptr = static_cast<jint>(array_size);
+  *methods_ptr = method_array;
+
+  return ERR(NONE);
+}
+
+jvmtiError ClassUtil::GetImplementedInterfaces(jvmtiEnv* env,
+                                               jclass jklass,
+                                               jint* interface_count_ptr,
+                                               jclass** interfaces_ptr) {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+
+  if (interface_count_ptr == nullptr || interfaces_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  // Need to handle array specifically. Arrays implement Serializable and Cloneable, but the
+  // spec says these should not be reported.
+  if (klass->IsArrayClass()) {
+    *interface_count_ptr = 0;
+    *interfaces_ptr = nullptr;  // TODO: Should we allocate a dummy here?
+    return ERR(NONE);
+  }
+
+  size_t array_size = klass->NumDirectInterfaces();
+  unsigned char* out_ptr;
+  jvmtiError allocError = env->Allocate(array_size * sizeof(jclass), &out_ptr);
+  if (allocError != ERR(NONE)) {
+    return allocError;
+  }
+  jclass* interface_array = reinterpret_cast<jclass*>(out_ptr);
+
+  art::StackHandleScope<1> hs(soa.Self());
+  art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass));
+
+  for (uint32_t idx = 0; idx != array_size; ++idx) {
+    art::ObjPtr<art::mirror::Class> inf_klass =
+        art::mirror::Class::ResolveDirectInterface(soa.Self(), h_klass, idx);
+    if (inf_klass == nullptr) {
+      soa.Self()->ClearException();
+      env->Deallocate(out_ptr);
+      // TODO: What is the right error code here?
+      return ERR(INTERNAL);
+    }
+    interface_array[idx] = soa.AddLocalReference<jclass>(inf_klass);
+  }
+
+  *interface_count_ptr = static_cast<jint>(array_size);
+  *interfaces_ptr = interface_array;
+
+  return ERR(NONE);
+}
+
 jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
                                          jclass jklass,
                                          char** signature_ptr,
@@ -70,4 +205,127 @@
   return ERR(NONE);
 }
 
+jvmtiError ClassUtil::GetClassStatus(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                     jclass jklass,
+                                     jint* status_ptr) {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+
+  if (status_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  if (klass->IsArrayClass()) {
+    *status_ptr = JVMTI_CLASS_STATUS_ARRAY;
+  } else if (klass->IsPrimitive()) {
+    *status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE;
+  } else {
+    *status_ptr = JVMTI_CLASS_STATUS_VERIFIED;  // All loaded classes are structurally verified.
+    // This is finicky. If there's an error, we'll say it wasn't prepared.
+    if (klass->IsResolved()) {
+      *status_ptr |= JVMTI_CLASS_STATUS_PREPARED;
+    }
+    if (klass->IsInitialized()) {
+      *status_ptr |= JVMTI_CLASS_STATUS_INITIALIZED;
+    }
+    // Technically the class may be erroneous for other reasons, but we do not have enough info.
+    if (klass->IsErroneous()) {
+      *status_ptr |= JVMTI_CLASS_STATUS_ERROR;
+    }
+  }
+
+  return ERR(NONE);
+}
+
+template <typename T>
+static jvmtiError ClassIsT(jclass jklass, T test, jboolean* is_t_ptr) {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+
+  if (is_t_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  *is_t_ptr = test(klass) ? JNI_TRUE : JNI_FALSE;
+  return ERR(NONE);
+}
+
+jvmtiError ClassUtil::IsInterface(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                  jclass jklass,
+                                  jboolean* is_interface_ptr) {
+  auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    return klass->IsInterface();
+  };
+  return ClassIsT(jklass, test, is_interface_ptr);
+}
+
+jvmtiError ClassUtil::IsArrayClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                   jclass jklass,
+                                   jboolean* is_array_class_ptr) {
+  auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    return klass->IsArrayClass();
+  };
+  return ClassIsT(jklass, test, is_array_class_ptr);
+}
+
+// Keep this in sync with Class.getModifiers().
+static uint32_t ClassGetModifiers(art::Thread* self, art::ObjPtr<art::mirror::Class> klass)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  if (klass->IsArrayClass()) {
+    uint32_t component_modifiers = ClassGetModifiers(self, klass->GetComponentType());
+    if ((component_modifiers & art::kAccInterface) != 0) {
+      component_modifiers &= ~(art::kAccInterface | art::kAccStatic);
+    }
+    return art::kAccAbstract | art::kAccFinal | component_modifiers;
+  }
+
+  uint32_t modifiers = klass->GetAccessFlags() & art::kAccJavaFlagsMask;
+
+  art::StackHandleScope<1> hs(self);
+  art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass));
+  return art::mirror::Class::GetInnerClassFlags(h_klass, modifiers);
+}
+
+jvmtiError ClassUtil::GetClassModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                        jclass jklass,
+                                        jint* modifiers_ptr) {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+
+  if (modifiers_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  *modifiers_ptr = ClassGetModifiers(soa.Self(), klass);
+
+  return ERR(NONE);
+}
+
+jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                     jclass jklass,
+                                     jobject* classloader_ptr) {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+
+  if (classloader_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  *classloader_ptr = soa.AddLocalReference<jobject>(klass->GetClassLoader());
+
+  return ERR(NONE);
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index caa77d4..577fc8e 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -39,10 +39,34 @@
 
 class ClassUtil {
  public:
+  static jvmtiError GetClassFields(jvmtiEnv* env,
+                                   jclass klass,
+                                   jint* field_count_ptr,
+                                   jfieldID** fields_ptr);
+
+  static jvmtiError GetClassMethods(jvmtiEnv* env,
+                                    jclass klass,
+                                    jint* method_count_ptr,
+                                    jmethodID** methods_ptr);
+
+  static jvmtiError GetImplementedInterfaces(jvmtiEnv* env,
+                                             jclass klass,
+                                             jint* interface_count_ptr,
+                                             jclass** interfaces_ptr);
+
+  static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr);
+
   static jvmtiError GetClassSignature(jvmtiEnv* env,
                                       jclass klass,
                                       char** signature_ptr,
                                       char** generic_ptr);
+
+  static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr);
+
+  static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr);
+
+  static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
+  static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc
new file mode 100644
index 0000000..a762830
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_field.cc
@@ -0,0 +1,171 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_field.h"
+
+#include "art_jvmti.h"
+#include "art_field-inl.h"
+#include "base/enums.h"
+#include "jni_internal.h"
+#include "modifiers.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+// Note: For all these functions, we could do a check that the field actually belongs to the given
+//       class. But the spec seems to assume a certain encoding of the field ID, and so doesn't
+//       specify any errors.
+
+jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env,
+                                   jclass klass,
+                                   jfieldID field,
+                                   char** name_ptr,
+                                   char** signature_ptr,
+                                   char** generic_ptr) {
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+  if (field == nullptr) {
+    return ERR(INVALID_FIELDID);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ArtField* art_field = art::jni::DecodeArtField(field);
+
+  JvmtiUniquePtr name_copy;
+  if (name_ptr != nullptr) {
+    const char* field_name = art_field->GetName();
+    if (field_name == nullptr) {
+      field_name = "<error>";
+    }
+    unsigned char* tmp;
+    jvmtiError ret = CopyString(env, field_name, &tmp);
+    if (ret != ERR(NONE)) {
+      return ret;
+    }
+    name_copy = MakeJvmtiUniquePtr(env, tmp);
+    *name_ptr = reinterpret_cast<char*>(tmp);
+  }
+
+  JvmtiUniquePtr signature_copy;
+  if (signature_ptr != nullptr) {
+    const char* sig = art_field->GetTypeDescriptor();
+    unsigned char* tmp;
+    jvmtiError ret = CopyString(env, sig, &tmp);
+    if (ret != ERR(NONE)) {
+      return ret;
+    }
+    signature_copy = MakeJvmtiUniquePtr(env, tmp);
+    *signature_ptr = reinterpret_cast<char*>(tmp);
+  }
+
+  // TODO: Support generic signature.
+  if (generic_ptr != nullptr) {
+    *generic_ptr = nullptr;
+  }
+
+  // Everything is fine, release the buffers.
+  name_copy.release();
+  signature_copy.release();
+
+  return ERR(NONE);
+}
+
+jvmtiError FieldUtil::GetFieldDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                             jclass klass,
+                                             jfieldID field,
+                                             jclass* declaring_class_ptr) {
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+  if (field == nullptr) {
+    return ERR(INVALID_FIELDID);
+  }
+  if (declaring_class_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ArtField* art_field = art::jni::DecodeArtField(field);
+  art::ObjPtr<art::mirror::Class> field_klass = art_field->GetDeclaringClass();
+
+  *declaring_class_ptr = soa.AddLocalReference<jclass>(field_klass);
+
+  return ERR(NONE);
+}
+
+jvmtiError FieldUtil::GetFieldModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                        jclass klass,
+                                        jfieldID field,
+                                        jint* modifiers_ptr) {
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+  if (field == nullptr) {
+    return ERR(INVALID_FIELDID);
+  }
+  if (modifiers_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ArtField* art_field = art::jni::DecodeArtField(field);
+  // Note: Keep this code in sync with Field.getModifiers.
+  uint32_t modifiers = art_field->GetAccessFlags() & 0xFFFF;
+
+  *modifiers_ptr = modifiers;
+  return ERR(NONE);
+}
+
+jvmtiError FieldUtil::IsFieldSynthetic(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                       jclass klass,
+                                       jfieldID field,
+                                       jboolean* is_synthetic_ptr) {
+  if (klass == nullptr) {
+    return ERR(INVALID_CLASS);
+  }
+  if (field == nullptr) {
+    return ERR(INVALID_FIELDID);
+  }
+  if (is_synthetic_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ArtField* art_field = art::jni::DecodeArtField(field);
+  uint32_t modifiers = art_field->GetAccessFlags();
+
+  *is_synthetic_ptr = ((modifiers & art::kAccSynthetic) != 0) ? JNI_TRUE : JNI_FALSE;
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_field.h b/runtime/openjdkjvmti/ti_field.h
new file mode 100644
index 0000000..9a29f81
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_field.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class FieldUtil {
+ public:
+  static jvmtiError GetFieldName(jvmtiEnv* env,
+                                 jclass klass,
+                                 jfieldID field,
+                                 char** name_ptr,
+                                 char** signature_ptr,
+                                 char** generic_ptr);
+
+  static jvmtiError GetFieldDeclaringClass(jvmtiEnv* env,
+                                           jclass klass,
+                                           jfieldID field,
+                                           jclass* declaring_class_ptr);
+
+  static jvmtiError GetFieldModifiers(jvmtiEnv* env,
+                                      jclass klass,
+                                      jfieldID field,
+                                      jint* modifiers_ptr);
+
+  static jvmtiError IsFieldSynthetic(jvmtiEnv* env,
+                                     jclass klass,
+                                     jfieldID field,
+                                     jboolean* is_synthetic_ptr);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index a0a0923..2ddd64a 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -37,9 +37,68 @@
 #include "jni_internal.h"
 #include "modifiers.h"
 #include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
 
 namespace openjdkjvmti {
 
+jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                        jmethodID method,
+                                        jint* size_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+  if (art_method->IsNative()) {
+    return ERR(NATIVE_METHOD);
+  }
+
+  if (size_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+    // This isn't specified as an error case, so return 0.
+    *size_ptr = 0;
+    return ERR(NONE);
+  }
+
+  DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+  *size_ptr = art_method->GetCodeItem()->ins_size_;
+
+  return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                    jmethodID method,
+                                    jint* max_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+  if (art_method->IsNative()) {
+    return ERR(NATIVE_METHOD);
+  }
+
+  if (max_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+    // This isn't specified as an error case, so return 0.
+    *max_ptr = 0;
+    return ERR(NONE);
+  }
+
+  DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+  *max_ptr = art_method->GetCodeItem()->registers_size_;
+
+  return ERR(NONE);
+}
+
 jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
                                      jmethodID method,
                                      char** name_ptr,
@@ -106,6 +165,38 @@
   return ERR(NONE);
 }
 
+jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                         jmethodID method,
+                                         jlocation* start_location_ptr,
+                                         jlocation* end_location_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+  if (art_method->IsNative()) {
+    return ERR(NATIVE_METHOD);
+  }
+
+  if (start_location_ptr == nullptr || end_location_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+    // This isn't specified as an error case, so return 0/0.
+    *start_location_ptr = 0;
+    *end_location_ptr = 0;
+    return ERR(NONE);
+  }
+
+  DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+  *start_location_ptr = 0;
+  *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1;
+
+  return ERR(NONE);
+}
+
 jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                           jmethodID method,
                                           jint* modifiers_ptr) {
@@ -189,4 +280,43 @@
   return ERR(NONE);
 }
 
+template <typename T>
+static jvmtiError IsMethodT(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                            jmethodID method,
+                            T test,
+                            jboolean* is_t_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  if (is_t_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+  *is_t_ptr = test(art_method) ? JNI_TRUE : JNI_FALSE;
+
+  return ERR(NONE);
+}
+
+jvmtiError MethodUtil::IsMethodNative(jvmtiEnv* env, jmethodID m, jboolean* is_native_ptr) {
+  auto test = [](art::ArtMethod* method) {
+    return method->IsNative();
+  };
+  return IsMethodT(env, m, test, is_native_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodObsolete(jvmtiEnv* env, jmethodID m, jboolean* is_obsolete_ptr) {
+  auto test = [](art::ArtMethod* method) {
+    return method->IsObsolete();
+  };
+  return IsMethodT(env, m, test, is_obsolete_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* is_synthetic_ptr) {
+  auto test = [](art::ArtMethod* method) {
+    return method->IsSynthetic();
+  };
+  return IsMethodT(env, m, test, is_synthetic_ptr);
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index fb2fbb2..e5c1705 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -39,6 +39,10 @@
 
 class MethodUtil {
  public:
+  static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
+
+  static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
+
   static jvmtiError GetMethodName(jvmtiEnv* env,
                                   jmethodID method,
                                   char** name_ptr,
@@ -49,6 +53,11 @@
                                             jmethodID method,
                                             jclass* declaring_class_ptr);
 
+  static jvmtiError GetMethodLocation(jvmtiEnv* env,
+                                      jmethodID method,
+                                      jlocation* start_location_ptr,
+                                      jlocation* end_location_ptr);
+
   static jvmtiError GetMethodModifiers(jvmtiEnv* env,
                                        jmethodID method,
                                        jint* modifiers_ptr);
@@ -57,6 +66,10 @@
                                        jmethodID method,
                                        jint* entry_count_ptr,
                                        jvmtiLineNumberEntry** table_ptr);
+
+  static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr);
+  static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr);
+  static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr);
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_object.cc b/runtime/openjdkjvmti/ti_object.cc
new file mode 100644
index 0000000..bf84499
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_object.cc
@@ -0,0 +1,76 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_object.h"
+
+#include "art_jvmti.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError ObjectUtil::GetObjectSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                     jobject jobject,
+                                     jlong* size_ptr) {
+  if (jobject == nullptr) {
+    return ERR(INVALID_OBJECT);
+  }
+  if (size_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Object> object = soa.Decode<art::mirror::Object>(jobject);
+
+  *size_ptr = object->SizeOf();
+  return ERR(NONE);
+}
+
+jvmtiError ObjectUtil::GetObjectHashCode(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                         jobject jobject,
+                                         jint* hash_code_ptr) {
+  if (jobject == nullptr) {
+    return ERR(INVALID_OBJECT);
+  }
+  if (hash_code_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ObjPtr<art::mirror::Object> object = soa.Decode<art::mirror::Object>(jobject);
+
+  *hash_code_ptr = object->IdentityHashCode();
+
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_object.h b/runtime/openjdkjvmti/ti_object.h
new file mode 100644
index 0000000..09eee61
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_object.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ObjectUtil {
+ public:
+  static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr);
+
+  static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
diff --git a/runtime/openjdkjvmti/ti_properties.cc b/runtime/openjdkjvmti/ti_properties.cc
new file mode 100644
index 0000000..46b9e71
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_properties.cc
@@ -0,0 +1,192 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_properties.h"
+
+#include <string.h>
+#include <vector>
+
+#include "art_jvmti.h"
+#include "runtime.h"
+
+namespace openjdkjvmti {
+
+// Hardcoded properties. Tests ensure that these are consistent with libcore's view, as seen
+// in System.java and AndroidHardcodedSystemProperties.java.
+static constexpr const char* kProperties[][2] = {
+    // Recommended by the spec.
+    { "java.vm.vendor", "The Android Project" },
+    { "java.vm.version", "2.1.0" },  // This is Runtime::GetVersion().
+    { "java.vm.name", "Dalvik" },
+    // Android does not provide java.vm.info.
+    //
+    // These are other values provided by AndroidHardcodedSystemProperties.
+    { "java.class.version", "50.0" },
+    { "java.version", "0" },
+    { "java.compiler", "" },
+    { "java.ext.dirs", "" },
+
+    { "java.specification.name", "Dalvik Core Library" },
+    { "java.specification.vendor", "The Android Project" },
+    { "java.specification.version", "0.9" },
+
+    { "java.vendor", "The Android Project" },
+    { "java.vendor.url", "http://www.android.com/" },
+    { "java.vm.name", "Dalvik" },
+    { "java.vm.specification.name", "Dalvik Virtual Machine Specification" },
+    { "java.vm.specification.vendor", "The Android Project" },
+    { "java.vm.specification.version", "0.9" },
+    { "java.vm.vendor", "The Android Project" },
+
+    { "java.vm.vendor.url", "http://www.android.com/" },
+
+    { "java.net.preferIPv6Addresses", "false" },
+
+    { "file.encoding", "UTF-8" },
+
+    { "file.separator", "/" },
+    { "line.separator", "\n" },
+    { "path.separator", ":" },
+
+    { "os.name", "Linux" },
+};
+static constexpr size_t kPropertiesSize = arraysize(kProperties);
+static constexpr const char* kPropertyLibraryPath = "java.library.path";
+static constexpr const char* kPropertyClassPath = "java.class.path";
+
+static jvmtiError Copy(jvmtiEnv* env, const char* in, char** out) {
+  unsigned char* data = nullptr;
+  jvmtiError result = CopyString(env, in, &data);
+  *out = reinterpret_cast<char*>(data);
+  return result;
+}
+
+jvmtiError PropertiesUtil::GetSystemProperties(jvmtiEnv* env,
+                                               jint* count_ptr,
+                                               char*** property_ptr) {
+  if (count_ptr == nullptr || property_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  unsigned char* array_data;
+  jvmtiError array_alloc_result = env->Allocate((kPropertiesSize + 2) * sizeof(char*), &array_data);
+  if (array_alloc_result != ERR(NONE)) {
+    return array_alloc_result;
+  }
+  JvmtiUniquePtr array_data_ptr = MakeJvmtiUniquePtr(env, array_data);
+  char** array = reinterpret_cast<char**>(array_data);
+
+  std::vector<JvmtiUniquePtr> property_copies;
+
+  {
+    char* libpath_data;
+    jvmtiError libpath_result = Copy(env, kPropertyLibraryPath, &libpath_data);
+    if (libpath_result != ERR(NONE)) {
+      return libpath_result;
+    }
+    array[0] = libpath_data;
+    property_copies.push_back(MakeJvmtiUniquePtr(env, libpath_data));
+  }
+
+  {
+    char* classpath_data;
+    jvmtiError classpath_result = Copy(env, kPropertyClassPath, &classpath_data);
+    if (classpath_result != ERR(NONE)) {
+      return classpath_result;
+    }
+    array[1] = classpath_data;
+    property_copies.push_back(MakeJvmtiUniquePtr(env, classpath_data));
+  }
+
+  for (size_t i = 0; i != kPropertiesSize; ++i) {
+    char* data;
+    jvmtiError data_result = Copy(env, kProperties[i][0], &data);
+    if (data_result != ERR(NONE)) {
+      return data_result;
+    }
+    array[i + 2] = data;
+    property_copies.push_back(MakeJvmtiUniquePtr(env, data));
+  }
+
+  // Everything is OK, release the data.
+  array_data_ptr.release();
+  for (auto& uptr : property_copies) {
+    uptr.release();
+  }
+
+  *count_ptr = kPropertiesSize + 2;
+  *property_ptr = array;
+
+  return ERR(NONE);
+}
+
+jvmtiError PropertiesUtil::GetSystemProperty(jvmtiEnv* env,
+                                             const char* property,
+                                             char** value_ptr) {
+  if (property == nullptr || value_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  if (strcmp(property, kPropertyLibraryPath) == 0) {
+    // TODO: In the live phase, we should probably compare to System.getProperty. java.library.path
+    //       may not be set initially, and is then freely modifiable.
+    const std::vector<std::string>& runtime_props = art::Runtime::Current()->GetProperties();
+    for (const std::string& prop_assignment : runtime_props) {
+      size_t assign_pos = prop_assignment.find('=');
+      if (assign_pos != std::string::npos && assign_pos > 0) {
+        if (prop_assignment.substr(0, assign_pos) == kPropertyLibraryPath) {
+          return Copy(env, prop_assignment.substr(assign_pos + 1).c_str(), value_ptr);
+        }
+      }
+    }
+    return ERR(NOT_AVAILABLE);
+  }
+
+  if (strcmp(property, kPropertyClassPath) == 0) {
+    return Copy(env, art::Runtime::Current()->GetClassPathString().c_str(), value_ptr);
+  }
+
+  for (size_t i = 0; i != kPropertiesSize; ++i) {
+    if (strcmp(property, kProperties[i][0]) == 0) {
+      return Copy(env, kProperties[i][1], value_ptr);
+    }
+  }
+
+  return ERR(NOT_AVAILABLE);
+}
+
+jvmtiError PropertiesUtil::SetSystemProperty(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                             const char* property ATTRIBUTE_UNUSED,
+                                             const char* value ATTRIBUTE_UNUSED) {
+  // We do not allow manipulation of any property here.
+  return ERR(NOT_AVAILABLE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_properties.h b/runtime/openjdkjvmti/ti_properties.h
new file mode 100644
index 0000000..7073481
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_properties.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class PropertiesUtil {
+ public:
+  static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr);
+
+  static jvmtiError GetSystemProperty(jvmtiEnv* env, const char* property, char** value_ptr);
+
+  static jvmtiError SetSystemProperty(jvmtiEnv* env, const char* property, const char* value);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 926819d..5bf8445 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -37,6 +37,8 @@
 
 #include "art_jvmti.h"
 #include "base/logging.h"
+#include "dex_file.h"
+#include "dex_file_types.h"
 #include "events-inl.h"
 #include "gc/allocation_listener.h"
 #include "gc/heap.h"
@@ -64,19 +66,14 @@
       art::Thread* thread,
       art::LinearAlloc* allocator,
       const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
-      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
-      /*out*/bool* success,
-      /*out*/std::string* error_msg)
+      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
         : StackVisitor(thread,
                        /*context*/nullptr,
                        StackVisitor::StackWalkKind::kIncludeInlinedFrames),
           allocator_(allocator),
           obsoleted_methods_(obsoleted_methods),
           obsolete_maps_(obsolete_maps),
-          success_(success),
-          is_runtime_frame_(false),
-          error_msg_(error_msg) {
-    *success_ = true;
+          is_runtime_frame_(false) {
   }
 
   ~ObsoleteMethodStackVisitor() OVERRIDE {}
@@ -85,34 +82,17 @@
   // Returns true if we successfully installed obsolete methods on this thread, filling
   // obsolete_maps_ with the translations if needed. Returns false and fills error_msg if we fail.
   // The stack is cleaned up when we fail.
-  static bool UpdateObsoleteFrames(
+  static void UpdateObsoleteFrames(
       art::Thread* thread,
       art::LinearAlloc* allocator,
       const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
-      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
-      /*out*/std::string* error_msg) REQUIRES(art::Locks::mutator_lock_) {
-    bool success = true;
+      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+        REQUIRES(art::Locks::mutator_lock_) {
     ObsoleteMethodStackVisitor visitor(thread,
                                        allocator,
                                        obsoleted_methods,
-                                       obsolete_maps,
-                                       &success,
-                                       error_msg);
+                                       obsolete_maps);
     visitor.WalkStack();
-    if (!success) {
-      RestoreFrames(thread, *obsolete_maps, error_msg);
-      return false;
-    } else {
-      return true;
-    }
-  }
-
-  static void RestoreFrames(
-      art::Thread* thread ATTRIBUTE_UNUSED,
-      const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsolete_maps ATTRIBUTE_UNUSED,
-      std::string* error_msg)
-        REQUIRES(art::Locks::mutator_lock_) {
-    LOG(FATAL) << "Restoring stack frames is not yet supported. Error was: " << *error_msg;
   }
 
   bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
@@ -130,9 +110,7 @@
       // works through runtime methods.
       // TODO b/33616143
       if (!IsShadowFrame() && prev_was_runtime_frame_) {
-        *error_msg_ = StringPrintf("Deoptimization failed due to runtime method in stack.");
-        *success_ = false;
-        return false;
+        LOG(FATAL) << "Deoptimization failed due to runtime method in stack. See b/33616143";
       }
       // We cannot ensure that the right dex file is used in inlined frames so we don't support
       // redefining them.
@@ -150,12 +128,8 @@
         auto ptr_size = cl->GetImagePointerSize();
         const size_t method_size = art::ArtMethod::Size(ptr_size);
         auto* method_storage = allocator_->Alloc(GetThread(), method_size);
-        if (method_storage == nullptr) {
-          *success_ = false;
-          *error_msg_ = StringPrintf("Unable to allocate storage for obsolete version of '%s'",
-                                     old_method->PrettyMethod().c_str());
-          return false;
-        }
+        CHECK(method_storage != nullptr) << "Unable to allocate storage for obsolete version of '"
+                                         << old_method->PrettyMethod() << "'";
         new_obsolete_method = new (method_storage) art::ArtMethod();
         new_obsolete_method->CopyFrom(old_method, ptr_size);
         DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
@@ -186,13 +160,50 @@
   // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
   // the redefined classes ClassExt by the caller.
   std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
-  bool* success_;
   // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
   // works through runtime methods.
   bool is_runtime_frame_;
-  std::string* error_msg_;
 };
 
+jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                        jclass klass,
+                                        jboolean* is_redefinable) {
+  // TODO Check for the appropriate feature flags once we have enabled them.
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);
+  art::StackHandleScope<1> hs(self);
+  art::ObjPtr<art::mirror::Object> obj(self->DecodeJObject(klass));
+  if (obj.IsNull()) {
+    return ERR(INVALID_CLASS);
+  }
+  art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
+  std::string err_unused;
+  *is_redefinable =
+      Redefiner::GetClassRedefinitionError(h_klass, &err_unused) == OK ? JNI_TRUE : JNI_FALSE;
+  return OK;
+}
+
+jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
+                                                /*out*/std::string* error_msg) {
+  if (klass->IsPrimitive()) {
+    *error_msg = "Modification of primitive classes is not supported";
+    return ERR(UNMODIFIABLE_CLASS);
+  } else if (klass->IsInterface()) {
+    *error_msg = "Modification of Interface classes is currently not supported";
+    return ERR(UNMODIFIABLE_CLASS);
+  } else if (klass->IsArrayClass()) {
+    *error_msg = "Modification of Array classes is not supported";
+    return ERR(UNMODIFIABLE_CLASS);
+  } else if (klass->IsProxyClass()) {
+    *error_msg = "Modification of proxy classes is not supported";
+    return ERR(UNMODIFIABLE_CLASS);
+  }
+
+  // TODO We should check if the class has non-obsoletable methods on the stack
+  LOG(WARNING) << "presence of non-obsoletable methods on stacks is not currently checked";
+  return OK;
+}
+
 // Moves dex data to an anonymous, read-only mmap'd region.
 std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location,
                                                          jint data_len,
@@ -446,59 +457,38 @@
   art::LinearAlloc* allocator;
   std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
   std::unordered_set<art::ArtMethod*> obsolete_methods;
-  bool success;
-  std::string* error_msg;
 
-  CallbackCtx(Redefiner* self, art::LinearAlloc* alloc, std::string* error)
-      : r(self), allocator(alloc), success(true), error_msg(error) {}
+  CallbackCtx(Redefiner* self, art::LinearAlloc* alloc)
+      : r(self), allocator(alloc) {}
 };
 
-void DoRestoreObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
-  CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
-  ObsoleteMethodStackVisitor::RestoreFrames(t, data->obsolete_map, data->error_msg);
-}
-
 void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
   CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
-  if (data->success) {
-    // Don't do anything if we already failed once.
-    data->success = ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
-                                                                     data->allocator,
-                                                                     data->obsolete_methods,
-                                                                     &data->obsolete_map,
-                                                                     data->error_msg);
-  }
+  ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
+                                                   data->allocator,
+                                                   data->obsolete_methods,
+                                                   &data->obsolete_map);
 }
 
 // This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
 // updated so they will be run.
-bool Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) {
+void Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) {
   art::ScopedAssertNoThreadSuspension ns("No thread suspension during thread stack walking");
   art::mirror::ClassExt* ext = art_klass->GetExtData();
   CHECK(ext->GetObsoleteMethods() != nullptr);
-  CallbackCtx ctx(this, art_klass->GetClassLoader()->GetAllocator(), error_msg_);
+  CallbackCtx ctx(this, art_klass->GetClassLoader()->GetAllocator());
   // Add all the declared methods to the map
   for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
     ctx.obsolete_methods.insert(&m);
-  }
-  for (art::ArtMethod* old_method : ctx.obsolete_methods) {
-    if (old_method->IsIntrinsic()) {
-      *error_msg_ = StringPrintf("Method '%s' is intrinsic and cannot be made obsolete!",
-                                 old_method->PrettyMethod().c_str());
-      return false;
-    }
+    // TODO Allow this or check in IsModifiableClass.
+    DCHECK(!m.IsIntrinsic());
   }
   {
     art::MutexLock mu(self_, *art::Locks::thread_list_lock_);
     art::ThreadList* list = art::Runtime::Current()->GetThreadList();
     list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
-    if (!ctx.success) {
-      list->ForEach(DoRestoreObsoleteMethodsCallback, static_cast<void*>(&ctx));
-      return false;
-    }
   }
   FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
-  return true;
 }
 
 // Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
@@ -542,6 +532,107 @@
   i->ReJitEverything("libOpenJkdJvmti - Class Redefinition");
 }
 
+bool Redefiner::CheckClass() {
+  // TODO Might just want to put it in a ObjPtr and NoSuspend assert.
+  art::StackHandleScope<1> hs(self_);
+  // Easy check that only 1 class def is present.
+  if (dex_file_->NumClassDefs() != 1) {
+    RecordFailure(ERR(ILLEGAL_ARGUMENT),
+                  StringPrintf("Expected 1 class def in dex file but found %d",
+                               dex_file_->NumClassDefs()));
+    return false;
+  }
+  // Get the ClassDef from the new DexFile.
+  // Since the dex file has only a single class def the index is always 0.
+  const art::DexFile::ClassDef& def = dex_file_->GetClassDef(0);
+  // Get the class as it is now.
+  art::Handle<art::mirror::Class> current_class(hs.NewHandle(GetMirrorClass()));
+
+  // Check the access flags didn't change.
+  if (def.GetJavaAccessFlags() != (current_class->GetAccessFlags() & art::kAccValidClassFlags)) {
+    RecordFailure(ERR(UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED),
+                  "Cannot change modifiers of class by redefinition");
+    return false;
+  }
+
+  // Check class name.
+  // These should have been checked by the dexfile verifier on load.
+  DCHECK_NE(def.class_idx_, art::dex::TypeIndex::Invalid()) << "Invalid type index";
+  const char* descriptor = dex_file_->StringByTypeIdx(def.class_idx_);
+  DCHECK(descriptor != nullptr) << "Invalid dex file structure!";
+  if (!current_class->DescriptorEquals(descriptor)) {
+    std::string storage;
+    RecordFailure(ERR(NAMES_DONT_MATCH),
+                  StringPrintf("expected file to contain class called '%s' but found '%s'!",
+                               current_class->GetDescriptor(&storage),
+                               descriptor));
+    return false;
+  }
+  if (current_class->IsObjectClass()) {
+    if (def.superclass_idx_ != art::dex::TypeIndex::Invalid()) {
+      RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Superclass added!");
+      return false;
+    }
+  } else {
+    const char* super_descriptor = dex_file_->StringByTypeIdx(def.superclass_idx_);
+    DCHECK(descriptor != nullptr) << "Invalid dex file structure!";
+    if (!current_class->GetSuperClass()->DescriptorEquals(super_descriptor)) {
+      RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Superclass changed");
+      return false;
+    }
+  }
+  const art::DexFile::TypeList* interfaces = dex_file_->GetInterfacesList(def);
+  if (interfaces == nullptr) {
+    if (current_class->NumDirectInterfaces() != 0) {
+      RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Interfaces added");
+      return false;
+    }
+  } else {
+    DCHECK(!current_class->IsProxyClass());
+    const art::DexFile::TypeList* current_interfaces = current_class->GetInterfaceTypeList();
+    if (current_interfaces == nullptr || current_interfaces->Size() != interfaces->Size()) {
+      RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Interfaces added or removed");
+      return false;
+    }
+    // The order of interfaces is (barely) meaningful so we error if it changes.
+    const art::DexFile& orig_dex_file = current_class->GetDexFile();
+    for (uint32_t i = 0; i < interfaces->Size(); i++) {
+      if (strcmp(
+            dex_file_->StringByTypeIdx(interfaces->GetTypeItem(i).type_idx_),
+            orig_dex_file.StringByTypeIdx(current_interfaces->GetTypeItem(i).type_idx_)) != 0) {
+        RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED),
+                      "Interfaces changed or re-ordered");
+        return false;
+      }
+    }
+  }
+  LOG(WARNING) << "No verification is done on annotations of redefined classes.";
+
+  return true;
+}
+
+// TODO Move this to use IsRedefinable when that function is made.
+bool Redefiner::CheckRedefinable() {
+  std::string err;
+  art::StackHandleScope<1> hs(self_);
+
+  art::Handle<art::mirror::Class> h_klass(hs.NewHandle(GetMirrorClass()));
+  jvmtiError res = Redefiner::GetClassRedefinitionError(h_klass, &err);
+  if (res != OK) {
+    RecordFailure(res, err);
+    return false;
+  } else {
+    return true;
+  }
+}
+
+bool Redefiner::CheckRedefinitionIsValid() {
+  return CheckRedefinable() &&
+      CheckClass() &&
+      CheckSameFields() &&
+      CheckSameMethods();
+}
+
 jvmtiError Redefiner::Run() {
   art::StackHandleScope<5> hs(self_);
   // TODO We might want to have a global lock (or one based on the class being redefined at least)
@@ -552,7 +643,7 @@
   // doing a try loop. The other allocations we need to ensure that nothing has changed in the time
   // between allocating them and pausing all threads before we can update them so we need to do a
   // try loop.
-  if (!EnsureRedefinitionIsValid() || !EnsureClassAllocationsFinished()) {
+  if (!CheckRedefinitionIsValid() || !EnsureClassAllocationsFinished()) {
     return result_;
   }
   art::MutableHandle<art::mirror::ClassLoader> source_class_loader(
@@ -601,31 +692,9 @@
   // TODO We should really Retry if this fails instead of simply aborting.
   // Set the new DexFileCookie returns the original so we can fix it back up if redefinition fails
   art::ObjPtr<art::mirror::LongArray> original_dex_file_cookie(nullptr);
-  if (!UpdateJavaDexFile(java_dex_file.Get(),
-                         new_dex_file_cookie.Get(),
-                         &original_dex_file_cookie) ||
-      !FindAndAllocateObsoleteMethods(art_class.Get())) {
-    // Release suspendAll
-    runtime_->GetThreadList()->ResumeAll();
-    // Get back shared mutator lock as expected for return.
-    self_->TransitionFromSuspendedToRunnable();
-    if (heap->IsGcConcurrentAndMoving()) {
-      heap->DecrementDisableMovingGC(self_);
-    }
-    return result_;
-  }
-  if (!UpdateClass(art_class.Get(), new_dex_cache.Get())) {
-    // TODO Should have some form of scope to do this.
-    RestoreJavaDexFile(java_dex_file.Get(), original_dex_file_cookie);
-    // Release suspendAll
-    runtime_->GetThreadList()->ResumeAll();
-    // Get back shared mutator lock as expected for return.
-    self_->TransitionFromSuspendedToRunnable();
-    if (heap->IsGcConcurrentAndMoving()) {
-      heap->DecrementDisableMovingGC(self_);
-    }
-    return result_;
-  }
+  UpdateJavaDexFile(java_dex_file.Get(), new_dex_file_cookie.Get(), &original_dex_file_cookie);
+  FindAndAllocateObsoleteMethods(art_class.Get());
+  UpdateClass(art_class.Get(), new_dex_cache.Get());
   // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
   // pointers to their ArtMethod's stashed in registers that they then use to attempt to hit the
   // DexCache.
@@ -652,21 +721,7 @@
   return OK;
 }
 
-void Redefiner::RestoreJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
-                                   art::ObjPtr<art::mirror::LongArray> orig_cookie) {
-  art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
-      "mInternalCookie", "Ljava/lang/Object;");
-  art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
-      "mCookie", "Ljava/lang/Object;");
-  art::ObjPtr<art::mirror::LongArray> new_cookie(
-      cookie_field->GetObject(java_dex_file)->AsLongArray());
-  internal_cookie_field->SetObject<false>(java_dex_file, orig_cookie);
-  if (!new_cookie.IsNull()) {
-    cookie_field->SetObject<false>(java_dex_file, orig_cookie);
-  }
-}
-
-bool Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+void Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
                               art::ObjPtr<art::mirror::DexCache> new_dex_cache,
                               const art::DexFile::ClassDef& class_def) {
   art::ClassLinker* linker = runtime_->GetClassLinker();
@@ -709,10 +764,9 @@
       jit->GetCodeCache()->NotifyMethodRedefined(&method);
     }
   }
-  return true;
 }
 
-bool Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
+void Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
   // TODO The IFields & SFields pointers should be combined like the methods_ arrays were.
   for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) {
     for (art::ArtField& field : fields_iter) {
@@ -730,28 +784,16 @@
       field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id));
     }
   }
-  return true;
 }
 
 // Performs updates to class that will allow us to verify it.
-bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+void Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
                             art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
   const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
       *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
-  if (class_def == nullptr) {
-    RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
-    return false;
-  }
-  if (!UpdateMethods(mclass, new_dex_cache, *class_def)) {
-    // TODO Investigate appropriate error types.
-    RecordFailure(ERR(INTERNAL), "Unable to update class methods.");
-    return false;
-  }
-  if (!UpdateFields(mclass)) {
-    // TODO Investigate appropriate error types.
-    RecordFailure(ERR(INTERNAL), "Unable to update class fields.");
-    return false;
-  }
+  DCHECK(class_def != nullptr);
+  UpdateMethods(mclass, new_dex_cache, *class_def);
+  UpdateFields(mclass);
 
   // Update the class fields.
   // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
@@ -759,10 +801,9 @@
   mclass->SetDexCache(new_dex_cache.Ptr());
   mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
   mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_)));
-  return true;
 }
 
-bool Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+void Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
                                   art::ObjPtr<art::mirror::LongArray> new_cookie,
                                   /*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie) {
   art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
@@ -779,7 +820,6 @@
   if (!orig_cookie.IsNull()) {
     cookie_field->SetObject<false>(java_dex_file, new_cookie);
   }
-  return true;
 }
 
 // This function does all (java) allocations we need to do for the Class being redefined.
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 9d23ce4..5852309 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -80,6 +80,8 @@
                                   unsigned char* dex_data,
                                   std::string* error_msg);
 
+  static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
+
  private:
   jvmtiError result_;
   art::Runtime* runtime_;
@@ -106,6 +108,10 @@
         error_msg_(error_msg),
         class_sig_(class_sig) { }
 
+  static jvmtiError GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
+                                              /*out*/std::string* error_msg)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
+
   static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
                                                        jint data_len,
                                                        unsigned char* dex_data,
@@ -152,36 +158,47 @@
 
   void RecordFailure(jvmtiError result, const std::string& error_msg);
 
-  // TODO Actually write this.
   // This will check that no constraints are violated (more than 1 class in dex file, any changes in
   // number/declaration of methods & fields, changes in access flags, etc.)
-  bool EnsureRedefinitionIsValid() {
-    LOG(WARNING) << "Redefinition is not checked for validity currently";
+  bool CheckRedefinitionIsValid() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  // Checks that the class can even be redefined.
+  bool CheckRedefinable() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  // Checks that the dex file does not add/remove methods.
+  bool CheckSameMethods() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    LOG(WARNING) << "methods are not checked for modification currently";
     return true;
   }
 
-  bool UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+  // Checks that the dex file does not modify fields
+  bool CheckSameFields() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    LOG(WARNING) << "Fields are not checked for modification currently";
+    return true;
+  }
+
+  // Checks that the dex file contains only the single expected class and that the top-level class
+  // data has not been modified in an incompatible manner.
+  bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
                          art::ObjPtr<art::mirror::LongArray> new_cookie,
                          /*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie)
       REQUIRES(art::Locks::mutator_lock_);
 
-  void RestoreJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
-                          art::ObjPtr<art::mirror::LongArray> original_cookie)
+  void UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
       REQUIRES(art::Locks::mutator_lock_);
 
-  bool UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
-      REQUIRES(art::Locks::mutator_lock_);
-
-  bool UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+  void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
                      art::ObjPtr<art::mirror::DexCache> new_dex_cache,
                      const art::DexFile::ClassDef& class_def)
       REQUIRES(art::Locks::mutator_lock_);
 
-  bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+  void UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
                    art::ObjPtr<art::mirror::DexCache> new_dex_cache)
       REQUIRES(art::Locks::mutator_lock_);
 
-  bool FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
+  void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
       REQUIRES(art::Locks::mutator_lock_);
 
   void FillObsoleteMethodMap(art::mirror::Class* art_klass,
diff --git a/runtime/thread.cc b/runtime/thread.cc
index aff12ff..33c6a40 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2627,10 +2627,9 @@
   QUICK_ENTRY_POINT_INFO(pAllocArray)
   QUICK_ENTRY_POINT_INFO(pAllocArrayResolved)
   QUICK_ENTRY_POINT_INFO(pAllocArrayWithAccessCheck)
-  QUICK_ENTRY_POINT_INFO(pAllocObject)
   QUICK_ENTRY_POINT_INFO(pAllocObjectResolved)
   QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized)
-  QUICK_ENTRY_POINT_INFO(pAllocObjectWithAccessCheck)
+  QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks)
   QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray)
   QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck)
   QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes)
diff --git a/runtime/thread.h b/runtime/thread.h
index 411d85f..6308851 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1416,7 +1416,7 @@
       stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr),
       frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0),
       last_no_thread_suspension_cause(nullptr), checkpoint_function(nullptr),
-      thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_start(nullptr),
+      thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr),
       thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr),
       mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr),
       thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr),
@@ -1540,12 +1540,12 @@
     JniEntryPoints jni_entrypoints;
     QuickEntryPoints quick_entrypoints;
 
+    // Thread-local allocation pointer. Moved here to force alignment for thread_local_pos on ARM.
+    uint8_t* thread_local_start;
     // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for
     // potentially better performance.
     uint8_t* thread_local_pos;
     uint8_t* thread_local_end;
-    // Thread-local allocation pointer.
-    uint8_t* thread_local_start;
 
     size_t thread_local_objects;
 
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index bf9eef8..34f9043 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -653,7 +653,7 @@
   // is done with a timeout so that we can detect problems.
 #if ART_USE_FUTEXES
   timespec wait_timeout;
-  InitTimeSpec(true, CLOCK_MONOTONIC, 10000, 0, &wait_timeout);
+  InitTimeSpec(false, CLOCK_MONOTONIC, 10000, 0, &wait_timeout);
 #endif
   while (true) {
     int32_t cur_val = pending_threads.LoadRelaxed();
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index c4058d6..15cc566 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -335,6 +335,60 @@
   }
 }
 
+mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* destination,
+                                                                  mirror::Class* source) const {
+  DCHECK(destination->IsInterface());
+  DCHECK(IsInClassPath(destination));
+  Thread* thread = Thread::Current();
+  mirror::Class* current = source;
+  // Record the classes that are at the boundary between the compiled DEX files and
+  // the classpath. We will check those classes later to find one class that inherits
+  // `destination`.
+  std::vector<ObjPtr<mirror::Class>> boundaries;
+  // If the destination is a direct interface of a class defined in the DEX files being
+  // compiled, no need to record it.
+  while (!IsInClassPath(current)) {
+    for (size_t i = 0; i < current->NumDirectInterfaces(); ++i) {
+      ObjPtr<mirror::Class> direct = mirror::Class::GetDirectInterface(thread, current, i);
+      if (direct == destination) {
+        return nullptr;
+      } else if (IsInClassPath(direct)) {
+        boundaries.push_back(direct);
+      }
+    }
+    current = current->GetSuperClass();
+  }
+  DCHECK(current != nullptr);
+  boundaries.push_back(current);
+
+  // Check if we have an interface defined in the DEX files being compiled, direclty
+  // inheriting `destination`.
+  int32_t iftable_count = source->GetIfTableCount();
+  ObjPtr<mirror::IfTable> iftable = source->GetIfTable();
+  for (int32_t i = 0; i < iftable_count; ++i) {
+    mirror::Class* itf = iftable->GetInterface(i);
+    if (!IsInClassPath(itf)) {
+      for (size_t j = 0; j < itf->NumDirectInterfaces(); ++j) {
+        ObjPtr<mirror::Class> direct = mirror::Class::GetDirectInterface(thread, itf, j);
+        if (direct == destination) {
+          return nullptr;
+        } else if (IsInClassPath(direct)) {
+          boundaries.push_back(direct);
+        }
+      }
+    }
+  }
+
+  // Find a boundary making `source` inherit from `destination`. We must find one.
+  for (const ObjPtr<mirror::Class>& boundary : boundaries) {
+    if (destination->IsAssignableFrom(boundary)) {
+      return boundary.Ptr();
+    }
+  }
+  LOG(FATAL) << "Should have found a classpath boundary";
+  UNREACHABLE();
+}
+
 void VerifierDeps::AddAssignability(const DexFile& dex_file,
                                     mirror::Class* destination,
                                     mirror::Class* source,
@@ -403,17 +457,26 @@
     return;
   }
 
-  if (!IsInClassPath(source) && !source->IsInterface() && !destination->IsInterface()) {
-    // Find the super class at the classpath boundary. Only that class
-    // can change the assignability.
-    // TODO: also chase the boundary for interfaces.
-    do {
-      source = source->GetSuperClass();
-    } while (!IsInClassPath(source));
+  if (!IsInClassPath(source)) {
+    if (!destination->IsInterface()) {
+      DCHECK(!source->IsInterface());
+      // Find the super class at the classpath boundary. Only that class
+      // can change the assignability.
+      do {
+        source = source->GetSuperClass();
+      } while (!IsInClassPath(source));
 
-    // If that class is the actual destination, no need to record it.
-    if (source == destination) {
-      return;
+      // If that class is the actual destination, no need to record it.
+      if (source == destination) {
+        return;
+      }
+    } else if (is_assignable) {
+      source = FindOneClassPathBoundaryForInterface(destination, source);
+      if (source == nullptr) {
+        // There was no classpath boundary, no need to record.
+        return;
+      }
+      DCHECK(IsInClassPath(source));
     }
   }
 
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 4b8206f..11750fd 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -204,6 +204,13 @@
   bool IsInClassPath(ObjPtr<mirror::Class> klass) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Finds the class in the classpath that makes `source` inherit` from `destination`.
+  // Returns null if a class defined in the compiled DEX files, and assignable to
+  // `source`, direclty inherits from `destination`.
+  mirror::Class* FindOneClassPathBoundaryForInterface(mirror::Class* destination,
+                                                      mirror::Class* source) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Returns the index of `str`. If it is defined in `dex_file_`, this is the dex
   // string ID. If not, an ID is assigned to the string and cached in `strings_`
   // of the corresponding DexFileDeps structure (either provided or inferred from
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 7b5ced1..a5b275c 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -337,7 +337,6 @@
   java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
 
   java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
-  java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
   java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
   java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
   java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
@@ -371,7 +370,6 @@
   java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
   java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
   java_lang_reflect_Executable_artMethod = CacheField(env, java_lang_reflect_Executable, false, "artMethod", "J");
-  java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;");
   java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
   java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J");
   java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "elementData", "[Ljava/lang/Object;");
@@ -398,10 +396,20 @@
 
 void WellKnownClasses::LateInit(JNIEnv* env) {
   ScopedLocalRef<jclass> java_lang_Runtime(env, env->FindClass("java/lang/Runtime"));
+  // CacheField and CacheMethod will initialize their classes. Classes below
+  // have clinit sections that call JNI methods. Late init is required
+  // to make sure these JNI methods are available.
   java_lang_Runtime_nativeLoad =
       CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
                   "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)"
                       "Ljava/lang/String;");
+  java_lang_reflect_Proxy_invoke =
+    CacheMethod(env, java_lang_reflect_Proxy, true, "invoke",
+                "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;"
+                    "[Ljava/lang/Object;)Ljava/lang/Object;");
+  java_lang_reflect_Proxy_h =
+    CacheField(env, java_lang_reflect_Proxy, false, "h",
+               "Ljava/lang/reflect/InvocationHandler;");
 }
 
 ObjPtr<mirror::Class> WellKnownClasses::ToClass(jclass global_jclass) {
diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java
index 5fd51e1..89b9cb4 100644
--- a/test/529-checker-unresolved/src/Main.java
+++ b/test/529-checker-unresolved/src/Main.java
@@ -192,13 +192,13 @@
   /// CHECK-START: void Main.testLicm(int) licm (before)
   /// CHECK:      <<Class:l\d+>>        LoadClass                                     loop:B2
   /// CHECK-NEXT: <<Clinit:l\d+>>       ClinitCheck [<<Class>>]                       loop:B2
-  /// CHECK-NEXT: <<New:l\d+>>          NewInstance [<<Clinit>>,<<Method:[i|j]\d+>>]  loop:B2
+  /// CHECK-NEXT: <<New:l\d+>>          NewInstance [<<Clinit>>]                      loop:B2
   /// CHECK-NEXT:                       InvokeUnresolved [<<New>>]                    loop:B2
 
   /// CHECK-START: void Main.testLicm(int) licm (after)
   /// CHECK:      <<Class:l\d+>>        LoadClass                                     loop:none
   /// CHECK-NEXT: <<Clinit:l\d+>>       ClinitCheck [<<Class>>]                       loop:none
-  /// CHECK:      <<New:l\d+>>          NewInstance [<<Clinit>>,<<Method:[i|j]\d+>>]  loop:B2
+  /// CHECK:      <<New:l\d+>>          NewInstance [<<Clinit>>]                      loop:B2
   /// CHECK-NEXT:                       InvokeUnresolved [<<New>>]                    loop:B2
   static public void testLicm(int count) {
     // Test to make sure we keep the initialization check after loading an unresolved class.
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index 26475ae..ed7524c 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -153,8 +153,8 @@
   /// CHECK-DAG:  <<Pos:i\d+>>      ParameterValue
   /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
   /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
-  /// CHECK-DAG:                    BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
-  /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+  /// CHECK-DAG:  <<Bounds:i\d+>>   BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+  /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
   /// CHECK-DAG:                    Return [<<Char>>]
 
   /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
@@ -174,8 +174,8 @@
   /// CHECK-DAG:  <<Pos:i\d+>>      ParameterValue
   /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
   /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
-  /// CHECK-DAG:                    BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
-  /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+  /// CHECK-DAG:  <<Bounds:i\d+>>   BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+  /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
   /// CHECK-DAG:                    Return [<<Char>>]
 
   /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
diff --git a/test/621-checker-new-instance/info.txt b/test/621-checker-new-instance/info.txt
deleted file mode 100644
index c27c45c..0000000
--- a/test/621-checker-new-instance/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for removing useless load class.
diff --git a/test/621-checker-new-instance/src/Main.java b/test/621-checker-new-instance/src/Main.java
deleted file mode 100644
index 68a4644..0000000
--- a/test/621-checker-new-instance/src/Main.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-public class Main {
-  /// CHECK-START: java.lang.Object Main.newObject() prepare_for_register_allocation (before)
-  /// CHECK: LoadClass
-  /// CHECK: NewInstance
-
-  /// CHECK-START: java.lang.Object Main.newObject() prepare_for_register_allocation (after)
-  /// CHECK-NOT: LoadClass
-  /// CHECK: NewInstance
-  public static Object newObject() {
-      return new Object();
-  }
-
-  /// CHECK-START: java.lang.Object Main.newFinalizableMayThrow() prepare_for_register_allocation (after)
-  /// CHECK: LoadClass
-  /// CHECK: NewInstance
-  public static Object newFinalizableMayThrow() {
-      return $inline$newFinalizableMayThrow();
-  }
-
-  public static Object $inline$newFinalizableMayThrow() {
-      return new FinalizableMayThrow();
-  }
-
-  public static void main(String[] args) {
-      newFinalizableMayThrow();
-      newObject();
-  }
-}
-
-class FinalizableMayThrow {
-    // clinit may throw OOME.
-    static Object o = new Object();
-    static String s;
-    public void finalize() {
-        s = "Test";
-    }
-}
diff --git a/test/621-checker-new-instance/expected.txt b/test/631-checker-get-class/expected.txt
similarity index 100%
rename from test/621-checker-new-instance/expected.txt
rename to test/631-checker-get-class/expected.txt
diff --git a/test/631-checker-get-class/info.txt b/test/631-checker-get-class/info.txt
new file mode 100644
index 0000000..f236a22
--- /dev/null
+++ b/test/631-checker-get-class/info.txt
@@ -0,0 +1,4 @@
+Checker test to make sure we recognize the pattern:
+if (foo.getClass() == Foo.class)
+
+For doing better type propagation.
diff --git a/test/631-checker-get-class/src/Main.java b/test/631-checker-get-class/src/Main.java
new file mode 100644
index 0000000..61c0adf
--- /dev/null
+++ b/test/631-checker-get-class/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 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 {
+
+  /// CHECK-START: int Main.bar(Main) inliner (before)
+  /// CHECK: InvokeVirtual
+  /// CHECK: InvokeVirtual
+
+  /// CHECK-START: int Main.bar(Main) inliner (after)
+  /// CHECK-NOT: InvokeVirtual
+  public static int bar(Main m) {
+    if (m.getClass() == Main.class) {
+      return m.foo();
+    }
+    return 4;
+  }
+
+  public int foo() {
+    return 42;
+  }
+
+  /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality1() {
+    return new Main().getClass() == Main.class;
+  }
+
+  /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality2() {
+    Object o = new SubMain();
+    return o.getClass() == Main.class;
+  }
+
+  /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality3() {
+    return new Main().getClass() != Main.class;
+  }
+
+  /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Eq:z\d+>>     {{Equal|NotEqual}}
+  /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+  /// CHECK-DAG:                 Return [<<Select>>]
+
+  /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
+  /// CHECK-DAG:                   Return [<<Constant>>]
+  public static boolean classEquality4() {
+    Object o = new SubMain();
+    return o.getClass() != Main.class;
+  }
+
+  public static void main(String[] args) {
+    int actual = bar(new Main());
+    if (actual != 42) {
+      throw new Error("Expected 42, got " + actual);
+    }
+    actual = bar(new SubMain());
+    if (actual != 4) {
+      throw new Error("Expected 4, got " + actual);
+    }
+  }
+}
+
+class SubMain extends Main {
+}
diff --git a/test/621-checker-new-instance/expected.txt b/test/632-checker-char-at-bounds/expected.txt
similarity index 100%
copy from test/621-checker-new-instance/expected.txt
copy to test/632-checker-char-at-bounds/expected.txt
diff --git a/test/632-checker-char-at-bounds/info.txt b/test/632-checker-char-at-bounds/info.txt
new file mode 100644
index 0000000..10b9a44
--- /dev/null
+++ b/test/632-checker-char-at-bounds/info.txt
@@ -0,0 +1,2 @@
+Regression test for the optimization of String.charAt, which
+had its SSA dependency incorrect with its corresponding bound check.
diff --git a/test/632-checker-char-at-bounds/src/Main.java b/test/632-checker-char-at-bounds/src/Main.java
new file mode 100644
index 0000000..65022d0
--- /dev/null
+++ b/test/632-checker-char-at-bounds/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  /// CHECK-START: void Main.main(java.lang.String[]) licm (after)
+  /// CHECK-DAG: <<NullCheck:l\d+>>   NullCheck
+  /// CHECK-DAG: <<BoundsCheck:i\d+>> BoundsCheck
+  /// CHECK-DAG:                      ArrayGet [<<NullCheck>>,<<BoundsCheck>>]
+  public static void main(String[] args) {
+    try {
+      String foo = myString;
+      foo.getClass(); // Make sure the null check is not in the loop.
+      char c = 0;
+      for (int i = 0; i < 10; i++) {
+        // The charAt may be licm'ed, but it has to be licm'ed with its
+        // bounds check.
+        c = foo.charAt(10000000);
+      }
+      System.out.println(c);
+    } catch (StringIndexOutOfBoundsException e) {
+      // Expected
+    }
+  }
+
+  static String myString = "foo";
+}
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index 1557d45..60a31bd 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -44,6 +44,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error setting tag: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
@@ -56,6 +57,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error getting tag: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
   return tag;
 }
@@ -90,6 +92,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Failure running GetLoadedClasses: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return nullptr;
   }
 
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index 9261a9f..f993606 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -69,6 +69,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error setting callbacks: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
@@ -84,6 +85,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error enabling/disabling allocation tracking: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index fc43acc..7f295ac 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -50,6 +50,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error setting callbacks: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
@@ -64,6 +65,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error enabling/disabling object-free callbacks: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 8dac89d..a2fd591 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -61,6 +61,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Failure running IterateThroughHeap: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return false;
   }
   return true;
diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc
index afbb774..36d33b6 100644
--- a/test/907-get-loaded-classes/get_loaded_classes.cc
+++ b/test/907-get-loaded-classes/get_loaded_classes.cc
@@ -48,6 +48,7 @@
     char* err;
     jvmti_env->GetErrorName(result, &err);
     printf("Failure running GetLoadedClasses: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return nullptr;
   }
 
diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc
index 771d1ad..1fab79d 100644
--- a/test/908-gc-start-finish/gc_callbacks.cc
+++ b/test/908-gc-start-finish/gc_callbacks.cc
@@ -51,6 +51,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error setting callbacks: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
@@ -65,6 +66,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error enabling/disabling gc callbacks: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
   ret = jvmti_env->SetEventNotificationMode(
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
@@ -74,6 +76,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error enabling/disabling gc callbacks: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
index 9a74799..c913b3f 100644
--- a/test/910-methods/expected.txt
+++ b/test/910-methods/expected.txt
@@ -1,15 +1,59 @@
 [toString, ()Ljava/lang/String;, null]
 class java.lang.Object
 1
+Max locals: 3
+Argument size: 1
+Location start: 0
+Location end: 40
+Is native: false
+Is obsolete: false
+Is synthetic: false
 [charAt, (I)C, null]
 class java.lang.String
 257
+Max locals: JVMTI_ERROR_NATIVE_METHOD
+Argument size: JVMTI_ERROR_NATIVE_METHOD
+Location start: JVMTI_ERROR_NATIVE_METHOD
+Location end: JVMTI_ERROR_NATIVE_METHOD
+Is native: true
+Is obsolete: false
+Is synthetic: false
 [sqrt, (D)D, null]
 class java.lang.Math
 265
+Max locals: JVMTI_ERROR_NATIVE_METHOD
+Argument size: JVMTI_ERROR_NATIVE_METHOD
+Location start: JVMTI_ERROR_NATIVE_METHOD
+Location end: JVMTI_ERROR_NATIVE_METHOD
+Is native: true
+Is obsolete: false
+Is synthetic: false
 [add, (Ljava/lang/Object;)Z, null]
 interface java.util.List
 1025
+Max locals: 0
+Argument size: 0
+Location start: 0
+Location end: 0
+Is native: false
+Is obsolete: false
+Is synthetic: false
 [run, ()V, null]
 class $Proxy0
 17
+Max locals: 0
+Argument size: 0
+Location start: 0
+Location end: 0
+Is native: false
+Is obsolete: false
+Is synthetic: false
+class Main$NestedSynthetic
+4104
+Max locals: 1
+Argument size: 0
+Location start: 0
+Location end: 2
+Is native: false
+Is obsolete: false
+Is synthetic: true
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index 3ed91d7..fa9679d 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -41,6 +41,7 @@
     char* err;
     jvmti_env->GetErrorName(result, &err);
     printf("Failure running GetMethodName: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return nullptr;
   }
 
@@ -72,6 +73,7 @@
     char* err;
     jvmti_env->GetErrorName(result2, &err);
     printf("Failure running GetMethodName(null, null, null): %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return nullptr;
   }
 
@@ -88,6 +90,7 @@
     char* err;
     jvmti_env->GetErrorName(result, &err);
     printf("Failure running GetMethodDeclaringClass: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return nullptr;
   }
 
@@ -104,12 +107,106 @@
     char* err;
     jvmti_env->GetErrorName(result, &err);
     printf("Failure running GetMethodModifiers: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return 0;
   }
 
   return modifiers;
 }
 
+extern "C" JNIEXPORT jint JNICALL Java_Main_getMaxLocals(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jint max_locals;
+  jvmtiError result = jvmti_env->GetMaxLocals(id, &max_locals);
+  if (JvmtiErrorToException(env, result)) {
+    return -1;
+  }
+
+  return max_locals;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getArgumentsSize(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jint arguments;
+  jvmtiError result = jvmti_env->GetArgumentsSize(id, &arguments);
+  if (JvmtiErrorToException(env, result)) {
+    return -1;
+  }
+
+  return arguments;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getMethodLocationStart(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jlong start;
+  jlong end;
+  jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
+  if (JvmtiErrorToException(env, result)) {
+    return -1;
+  }
+
+  return start;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getMethodLocationEnd(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jlong start;
+  jlong end;
+  jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
+  if (JvmtiErrorToException(env, result)) {
+    return -1;
+  }
+
+  return end;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodNative(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jboolean is_native;
+  jvmtiError result = jvmti_env->IsMethodNative(id, &is_native);
+  if (JvmtiErrorToException(env, result)) {
+    return JNI_FALSE;
+  }
+
+  return is_native;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodObsolete(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jboolean is_obsolete;
+  jvmtiError result = jvmti_env->IsMethodObsolete(id, &is_obsolete);
+  if (JvmtiErrorToException(env, result)) {
+    return JNI_FALSE;
+  }
+
+  return is_obsolete;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodSynthetic(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jboolean is_synthetic;
+  jvmtiError result = jvmti_env->IsMethodSynthetic(id, &is_synthetic);
+  if (JvmtiErrorToException(env, result)) {
+    return JNI_FALSE;
+  }
+
+  return is_synthetic;
+}
+
 // Don't do anything
 jint OnLoad(JavaVM* vm,
             char* options ATTRIBUTE_UNUSED,
diff --git a/test/910-methods/src/Main.java b/test/910-methods/src/Main.java
index 3459134..bf25a0d 100644
--- a/test/910-methods/src/Main.java
+++ b/test/910-methods/src/Main.java
@@ -32,6 +32,10 @@
     testMethod("java.util.List", "add", Object.class);
 
     testMethod(getProxyClass(), "run");
+
+    // Find a synthetic method in the dummy inner class. Do not print the name. Javac and Jack
+    // disagree on the naming of synthetic accessors.
+    testMethod(findSyntheticMethod(), NestedSynthetic.class, false);
   }
 
   private static Class<?> proxyClass = null;
@@ -54,8 +58,17 @@
   private static void testMethod(Class<?> base, String methodName, Class<?>... types)
       throws Exception {
     Method m = base.getDeclaredMethod(methodName, types);
+    testMethod(m, base, true);
+  }
+
+  private static void testMethod(Method m, Class<?> base, boolean printName) {
     String[] result = getMethodName(m);
-    System.out.println(Arrays.toString(result));
+    if (!result[0].equals(m.getName())) {
+      throw new RuntimeException("Name not equal: " + m.getName() + " vs " + result[0]);
+    }
+    if (printName) {
+      System.out.println(Arrays.toString(result));
+    }
 
     Class<?> declClass = getMethodDeclaringClass(m);
     if (base != declClass) {
@@ -68,9 +81,67 @@
       throw new RuntimeException("Modifiers not equal: " + m.getModifiers() + " vs " + modifiers);
     }
     System.out.println(modifiers);
+
+    System.out.print("Max locals: ");
+    try {
+      System.out.println(getMaxLocals(m));
+    } catch (RuntimeException e) {
+      System.out.println(e.getMessage());
+    }
+
+    System.out.print("Argument size: ");
+    try {
+      System.out.println(getArgumentsSize(m));
+    } catch (RuntimeException e) {
+      System.out.println(e.getMessage());
+    }
+
+    System.out.print("Location start: ");
+    try {
+      System.out.println(getMethodLocationStart(m));
+    } catch (RuntimeException e) {
+      System.out.println(e.getMessage());
+    }
+
+    System.out.print("Location end: ");
+    try {
+      System.out.println(getMethodLocationEnd(m));
+    } catch (RuntimeException e) {
+      System.out.println(e.getMessage());
+    }
+
+    System.out.println("Is native: " + isMethodNative(m));
+    System.out.println("Is obsolete: " + isMethodObsolete(m));
+    System.out.println("Is synthetic: " + isMethodSynthetic(m));
+  }
+
+  private static class NestedSynthetic {
+    // Accessing this private field will create a synthetic accessor method;
+    private static String dummy;
+  }
+
+  private static void dummyAccess() {
+    System.out.println(NestedSynthetic.dummy);
+  }
+
+  private static Method findSyntheticMethod() throws Exception {
+    Method methods[] = NestedSynthetic.class.getDeclaredMethods();
+    for (Method m : methods) {
+      if (m.isSynthetic()) {
+        return m;
+      }
+    }
+    throw new RuntimeException("Could not find synthetic method");
   }
 
   private static native String[] getMethodName(Method m);
   private static native Class<?> getMethodDeclaringClass(Method m);
   private static native int getMethodModifiers(Method m);
+  private static native int getMaxLocals(Method m);
+  private static native int getArgumentsSize(Method m);
+  private static native long getMethodLocationStart(Method m);
+  private static native long getMethodLocationEnd(Method m);
+  private static native boolean isMethodNative(Method m);
+  private static native boolean isMethodObsolete(Method m);
+  private static native boolean isMethodSynthetic(Method m);
 }
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index 57f6a92..b3e8bc3 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -63,6 +63,7 @@
       char* err;
       jvmti_env->GetErrorName(result, &err);
       printf("Failure running GetStackTrace: %s\n", err);
+      jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
       return nullptr;
     }
   }
@@ -77,6 +78,7 @@
         char* err;
         jvmti_env->GetErrorName(result2, &err);
         printf("Failure running GetMethodName: %s\n", err);
+        jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
         return nullptr;
       }
     }
@@ -94,6 +96,7 @@
           char* err;
           jvmti_env->GetErrorName(line_result, &err);
           printf("Failure running GetLineNumberTable: %s\n", err);
+          jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
           return nullptr;
         }
         line_number_table = nullptr;
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 838a92a..38a4f0e 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -29,6 +29,20 @@
 namespace art {
 namespace Test912Classes {
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isModifiableClass(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jboolean res = JNI_FALSE;
+  jvmtiError result = jvmti_env->IsModifiableClass(klass, &res);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running IsModifiableClass: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return JNI_FALSE;
+  }
+  return res;
+}
+
 extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassSignature(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
   char* sig;
@@ -38,6 +52,7 @@
     char* err;
     jvmti_env->GetErrorName(result, &err);
     printf("Failure running GetClassSignature: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return nullptr;
   }
 
@@ -61,6 +76,154 @@
   return ret;
 }
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterface(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jboolean is_interface = JNI_FALSE;
+  jvmtiError result = jvmti_env->IsInterface(klass, &is_interface);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running IsInterface: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return JNI_FALSE;
+  }
+  return is_interface;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isArrayClass(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jboolean is_array_class = JNI_FALSE;
+  jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running IsArrayClass: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return JNI_FALSE;
+  }
+  return is_array_class;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getClassModifiers(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jint mod;
+  jvmtiError result = jvmti_env->GetClassModifiers(klass, &mod);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetClassModifiers: %s\n", err);
+    return JNI_FALSE;
+  }
+  return mod;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jint count = 0;
+  jfieldID* fields = nullptr;
+  jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetClassFields: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return nullptr;
+  }
+
+  auto callback = [&](jint i) {
+    jint modifiers;
+    // Ignore any errors for simplicity.
+    jvmti_env->GetFieldModifiers(klass, fields[i], &modifiers);
+    constexpr jint kStatic = 0x8;
+    return env->ToReflectedField(klass,
+                                 fields[i],
+                                 (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+  };
+  jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
+  if (fields != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassMethods(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jint count = 0;
+  jmethodID* methods = nullptr;
+  jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetClassMethods: %s\n", err);
+    return nullptr;
+  }
+
+  auto callback = [&](jint i) {
+    jint modifiers;
+    // Ignore any errors for simplicity.
+    jvmti_env->GetMethodModifiers(methods[i], &modifiers);
+    constexpr jint kStatic = 0x8;
+    return env->ToReflectedMethod(klass,
+                                  methods[i],
+                                  (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+  };
+  jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
+  if (methods != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(methods));
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getImplementedInterfaces(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jint count = 0;
+  jclass* classes = nullptr;
+  jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetImplementedInterfaces: %s\n", err);
+    return nullptr;
+  }
+
+  auto callback = [&](jint i) {
+    return classes[i];
+  };
+  jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback);
+  if (classes != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getClassStatus(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jint status;
+  jvmtiError result = jvmti_env->GetClassStatus(klass, &status);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetClassStatus: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return JNI_FALSE;
+  }
+  return status;
+}
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+  jobject classloader;
+  jvmtiError result = jvmti_env->GetClassLoader(klass, &classloader);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetClassLoader: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return nullptr;
+  }
+  return classloader;
+}
+
 // Don't do anything
 jint OnLoad(JavaVM* vm,
             char* options ATTRIBUTE_UNUSED,
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 71b22f4..44c861a 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -1,7 +1,45 @@
 [Ljava/lang/Object;, null]
+1
 [Ljava/lang/String;, null]
+11
 [Ljava/lang/Math;, null]
+11
 [Ljava/util/List;, null]
+601
 [L$Proxy0;, null]
+11
 [I, null]
+411
 [[D, null]
+411
+int interface=false array=false modifiable=false
+$Proxy0 interface=false array=false modifiable=false
+java.lang.Runnable interface=true array=false modifiable=false
+java.lang.String interface=false array=false modifiable=true
+[I interface=false array=true modifiable=false
+[Ljava.lang.Runnable; interface=false array=true modifiable=false
+[Ljava.lang.String; interface=false array=true modifiable=false
+[public static final int java.lang.Integer.BYTES, static final char[] java.lang.Integer.DigitOnes, static final char[] java.lang.Integer.DigitTens, public static final int java.lang.Integer.MAX_VALUE, public static final int java.lang.Integer.MIN_VALUE, public static final int java.lang.Integer.SIZE, private static final java.lang.String[] java.lang.Integer.SMALL_NEG_VALUES, private static final java.lang.String[] java.lang.Integer.SMALL_NONNEG_VALUES, public static final java.lang.Class java.lang.Integer.TYPE, static final char[] java.lang.Integer.digits, private static final long java.lang.Integer.serialVersionUID, static final int[] java.lang.Integer.sizeTable, private final int java.lang.Integer.value]
+[]
+[]
+[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), static int java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static void java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()]
+[]
+[]
+int 100000
+class [Ljava.lang.String; 10000
+class java.lang.Object 111
+class Main$TestForNonInit 11
+class Main$TestForInitFail 1001
+int []
+class [Ljava.lang.String; []
+class java.lang.Object []
+interface Main$InfA []
+interface Main$InfB [interface Main$InfA]
+interface Main$InfC [interface Main$InfB]
+class Main$ClassA [interface Main$InfA]
+class Main$ClassB [interface Main$InfB]
+class Main$ClassC [interface Main$InfA, interface Main$InfC]
+class java.lang.String null
+class [Ljava.lang.String; null
+interface Main$InfA dalvik.system.PathClassLoader
+class $Proxy0 dalvik.system.PathClassLoader
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 025584e..e627d42 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -34,6 +34,48 @@
 
     testClass(int.class);
     testClass(double[].class);
+
+    testClassType(int.class);
+    testClassType(getProxyClass());
+    testClassType(Runnable.class);
+    testClassType(String.class);
+
+    testClassType(int[].class);
+    testClassType(Runnable[].class);
+    testClassType(String[].class);
+
+    testClassFields(Integer.class);
+    testClassFields(int.class);
+    testClassFields(String[].class);
+
+    testClassMethods(Integer.class);
+    testClassMethods(int.class);
+    testClassMethods(String[].class);
+
+    testClassStatus(int.class);
+    testClassStatus(String[].class);
+    testClassStatus(Object.class);
+    testClassStatus(TestForNonInit.class);
+    try {
+      System.out.println(TestForInitFail.dummy);
+    } catch (ExceptionInInitializerError e) {
+    }
+    testClassStatus(TestForInitFail.class);
+
+    testInterfaces(int.class);
+    testInterfaces(String[].class);
+    testInterfaces(Object.class);
+    testInterfaces(InfA.class);
+    testInterfaces(InfB.class);
+    testInterfaces(InfC.class);
+    testInterfaces(ClassA.class);
+    testInterfaces(ClassB.class);
+    testInterfaces(ClassC.class);
+
+    testClassLoader(String.class);
+    testClassLoader(String[].class);
+    testClassLoader(InfA.class);
+    testClassLoader(getProxyClass());
   }
 
   private static Class<?> proxyClass = null;
@@ -55,7 +97,95 @@
   private static void testClass(Class<?> base) throws Exception {
     String[] result = getClassSignature(base);
     System.out.println(Arrays.toString(result));
+    int mod = getClassModifiers(base);
+    if (mod != base.getModifiers()) {
+      throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod);
+    }
+    System.out.println(Integer.toHexString(mod));
   }
 
+  private static void testClassType(Class<?> c) throws Exception {
+    boolean isInterface = isInterface(c);
+    boolean isArray = isArrayClass(c);
+    boolean isModifiable = isModifiableClass(c);
+    System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray +
+        " modifiable=" + isModifiable);
+  }
+
+  private static void testClassFields(Class<?> c) throws Exception {
+    System.out.println(Arrays.toString(getClassFields(c)));
+  }
+
+  private static void testClassMethods(Class<?> c) throws Exception {
+    System.out.println(Arrays.toString(getClassMethods(c)));
+  }
+
+  private static void testClassStatus(Class<?> c) {
+    System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
+  }
+
+  private static void testInterfaces(Class<?> c) {
+    System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c)));
+  }
+
+  private static boolean IsBootClassLoader(ClassLoader l) {
+    // Hacky check for Android's fake boot classloader.
+    return l.getClass().getName().equals("java.lang.BootClassLoader");
+  }
+
+  private static void testClassLoader(Class<?> c) {
+    Object cl = getClassLoader(c);
+    System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null"));
+    if (cl == null) {
+      if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) {
+        throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null.");
+      }
+    } else {
+      if (!(cl instanceof ClassLoader)) {
+        throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() +
+            ")");
+      }
+      if (cl != c.getClassLoader()) {
+        throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl);
+      }
+    }
+  }
+
+  private static native boolean isModifiableClass(Class<?> c);
   private static native String[] getClassSignature(Class<?> c);
+
+  private static native boolean isInterface(Class<?> c);
+  private static native boolean isArrayClass(Class<?> c);
+
+  private static native int getClassModifiers(Class<?> c);
+
+  private static native Object[] getClassFields(Class<?> c);
+  private static native Object[] getClassMethods(Class<?> c);
+  private static native Class[] getImplementedInterfaces(Class<?> c);
+
+  private static native int getClassStatus(Class<?> c);
+
+  private static native Object getClassLoader(Class<?> c);
+
+  private static class TestForNonInit {
+    public static double dummy = Math.random();  // So it can't be compile-time initialized.
+  }
+
+  private static class TestForInitFail {
+    public static int dummy = ((int)Math.random())/0;  // So it throws when initializing.
+  }
+
+  public static interface InfA {
+  }
+  public static interface InfB extends InfA {
+  }
+  public static interface InfC extends InfB {
+  }
+
+  public abstract static class ClassA implements InfA {
+  }
+  public abstract static class ClassB extends ClassA implements InfB {
+  }
+  public abstract static class ClassC implements InfA, InfC {
+  }
 }
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 49ab7dd..0b232af 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -49,6 +49,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Error forcing a garbage collection: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
   }
 }
 
@@ -106,6 +107,7 @@
     char* err;
     jvmti_env->GetErrorName(ret, &err);
     printf("Failure running FollowReferences: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
     return false;
   }
   return true;
diff --git a/test/918-fields/build b/test/918-fields/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/918-fields/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/918-fields/expected.txt b/test/918-fields/expected.txt
new file mode 100644
index 0000000..39d3e70
--- /dev/null
+++ b/test/918-fields/expected.txt
@@ -0,0 +1,16 @@
+[PI, D, null]
+class java.lang.Math
+25
+false
+[value, I, null]
+class java.lang.Integer
+18
+false
+[this$0, LMain;, null]
+class Main$Foo
+4112
+true
+[VAL, I, null]
+interface Main$Bar
+25
+false
diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc
new file mode 100644
index 0000000..4d2b34b
--- /dev/null
+++ b/test/918-fields/fields.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 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 "fields.h"
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test918Fields {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getFieldName(
+    JNIEnv* env, jclass klass, jobject field) {
+  jfieldID id = env->FromReflectedField(field);
+
+  char* name;
+  char* sig;
+  char* gen;
+  // Note: technically putting the caller class here is wrong, but we don't need it, anyways.
+  jvmtiError result = jvmti_env->GetFieldName(klass, id, &name, &sig, &gen);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetFieldName: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return nullptr;
+  }
+
+  auto callback = [&](jint i) {
+    if (i == 0) {
+      return name == nullptr ? nullptr : env->NewStringUTF(name);
+    } else if (i == 1) {
+      return sig == nullptr ? nullptr : env->NewStringUTF(sig);
+    } else {
+      return gen == nullptr ? nullptr : env->NewStringUTF(gen);
+    }
+  };
+  jobjectArray ret = CreateObjectArray(env, 3, "java/lang/String", callback);
+
+  // Need to deallocate the strings.
+  if (name != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
+  }
+  if (sig != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+  }
+  if (gen != nullptr) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
+  }
+
+  // Also run GetMethodName with all parameter pointers null to check for segfaults.
+  jvmtiError result2 = jvmti_env->GetFieldName(klass, id, nullptr, nullptr, nullptr);
+  if (result2 != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result2, &err);
+    printf("Failure running GetFieldName(null, null, null): %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return nullptr;
+  }
+
+  return ret;
+}
+
+extern "C" JNIEXPORT jclass JNICALL Java_Main_getFieldDeclaringClass(
+    JNIEnv* env, jclass klass, jobject field) {
+  jfieldID id = env->FromReflectedField(field);
+
+  jclass declaring_class;
+  jvmtiError result = jvmti_env->GetFieldDeclaringClass(klass, id, &declaring_class);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetFieldDeclaringClass: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return nullptr;
+  }
+
+  return declaring_class;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldModifiers(
+    JNIEnv* env, jclass klass, jobject field) {
+  jfieldID id = env->FromReflectedField(field);
+
+  jint modifiers;
+  jvmtiError result = jvmti_env->GetFieldModifiers(klass, id, &modifiers);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetFieldModifiers: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return 0;
+  }
+
+  return modifiers;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isFieldSynthetic(
+    JNIEnv* env, jclass klass, jobject field) {
+  jfieldID id = env->FromReflectedField(field);
+
+  jboolean synth;
+  jvmtiError result = jvmti_env->IsFieldSynthetic(klass, id, &synth);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running IsFieldSynthetic: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return 0;
+  }
+
+  return synth;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  SetAllCapabilities(jvmti_env);
+  return 0;
+}
+
+}  // namespace Test918Fields
+}  // namespace art
diff --git a/test/918-fields/fields.h b/test/918-fields/fields.h
new file mode 100644
index 0000000..89bd161
--- /dev/null
+++ b/test/918-fields/fields.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_918_FIELDS_FIELDS_H_
+#define ART_TEST_918_FIELDS_FIELDS_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test918Fields {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test918Fields
+}  // namespace art
+
+#endif  // ART_TEST_918_FIELDS_FIELDS_H_
diff --git a/test/918-fields/info.txt b/test/918-fields/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/918-fields/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/918-fields/run b/test/918-fields/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/918-fields/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java
new file mode 100644
index 0000000..8af6e7b
--- /dev/null
+++ b/test/918-fields/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  public static void doTest() throws Exception {
+    testField(Math.class, "PI");
+    testField(Integer.class, "value");
+    testField(Foo.class, "this$0");
+    testField(Bar.class, "VAL");
+  }
+
+  private static void testField(Class<?> base, String fieldName)
+      throws Exception {
+    Field f = base.getDeclaredField(fieldName);
+    String[] result = getFieldName(f);
+    System.out.println(Arrays.toString(result));
+
+    Class<?> declClass = getFieldDeclaringClass(f);
+    if (base != declClass) {
+      throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+    }
+    System.out.println(declClass);
+
+    int modifiers = getFieldModifiers(f);
+    if (modifiers != f.getModifiers()) {
+      throw new RuntimeException("Modifiers not equal: " + f.getModifiers() + " vs " + modifiers);
+    }
+    System.out.println(modifiers);
+
+    boolean synth = isFieldSynthetic(f);
+    if (synth != f.isSynthetic()) {
+      throw new RuntimeException("Synthetic not equal: " + f.isSynthetic() + " vs " + synth);
+    }
+    System.out.println(synth);
+  }
+
+  private static native String[] getFieldName(Field f);
+  private static native Class<?> getFieldDeclaringClass(Field f);
+  private static native int getFieldModifiers(Field f);
+  private static native boolean isFieldSynthetic(Field f);
+
+  private class Foo {
+  }
+
+  private static interface Bar {
+    public static int VAL = 1;
+  }
+}
diff --git a/test/919-obsolete-fields/build b/test/919-obsolete-fields/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/919-obsolete-fields/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/919-obsolete-fields/expected.txt b/test/919-obsolete-fields/expected.txt
new file mode 100644
index 0000000..4caefc6
--- /dev/null
+++ b/test/919-obsolete-fields/expected.txt
@@ -0,0 +1,21 @@
+Pre Start private method call
+hello - private
+Post Start private method call
+Not doing anything here
+Pre Finish private method call
+goodbye - private
+Post Finish private method call
+Pre Start private method call
+hello - private
+Post Start private method call
+transforming calling function
+Pre Finish private method call
+Goodbye - private - Transformed
+Post Finish private method call
+pre Start private method call - Transformed
+Hello - private - Transformed
+post Start private method call - Transformed
+Not doing anything here
+pre Finish private method call - Transformed
+Goodbye - private - Transformed
+post Finish private method call - Transformed
diff --git a/test/919-obsolete-fields/info.txt b/test/919-obsolete-fields/info.txt
new file mode 100644
index 0000000..97739ca
--- /dev/null
+++ b/test/919-obsolete-fields/info.txt
@@ -0,0 +1 @@
+Tests obsolete method field access support
diff --git a/test/919-obsolete-fields/run b/test/919-obsolete-fields/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/919-obsolete-fields/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java
new file mode 100644
index 0000000..895c7a3
--- /dev/null
+++ b/test/919-obsolete-fields/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.function.Consumer;
+import java.util.Base64;
+
+public class Main {
+
+  // What follows is the base64 encoded representation of the following class:
+  //
+  // import java.util.function.Consumer;
+  //
+  // class Transform {
+  //   private Consumer<String> reporter;
+  //   public Transform(Consumer<String> reporter) {
+  //     this.reporter = reporter;
+  //   }
+  //
+  //   private void Start() {
+  //     reporter.accept("Hello - private - Transformed");
+  //   }
+  //
+  //   private void Finish() {
+  //     reporter.accept("Goodbye - private - Transformed");
+  //   }
+  //
+  //   public void sayHi(Runnable r) {
+  //     reporter.accept("pre Start private method call - Transformed");
+  //     Start();
+  //     reporter.accept("post Start private method call - Transformed");
+  //     r.run();
+  //     reporter.accept("pre Finish private method call - Transformed");
+  //     Finish();
+  //     reporter.accept("post Finish private method call - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQANAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
+    "LAcALQcALgEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
+    "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
+    "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
+    "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
+    "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
+    "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwAEwAZDAAPABABAB1IZWxs" +
+    "byAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAcALwwAMAAxAQAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
+    "cmFuc2Zvcm1lZAEAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQM" +
+    "ABgAGQEALHBvc3QgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkBwAyDAAz" +
+    "ABkBACxwcmUgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAGgAZAQAt" +
+    "cG9zdCBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkAQAJVHJhbnNmb3Jt" +
+    "AQAQamF2YS9sYW5nL09iamVjdAEAG2phdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcgEABmFjY2Vw" +
+    "dAEAFShMamF2YS9sYW5nL09iamVjdDspVgEAEmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgAgAA0A" +
+    "DgAAAAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAA" +
+    "AAEAFgAAAA4AAwAAABUABAAWAAkAFwARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAIS" +
+    "A7kABAIAsQAAAAEAFgAAAAoAAgAAABoACwAbAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkA" +
+    "BAIAsQAAAAEAFgAAAAoAAgAAAB4ACwAfAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIA" +
+    "KrcAByq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAA" +
+    "IgAIAAAAIgALACMADwAkABoAJQAgACYAKwAnAC8AKAA6ACkAAQAdAAAAAgAe");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAw/b59wCwTlSVDmuhPEezuK3oe0rtT4ujMBQAAcAAAAHhWNBIAAAAAAAAAAAgFAAAd" +
+    "AAAAcAAAAAYAAADkAAAABAAAAPwAAAABAAAALAEAAAcAAAA0AQAAAQAAAGwBAABABAAAjAEAAJoC" +
+    "AACdAgAAoAIAAKgCAACsAgAAsgIAALoCAADbAgAA+gIAAAcDAAAmAwAAOgMAAFADAABkAwAAggMA" +
+    "AKEDAACoAwAAuAMAALsDAAC/AwAAxwMAANsDAAAKBAAAOAQAAGYEAACTBAAAnQQAAKIEAACpBAAA" +
+    "CAAAAAkAAAAKAAAACwAAAA4AAAARAAAAEQAAAAUAAAAAAAAAEgAAAAUAAACEAgAAEgAAAAUAAACM" +
+    "AgAAEgAAAAUAAACUAgAAAAAEABkAAAAAAAMAAgAAAAAAAAAFAAAAAAAAAA8AAAAAAAIAGwAAAAIA" +
+    "AAACAAAAAwAAABoAAAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAQAAAAZAIAAO8EAAAAAAAAAQAA" +
+    "ANEEAAABAAAA3wQAAAIAAgABAAAAsAQAAAYAAABwEAQAAABbAQAADgADAAEAAgAAALgEAAAJAAAA" +
+    "VCAAABsBBgAAAHIgBgAQAA4AAAADAAEAAgAAAL4EAAAJAAAAVCAAABsBBwAAAHIgBgAQAA4AAAAE" +
+    "AAIAAgAAAMQEAAAqAAAAVCAAABsBGAAAAHIgBgAQAHAQAgACAFQgAAAbARYAAAByIAYAEAByEAUA" +
+    "AwBUIAAAGwEXAAAAciAGABAAcBABAAIAVCAAABsBFQAAAHIgBgAQAA4AAAAAAAEAAAABAAAAAAAA" +
+    "AAAAAACMAQAAAAAAAJQBAAABAAAAAgAAAAEAAAADAAAAAQAAAAQAASgAATwABjxpbml0PgACPjsA" +
+    "BD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8gLSBw" +
+    "cml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1MZGFsdmlrL2Fubm90YXRpb24vU2ln" +
+    "bmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEv" +
+    "bGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAB1MamF2YS91dGlsL2Z1" +
+    "bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAGYWNjZXB0ABJl" +
+    "bWl0dGVyOiBqYWNrLTQuMTkALXBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFu" +
+    "c2Zvcm1lZAAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHBy" +
+    "ZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJp" +
+    "dmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFs" +
+    "dWUAFQEABw48LQAeAAcOhwAaAAcOhwAiAQAHDoc8hzyHPIcAAgEBHBwEFw0XARcMFwMCAQEcHAUX" +
+    "ABcNFwEXDBcEAAEDAQACAIGABJwDAQK4AwEC3AMDAYAEABAAAAAAAAAAAQAAAAAAAAABAAAAHQAA" +
+    "AHAAAAACAAAABgAAAOQAAAADAAAABAAAAPwAAAAEAAAAAQAAACwBAAAFAAAABwAAADQBAAAGAAAA" +
+    "AQAAAGwBAAADEAAAAgAAAIwBAAABIAAABAAAAJwBAAAGIAAAAQAAAGQCAAABEAAAAwAAAIQCAAAC" +
+    "IAAAHQAAAJoCAAADIAAABAAAALAEAAAEIAAAAgAAANEEAAAAIAAAAQAAAO8EAAAAEAAAAQAAAAgF" +
+    "AAA=");
+
+  // A class that we can use to keep track of the output of this test.
+  private static class TestWatcher implements Consumer<String> {
+    private StringBuilder sb;
+    public TestWatcher() {
+      sb = new StringBuilder();
+    }
+
+    @Override
+    public void accept(String s) {
+      sb.append(s);
+      sb.append('\n');
+    }
+
+    public String getOutput() {
+      return sb.toString();
+    }
+  }
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    TestWatcher w = new TestWatcher();
+    doTest(new Transform(w), w);
+  }
+
+  // TODO Workaround to (1) inability to ensure that current_method is not put into a register by
+  // the JIT and/or (2) inability to deoptimize frames near runtime functions.
+  // TODO Fix one/both of these issues.
+  public static void doCall(Runnable r) {
+      r.run();
+  }
+
+  private static boolean interpreting = true;
+  private static boolean retry = false;
+
+  public static void doTest(Transform t, TestWatcher w) {
+    Runnable do_redefinition = () -> {
+      w.accept("transforming calling function");
+      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    };
+    // This just prints something out to show we are running the Runnable.
+    Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+
+    // Try and redefine.
+    t.sayHi(say_nothing);
+    t.sayHi(do_redefinition);
+    t.sayHi(say_nothing);
+
+    // Print output of last run.
+    System.out.print(w.getOutput());
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] classfile,
+                                                       byte[] dexfile);
+}
diff --git a/test/919-obsolete-fields/src/Transform.java b/test/919-obsolete-fields/src/Transform.java
new file mode 100644
index 0000000..abd1d19
--- /dev/null
+++ b/test/919-obsolete-fields/src/Transform.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.function.Consumer;
+
+class Transform {
+  private Consumer<String> reporter;
+  public Transform(Consumer<String> reporter) {
+    this.reporter = reporter;
+  }
+
+  private void Start() {
+    reporter.accept("hello - private");
+  }
+
+  private void Finish() {
+    reporter.accept("goodbye - private");
+  }
+
+  public void sayHi(Runnable r) {
+    reporter.accept("Pre Start private method call");
+    Start();
+    reporter.accept("Post Start private method call");
+    // TODO Revist with b/33616143
+    // TODO Uncomment this
+    // r.run();
+    // TODO This is a very temporary fix until we get either deoptimization near runtime frames
+    // working, forcing current method to be always read from the stack or both working.
+    Main.doCall(r);
+    reporter.accept("Pre Finish private method call");
+    Finish();
+    reporter.accept("Post Finish private method call");
+  }
+}
diff --git a/test/920-objects/build b/test/920-objects/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/920-objects/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/920-objects/expected.txt b/test/920-objects/expected.txt
new file mode 100644
index 0000000..80feeb9
--- /dev/null
+++ b/test/920-objects/expected.txt
@@ -0,0 +1,10 @@
+class java.lang.Object 8
+class java.lang.Object 8
+class [I 12
+class [I 16
+class [I 20
+class [D 16
+class [D 24
+class [D 32
+class java.lang.String 24
+class java.lang.String 24
diff --git a/test/920-objects/info.txt b/test/920-objects/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/920-objects/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/920-objects/objects.cc b/test/920-objects/objects.cc
new file mode 100644
index 0000000..886dd0e
--- /dev/null
+++ b/test/920-objects/objects.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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 "objects.h"
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test920Objects {
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getObjectSize(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jobject object) {
+  jlong size;
+
+  jvmtiError result = jvmti_env->GetObjectSize(object, &size);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetObjectSize: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return -1;
+  }
+
+  return size;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getObjectHashCode(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jobject object) {
+  jint hash;
+
+  jvmtiError result = jvmti_env->GetObjectHashCode(object, &hash);
+  if (result != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result, &err);
+    printf("Failure running GetObjectHashCode: %s\n", err);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+    return -1;
+  }
+
+  return hash;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  SetAllCapabilities(jvmti_env);
+  return 0;
+}
+
+}  // namespace Test920Objects
+}  // namespace art
diff --git a/test/920-objects/objects.h b/test/920-objects/objects.h
new file mode 100644
index 0000000..5f21e7b
--- /dev/null
+++ b/test/920-objects/objects.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_920_OBJECTS_OBJECTS_H_
+#define ART_TEST_920_OBJECTS_OBJECTS_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test920Objects {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test920Objects
+}  // namespace art
+
+#endif  // ART_TEST_920_OBJECTS_OBJECTS_H_
diff --git a/test/920-objects/run b/test/920-objects/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/920-objects/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/920-objects/src/Main.java b/test/920-objects/src/Main.java
new file mode 100644
index 0000000..5dbe1a7
--- /dev/null
+++ b/test/920-objects/src/Main.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  public static void doTest() throws Exception {
+    testObjectSize(new Object());
+    testObjectSize(new Object());
+
+    testObjectSize(new int[0]);
+    testObjectSize(new int[1]);
+    testObjectSize(new int[2]);
+
+    testObjectSize(new double[0]);
+    testObjectSize(new double[1]);
+    testObjectSize(new double[2]);
+
+    testObjectSize(new String("abc"));
+    testObjectSize(new String("wxyz"));
+
+    testObjectHash();
+  }
+
+  private static void testObjectSize(Object o) {
+    System.out.println(o.getClass() + " " + getObjectSize(o));
+  }
+
+  private static void testObjectHash() {
+    Object[] objects = new Object[] {
+        new Object(),
+        new Object(),
+
+        new MyHash(1),
+        new MyHash(1),
+        new MyHash(2)
+    };
+
+    int hashes[] = new int[objects.length];
+
+    for (int i = 0; i < objects.length; i++) {
+      hashes[i] = getObjectHashCode(objects[i]);
+    }
+
+    // Implementation detail: we use the identity hashcode, for simplicity.
+    for (int i = 0; i < objects.length; i++) {
+      int ihash = System.identityHashCode(objects[i]);
+      if (hashes[i] != ihash) {
+        throw new RuntimeException(objects[i] + ": " + hashes[i] + " vs " + ihash);
+      }
+    }
+
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
+    for (int i = 0; i < objects.length; i++) {
+      int newhash = getObjectHashCode(objects[i]);
+      if (hashes[i] != newhash) {
+        throw new RuntimeException(objects[i] + ": " + hashes[i] + " vs " + newhash);
+      }
+    }
+  }
+
+  private static native long getObjectSize(Object o);
+  private static native int getObjectHashCode(Object o);
+
+  private static class MyHash {
+    private int hash;
+
+    public MyHash(int h) {
+      hash = h;
+    }
+
+    public int hashCode() {
+      return hash;
+    }
+  }
+}
diff --git a/test/921-hello-failure/build b/test/921-hello-failure/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/921-hello-failure/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/921-hello-failure/expected.txt b/test/921-hello-failure/expected.txt
new file mode 100644
index 0000000..e2665ef
--- /dev/null
+++ b/test/921-hello-failure/expected.txt
@@ -0,0 +1,15 @@
+hello - NewName
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_NAMES_DONT_MATCH)
+hello - NewName
+hello - DifferentAccess
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED)
+hello - DifferentAccess
+hello2 - NewInterface
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform2;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED)
+hello2 - NewInterface
+hello2 - MissingInterface
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform2;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED)
+hello2 - MissingInterface
+hello2 - ReorderInterface
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform2;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED)
+hello2 - ReorderInterface
diff --git a/test/921-hello-failure/info.txt b/test/921-hello-failure/info.txt
new file mode 100644
index 0000000..9d241c7
--- /dev/null
+++ b/test/921-hello-failure/info.txt
@@ -0,0 +1,7 @@
+Tests for redefinition failure modes.
+
+Tests
+----
+
+- NewName: The name of the class is changed
+- DifferentAccess: Class access is changed from <default> to 'public'
diff --git a/test/921-hello-failure/run b/test/921-hello-failure/run
new file mode 100755
index 0000000..3ef4832
--- /dev/null
+++ b/test/921-hello-failure/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/921-hello-failure/src/DifferentAccess.java b/test/921-hello-failure/src/DifferentAccess.java
new file mode 100644
index 0000000..d4b16e0
--- /dev/null
+++ b/test/921-hello-failure/src/DifferentAccess.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class DifferentAccess {
+  // The following is a base64 encoding of the following class.
+  // public class NotTransform {
+  //   public void sayHi(String name) {
+  //     throw new Error("Should not be called!");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAFQoABgAPBwAQCAARCgACABIHABMHABQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" +
+    "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VG" +
+    "aWxlAQAOVHJhbnNmb3JtLmphdmEMAAcACAEAD2phdmEvbGFuZy9FcnJvcgEAFVNob3VsZCBub3Qg" +
+    "YmUgY2FsbGVkIQwABwAMAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAAhAAUABgAAAAAA" +
+    "AgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEAAQALAAwAAQAJAAAA" +
+    "IgADAAIAAAAKuwACWRIDtwAEvwAAAAEACgAAAAYAAQAAAAMAAQANAAAAAgAO");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQANVRT7zleRLG4E5DhtK7OtoDxZlUQMI5eQAgAAcAAAAHhWNBIAAAAAAAAAAPwBAAAL" +
+    "AAAAcAAAAAUAAACcAAAAAgAAALAAAAAAAAAAAAAAAAQAAADIAAAAAQAAAOgAAACIAQAACAEAAEoB" +
+    "AABSAQAAXwEAAHIBAACGAQAAmgEAALEBAADBAQAAxAEAAMgBAADcAQAAAQAAAAIAAAADAAAABAAA" +
+    "AAcAAAAHAAAABAAAAAAAAAAIAAAABAAAAEQBAAAAAAAAAAAAAAAAAQAKAAAAAQABAAAAAAACAAAA" +
+    "AAAAAAAAAAABAAAAAgAAAAAAAAAGAAAAAAAAAO4BAAAAAAAAAQABAAEAAADjAQAABAAAAHAQAwAA" +
+    "AA4ABAACAAIAAADoAQAACQAAACIAAQAbAQUAAABwIAIAEAAnAAAAAQAAAAMABjxpbml0PgALTFRy" +
+    "YW5zZm9ybTsAEUxqYXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xh" +
+    "bmcvU3RyaW5nOwAVU2hvdWxkIG5vdCBiZSBjYWxsZWQhAA5UcmFuc2Zvcm0uamF2YQABVgACVkwA" +
+    "EmVtaXR0ZXI6IGphY2stNC4yMAAFc2F5SGkAAQAHDgADAQAHDgAAAAEBAIGABIgCAQGgAgwAAAAA" +
+    "AAAAAQAAAAAAAAABAAAACwAAAHAAAAACAAAABQAAAJwAAAADAAAAAgAAALAAAAAFAAAABAAAAMgA" +
+    "AAAGAAAAAQAAAOgAAAABIAAAAgAAAAgBAAABEAAAAQAAAEQBAAACIAAACwAAAEoBAAADIAAAAgAA" +
+    "AOMBAAAAIAAAAQAAAO4BAAAAEAAAAQAAAPwBAAA=");
+
+  public static void doTest(Transform t) {
+    t.sayHi("DifferentAccess");
+    try {
+      Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    } catch (Exception e) {
+      System.out.println(
+          "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+    }
+    t.sayHi("DifferentAccess");
+  }
+}
diff --git a/test/921-hello-failure/src/Iface1.java b/test/921-hello-failure/src/Iface1.java
new file mode 100644
index 0000000..f53275a
--- /dev/null
+++ b/test/921-hello-failure/src/Iface1.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Iface1 {
+  void sayHi(String s);
+}
diff --git a/test/921-hello-failure/src/Iface2.java b/test/921-hello-failure/src/Iface2.java
new file mode 100644
index 0000000..54cdd90
--- /dev/null
+++ b/test/921-hello-failure/src/Iface2.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Iface2 {
+  void sayHi(String s);
+}
diff --git a/test/921-hello-failure/src/Iface3.java b/test/921-hello-failure/src/Iface3.java
new file mode 100644
index 0000000..819134d
--- /dev/null
+++ b/test/921-hello-failure/src/Iface3.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Iface3 {
+  void sayHi(String s);
+}
diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java
new file mode 100644
index 0000000..69c48e2
--- /dev/null
+++ b/test/921-hello-failure/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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 main(String[] args) {
+    System.loadLibrary(args[1]);
+    NewName.doTest(new Transform());
+    DifferentAccess.doTest(new Transform());
+    NewInterface.doTest(new Transform2());
+    MissingInterface.doTest(new Transform2());
+    ReorderInterface.doTest(new Transform2());
+  }
+
+  // Transforms the class. This throws an exception if something goes wrong.
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile) throws Exception;
+}
diff --git a/test/921-hello-failure/src/MissingInterface.java b/test/921-hello-failure/src/MissingInterface.java
new file mode 100644
index 0000000..d17a6de
--- /dev/null
+++ b/test/921-hello-failure/src/MissingInterface.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class MissingInterface {
+  // The following is a base64 encoding of the following class.
+  // class Transform2 implements Iface1 {
+  //   public void sayHi(String name) {
+  //     throw new Error("Should not be called!");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+      "yv66vgAAADQAFwoABgAQBwARCAASCgACABMHABQHABUHABYBAAY8aW5pdD4BAAMoKVYBAARDb2Rl" +
+      "AQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3Vy" +
+      "Y2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAIAAkBAA9qYXZhL2xhbmcvRXJyb3IBABVTaG91bGQg" +
+      "bm90IGJlIGNhbGxlZCEMAAgADQEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAGSWZh" +
+      "Y2UxACAABQAGAAEABwAAAAIAAAAIAAkAAQAKAAAAHQABAAEAAAAFKrcAAbEAAAABAAsAAAAGAAEA" +
+      "AAABAAEADAANAAEACgAAACIAAwACAAAACrsAAlkSA7cABL8AAAABAAsAAAAGAAEAAAADAAEADgAA" +
+      "AAIADw==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+      "ZGV4CjAzNQDiWVay8/Z0/tXQaTTI+QtwTM65gRJVMOusAgAAcAAAAHhWNBIAAAAAAAAAABgCAAAM" +
+      "AAAAcAAAAAYAAACgAAAAAgAAALgAAAAAAAAAAAAAAAQAAADQAAAAAQAAAPAAAACcAQAAEAEAAFoB" +
+      "AABiAQAAbAEAAHoBAACNAQAAoQEAALUBAADMAQAA3QEAAOABAADkAQAA+AEAAAEAAAACAAAAAwAA" +
+      "AAQAAAAFAAAACAAAAAgAAAAFAAAAAAAAAAkAAAAFAAAAVAEAAAEAAAAAAAAAAQABAAsAAAACAAEA" +
+      "AAAAAAMAAAAAAAAAAQAAAAAAAAADAAAATAEAAAcAAAAAAAAACgIAAAAAAAABAAEAAQAAAP8BAAAE" +
+      "AAAAcBADAAAADgAEAAIAAgAAAAQCAAAJAAAAIgACABsBBgAAAHAgAgAQACcAAAABAAAAAAAAAAEA" +
+      "AAAEAAY8aW5pdD4ACExJZmFjZTE7AAxMVHJhbnNmb3JtMjsAEUxqYXZhL2xhbmcvRXJyb3I7ABJM" +
+      "amF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAVU2hvdWxkIG5vdCBiZSBjYWxs" +
+      "ZWQhAA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjAABXNheUhpAAEA" +
+      "Bw4AAwEABw4AAAABAQCAgASQAgEBqAIMAAAAAAAAAAEAAAAAAAAAAQAAAAwAAABwAAAAAgAAAAYA" +
+      "AACgAAAAAwAAAAIAAAC4AAAABQAAAAQAAADQAAAABgAAAAEAAADwAAAAASAAAAIAAAAQAQAAARAA" +
+      "AAIAAABMAQAAAiAAAAwAAABaAQAAAyAAAAIAAAD/AQAAACAAAAEAAAAKAgAAABAAAAEAAAAYAgAA");
+
+  public static void doTest(Transform2 t) {
+    t.sayHi("MissingInterface");
+    try {
+      Main.doCommonClassRedefinition(Transform2.class, CLASS_BYTES, DEX_BYTES);
+    } catch (Exception e) {
+      System.out.println(
+          "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+    }
+    t.sayHi("MissingInterface");
+  }
+}
diff --git a/test/921-hello-failure/src/NewInterface.java b/test/921-hello-failure/src/NewInterface.java
new file mode 100644
index 0000000..fe77222
--- /dev/null
+++ b/test/921-hello-failure/src/NewInterface.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class NewInterface {
+  // The following is a base64 encoding of the following class.
+  // class Transform2 implements Iface1, Iface2, Iface3 {
+  //   public void sayHi(String name) {
+  //     throw new Error("Should not be called!");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAGwoABgASBwATCAAUCgACABUHABYHABcHABgHABkHABoBAAY8aW5pdD4BAAMoKVYB" +
+    "AARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYB" +
+    "AApTb3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAKAAsBAA9qYXZhL2xhbmcvRXJyb3IBABVT" +
+    "aG91bGQgbm90IGJlIGNhbGxlZCEMAAoADwEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0" +
+    "AQAGSWZhY2UxAQAGSWZhY2UyAQAGSWZhY2UzACAABQAGAAMABwAIAAkAAAACAAAACgALAAEADAAA" +
+    "AB0AAQABAAAABSq3AAGxAAAAAQANAAAABgABAAAAAQABAA4ADwABAAwAAAAiAAMAAgAAAAq7AAJZ" +
+    "EgO3AAS/AAAAAQANAAAABgABAAAAAwABABAAAAACABE=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCBWnko4SMXeuXSO3fGJBp0WSlc0HLRr63UAgAAcAAAAHhWNBIAAAAAAAAAAEACAAAO" +
+    "AAAAcAAAAAgAAACoAAAAAgAAAMgAAAAAAAAAAAAAAAQAAADgAAAAAQAAAAABAAC0AQAAIAEAAG4B" +
+    "AAB2AQAAgAEAAIoBAACUAQAAogEAALUBAADJAQAA3QEAAPQBAAAFAgAACAIAAAwCAAAgAgAAAQAA" +
+    "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAoAAAAKAAAABwAAAAAAAAALAAAABwAAAGgBAAADAAAA" +
+    "AAAAAAMAAQANAAAABAABAAAAAAAFAAAAAAAAAAMAAAAAAAAABQAAAFwBAAAJAAAAAAAAADICAAAA" +
+    "AAAAAQABAAEAAAAnAgAABAAAAHAQAwAAAA4ABAACAAIAAAAsAgAACQAAACIABAAbAQgAAABwIAIA" +
+    "EAAnAAAAAwAAAAAAAQACAAAAAQAAAAYABjxpbml0PgAITElmYWNlMTsACExJZmFjZTI7AAhMSWZh" +
+    "Y2UzOwAMTFRyYW5zZm9ybTI7ABFMamF2YS9sYW5nL0Vycm9yOwASTGphdmEvbGFuZy9PYmplY3Q7" +
+    "ABJMamF2YS9sYW5nL1N0cmluZzsAFVNob3VsZCBub3QgYmUgY2FsbGVkIQAPVHJhbnNmb3JtMi5q" +
+    "YXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00LjIwAAVzYXlIaQABAAcOAAMBAAcOAAAAAQEAgIAE" +
+    "oAIBAbgCDAAAAAAAAAABAAAAAAAAAAEAAAAOAAAAcAAAAAIAAAAIAAAAqAAAAAMAAAACAAAAyAAA" +
+    "AAUAAAAEAAAA4AAAAAYAAAABAAAAAAEAAAEgAAACAAAAIAEAAAEQAAACAAAAXAEAAAIgAAAOAAAA" +
+    "bgEAAAMgAAACAAAAJwIAAAAgAAABAAAAMgIAAAAQAAABAAAAQAIAAA==");
+
+  public static void doTest(Transform2 t) {
+    t.sayHi("NewInterface");
+    try {
+      Main.doCommonClassRedefinition(Transform2.class, CLASS_BYTES, DEX_BYTES);
+    } catch (Exception e) {
+      System.out.println(
+          "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+    }
+    t.sayHi("NewInterface");
+  }
+}
diff --git a/test/921-hello-failure/src/NewName.java b/test/921-hello-failure/src/NewName.java
new file mode 100644
index 0000000..a6f249a
--- /dev/null
+++ b/test/921-hello-failure/src/NewName.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class NewName {
+  // class NotTransform {
+  //   public void sayHi(String name) {
+  //     throw new Error("Should not be called!");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAFQoABgAPBwAQCAARCgACABIHABMHABQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" +
+    "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VG" +
+    "aWxlAQARTm90VHJhbnNmb3JtLmphdmEMAAcACAEAD2phdmEvbGFuZy9FcnJvcgEAFVNob3VsZCBu" +
+    "b3QgYmUgY2FsbGVkIQwABwAMAQAMTm90VHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAAgAAUA" +
+    "BgAAAAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEAAQALAAwA" +
+    "AQAJAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEACgAAAAYAAQAAAAMAAQANAAAAAgAO");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQDLV95i5xnv6iUi6uIeDoY5jP5Xe9NP1AiYAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAL" +
+    "AAAAcAAAAAUAAACcAAAAAgAAALAAAAAAAAAAAAAAAAQAAADIAAAAAQAAAOgAAACQAQAACAEAAEoB" +
+    "AABSAQAAYgEAAHUBAACJAQAAnQEAALABAADHAQAAygEAAM4BAADiAQAAAQAAAAIAAAADAAAABAAA" +
+    "AAcAAAAHAAAABAAAAAAAAAAIAAAABAAAAEQBAAAAAAAAAAAAAAAAAQAKAAAAAQABAAAAAAACAAAA" +
+    "AAAAAAAAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAPQBAAAAAAAAAQABAAEAAADpAQAABAAAAHAQAwAA" +
+    "AA4ABAACAAIAAADuAQAACQAAACIAAQAbAQYAAABwIAIAEAAnAAAAAQAAAAMABjxpbml0PgAOTE5v" +
+    "dFRyYW5zZm9ybTsAEUxqYXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" +
+    "L2xhbmcvU3RyaW5nOwARTm90VHJhbnNmb3JtLmphdmEAFVNob3VsZCBub3QgYmUgY2FsbGVkIQAB" +
+    "VgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMAAFc2F5SGkAAQAHDgADAQAHDgAAAAEBAICABIgCAQGg" +
+    "AgAADAAAAAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAFAAAAnAAAAAMAAAACAAAAsAAAAAUA" +
+    "AAAEAAAAyAAAAAYAAAABAAAA6AAAAAEgAAACAAAACAEAAAEQAAABAAAARAEAAAIgAAALAAAASgEA" +
+    "AAMgAAACAAAA6QEAAAAgAAABAAAA9AEAAAAQAAABAAAABAIAAA==");
+
+  public static void doTest(Transform t) {
+    t.sayHi("NewName");
+    try {
+      Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    } catch (Exception e) {
+      System.out.println(
+          "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+    }
+    t.sayHi("NewName");
+  }
+}
diff --git a/test/921-hello-failure/src/ReorderInterface.java b/test/921-hello-failure/src/ReorderInterface.java
new file mode 100644
index 0000000..ce78dbc
--- /dev/null
+++ b/test/921-hello-failure/src/ReorderInterface.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class ReorderInterface {
+  // The following is a base64 encoding of the following class.
+  // class Transform2 implements Iface2, Iface1 {
+  //   public void sayHi(String name) {
+  //     throw new Error("Should not be called!");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAGQoABgARBwASCAATCgACABQHABUHABYHABcHABgBAAY8aW5pdD4BAAMoKVYBAARD" +
+    "b2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApT" +
+    "b3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAJAAoBAA9qYXZhL2xhbmcvRXJyb3IBABVTaG91" +
+    "bGQgbm90IGJlIGNhbGxlZCEMAAkADgEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAG" +
+    "SWZhY2UyAQAGSWZhY2UxACAABQAGAAIABwAIAAAAAgAAAAkACgABAAsAAAAdAAEAAQAAAAUqtwAB" +
+    "sQAAAAEADAAAAAYAAQAAAAEAAQANAA4AAQALAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEADAAA" +
+    "AAYAAQAAAAMAAQAPAAAAAgAQ");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQChWfUC02YEHJZLC4V4pHrGMdqwD8NnzXvAAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAN" +
+    "AAAAcAAAAAcAAACkAAAAAgAAAMAAAAAAAAAAAAAAAAQAAADYAAAAAQAAAPgAAACoAQAAGAEAAGIB" +
+    "AABqAQAAdAEAAH4BAACMAQAAnwEAALMBAADHAQAA3gEAAO8BAADyAQAA9gEAAAoCAAABAAAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAABcAQAAAgAAAAAAAAACAAEA" +
+    "DAAAAAMAAQAAAAAABAAAAAAAAAACAAAAAAAAAAQAAABUAQAACAAAAAAAAAAcAgAAAAAAAAEAAQAB" +
+    "AAAAEQIAAAQAAABwEAMAAAAOAAQAAgACAAAAFgIAAAkAAAAiAAMAGwEHAAAAcCACABAAJwAAAAIA" +
+    "AAABAAAAAQAAAAUABjxpbml0PgAITElmYWNlMTsACExJZmFjZTI7AAxMVHJhbnNmb3JtMjsAEUxq" +
+    "YXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAV" +
+    "U2hvdWxkIG5vdCBiZSBjYWxsZWQhAA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBq" +
+    "YWNrLTQuMjAABXNheUhpAAEABw4AAwEABw4AAAABAQCAgASYAgEBsAIAAAwAAAAAAAAAAQAAAAAA" +
+    "AAABAAAADQAAAHAAAAACAAAABwAAAKQAAAADAAAAAgAAAMAAAAAFAAAABAAAANgAAAAGAAAAAQAA" +
+    "APgAAAABIAAAAgAAABgBAAABEAAAAgAAAFQBAAACIAAADQAAAGIBAAADIAAAAgAAABECAAAAIAAA" +
+    "AQAAABwCAAAAEAAAAQAAACwCAAA=");
+
+  public static void doTest(Transform2 t) {
+    t.sayHi("ReorderInterface");
+    try {
+      Main.doCommonClassRedefinition(Transform2.class, CLASS_BYTES, DEX_BYTES);
+    } catch (Exception e) {
+      System.out.println(
+          "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+    }
+    t.sayHi("ReorderInterface");
+  }
+}
diff --git a/test/921-hello-failure/src/Transform.java b/test/921-hello-failure/src/Transform.java
new file mode 100644
index 0000000..ee444f0
--- /dev/null
+++ b/test/921-hello-failure/src/Transform.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi(String name) {
+    System.out.println("hello - " + name);
+  }
+}
diff --git a/test/921-hello-failure/src/Transform2.java b/test/921-hello-failure/src/Transform2.java
new file mode 100644
index 0000000..9d949f3
--- /dev/null
+++ b/test/921-hello-failure/src/Transform2.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform2 implements Iface1, Iface2 {
+  public void sayHi(String name) {
+    System.out.println("hello2 - " + name);
+  }
+}
diff --git a/test/922-properties/build b/test/922-properties/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/922-properties/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/922-properties/expected.txt b/test/922-properties/expected.txt
new file mode 100644
index 0000000..0be939b
--- /dev/null
+++ b/test/922-properties/expected.txt
@@ -0,0 +1,59 @@
+Recommended properties:
+ "java.class.path": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.library.path": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.info": OK !!!JVMTI_ERROR_NOT_AVAILABLE
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.name": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.vendor": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.version": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+Missing recommended properties: [java.vm.info]
+Other properties:
+ "file.encoding": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "file.separator": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.class.version": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.compiler": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.ext.dirs": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.net.preferIPv6Addresses": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.specification.name": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.specification.vendor": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.specification.version": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vendor": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vendor.url": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.version": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.specification.name": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.specification.vendor": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.specification.version": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.vendor.url": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "line.separator": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "os.name": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "path.separator": OK
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+Non-specified property:
+ "java.boot.class.path": ERROR !!!JVMTI_ERROR_NOT_AVAILABLE
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+Non-specified property (2):
+ "a": OK !!!JVMTI_ERROR_NOT_AVAILABLE
+  Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
diff --git a/test/922-properties/info.txt b/test/922-properties/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/922-properties/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/922-properties/properties.cc b/test/922-properties/properties.cc
new file mode 100644
index 0000000..b1e7fce
--- /dev/null
+++ b/test/922-properties/properties.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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 "properties.h"
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedUtfChars.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test922Properties {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getSystemProperties(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+  jint count;
+  char** properties;
+  jvmtiError result = jvmti_env->GetSystemProperties(&count, &properties);
+  if (JvmtiErrorToException(env, result)) {
+    return nullptr;
+  }
+
+  auto callback = [&](jint i) -> jstring {
+    char* data = properties[i];
+    if (data == nullptr) {
+      return nullptr;
+    }
+    jstring ret = env->NewStringUTF(data);
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(data));
+    return ret;
+  };
+  jobjectArray ret = CreateObjectArray(env, count, "java/lang/String", callback);
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(properties));
+
+  return ret;
+}
+
+extern "C" JNIEXPORT jstring JNICALL Java_Main_getSystemProperty(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring key) {
+  ScopedUtfChars string(env, key);
+  if (string.c_str() == nullptr) {
+    return nullptr;
+  }
+
+  char* value = nullptr;
+  jvmtiError result = jvmti_env->GetSystemProperty(string.c_str(), &value);
+  if (JvmtiErrorToException(env, result)) {
+    return nullptr;
+  }
+
+  jstring ret = (value == nullptr) ? nullptr : env->NewStringUTF(value);
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(value));
+
+  return ret;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setSystemProperty(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring key, jstring value) {
+  ScopedUtfChars key_string(env, key);
+  if (key_string.c_str() == nullptr) {
+    return;
+  }
+  ScopedUtfChars value_string(env, value);
+  if (value_string.c_str() == nullptr) {
+    return;
+  }
+
+  jvmtiError result = jvmti_env->SetSystemProperty(key_string.c_str(), value_string.c_str());
+  if (JvmtiErrorToException(env, result)) {
+    return;
+  }
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  SetAllCapabilities(jvmti_env);
+  return 0;
+}
+
+}  // namespace Test922Properties
+}  // namespace art
diff --git a/test/922-properties/properties.h b/test/922-properties/properties.h
new file mode 100644
index 0000000..84feb10
--- /dev/null
+++ b/test/922-properties/properties.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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_TEST_922_PROPERTIES_PROPERTIES_H_
+#define ART_TEST_922_PROPERTIES_PROPERTIES_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test922Properties {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test922Properties
+}  // namespace art
+
+#endif  // ART_TEST_922_PROPERTIES_PROPERTIES_H_
diff --git a/test/922-properties/run b/test/922-properties/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/922-properties/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/922-properties/src/Main.java b/test/922-properties/src/Main.java
new file mode 100644
index 0000000..6cec6e9
--- /dev/null
+++ b/test/922-properties/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Set;
+import java.util.TreeSet;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  public static void doTest() throws Exception {
+    Set<String> recommendedProperties = getRecommendedProperties();
+
+    System.out.println("Recommended properties:");
+    for (String key : recommendedProperties) {
+      checkProperty(key);
+    }
+
+    Set<String> allProperties = getAllProperties();
+
+    Set<String> retained = new TreeSet<String>(recommendedProperties);
+    retained.retainAll(allProperties);
+    if (!retained.equals(recommendedProperties)) {
+      Set<String> missing = new TreeSet<String>(recommendedProperties);
+      missing.removeAll(retained);
+      System.out.println("Missing recommended properties: " + missing);
+    }
+
+    Set<String> nonRecommended = new TreeSet<String>(allProperties);
+    nonRecommended.removeAll(recommendedProperties);
+
+    System.out.println("Other properties:");
+    for (String key : nonRecommended) {
+      checkProperty(key);
+    }
+
+    System.out.println("Non-specified property:");
+    String key = generate(allProperties);
+    checkProperty(key);
+
+    System.out.println("Non-specified property (2):");
+    String key2 = generateUnique(allProperties);
+    checkProperty(key2);
+  }
+
+  private static Set<String> getRecommendedProperties() {
+    Set<String> keys = new TreeSet<String>();
+    keys.add("java.vm.vendor");
+    keys.add("java.vm.version");
+    keys.add("java.vm.name");
+    keys.add("java.vm.info");
+    keys.add("java.library.path");
+    keys.add("java.class.path");
+    return keys;
+  }
+
+  private static Set<String> getAllProperties() {
+    Set<String> keys = new TreeSet<String>();
+    String[] props = getSystemProperties();
+    for (String p : props) {
+      keys.add(p);
+    }
+    return keys;
+  }
+
+  private static boolean equals(String s1, String s2) {
+    if (s1 == null && s2 == null) {
+      return true;
+    } else if (s1 != null) {
+      return s1.equals(s2);
+    } else {
+      return false;
+    }
+  }
+
+  private static void checkProperty(String key) {
+    System.out.print(" \"" + key + "\": ");
+    String err = null;
+    String value = null;
+    try {
+      value = getSystemProperty(key);
+    } catch (RuntimeException e) {
+      err = e.getMessage();
+    }
+    String sysValue = System.getProperty(key);
+    if (equals(value, sysValue)) {
+      System.out.print("OK");
+      if (err != null) {
+        System.out.println(" !!!" + err);
+      } else {
+        System.out.println();
+      }
+    } else {
+      System.out.println("ERROR !!!" + err);
+    }
+
+    System.out.print("  Setting value to \"abc\": ");
+    try {
+      setSystemProperty(key, "abc");
+      System.out.println("SUCCEEDED");
+    } catch (RuntimeException e) {
+      System.out.println("!!!" + e.getMessage());
+    }
+  }
+
+  private static String generateUnique(Set<String> others) {
+    // Construct something. To be deterministic, just use "a+".
+    StringBuilder sb = new StringBuilder("a");
+    for (;;) {
+      String key = sb.toString();
+      if (!others.contains(key)) {
+        return key;
+      }
+      sb.append('a');
+    }
+  }
+
+  private static String generate(Set<String> others) {
+    // First check for something in the overall System properties.
+    TreeSet<String> sysProps = new TreeSet<String>(System.getProperties().stringPropertyNames());
+    sysProps.removeAll(others);
+    if (!sysProps.isEmpty()) {
+      // Find something that starts with "java" or "os," trying to be platform-independent.
+      for (String s: sysProps) {
+        if (s.startsWith("java.") || s.startsWith("os.")) {
+          return s;
+        }
+      }
+      // Just return the first thing.
+      return sysProps.iterator().next();
+    }
+
+    return generateUnique(others);
+  }
+
+  private static native String[] getSystemProperties();
+  private static native String getSystemProperty(String key);
+  private static native void setSystemProperty(String key, String value);
+}
diff --git a/test/Android.bp b/test/Android.bp
index 5a2c902..f6648d1 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -260,6 +260,9 @@
         "911-get-stack-trace/stack_trace.cc",
         "912-classes/classes.cc",
         "913-heaps/heaps.cc",
+        "918-fields/fields.cc",
+        "920-objects/objects.cc",
+        "922-properties/properties.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ec1f6ba..a3f6864 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -290,6 +290,11 @@
   915-obsolete-2 \
   916-obsolete-jit \
   917-fields-transformation \
+  918-fields \
+  919-obsolete-fields \
+  920-objects \
+  921-hello-failure \
+  922-properties \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -543,7 +548,7 @@
 # between those runs to be able to have precise checks.
 # Test 902 hits races with the JIT compiler. b/32821077
 # Test 629 requires compilation.
-# Test 914, 915, 917, & 918 are very sensitive to the exact state of the stack,
+# Test 914, 915, 917, & 919 are very sensitive to the exact state of the stack,
 # including the jit-inserted runtime frames. This causes them to be somewhat
 # flaky as JIT tests. This should be fixed once b/33630159 or b/33616143 are
 # resolved but until then just disable them. Test 916 already checks this
@@ -557,7 +562,7 @@
   914-hello-obsolescence \
   915-obsolete-2 \
   917-fields-transformation \
-  918-obsolete-fields \
+  919-obsolete-fields \
 
 ifneq (,$(filter jit,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -1021,10 +1026,11 @@
     test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_IMAGE_RULES
     run_test_options += --no-image
     # Add the core dependency. This is required for pre-building.
+    # Use the PIC image, as it is the default in run-test, to match dependencies.
     ifeq ($(1),host)
-      prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_$(13))
+      prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_$(13))
     else
-      prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_$(13))
+      prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13))
     endif
   else
     ifeq ($(9),npicimage)
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index ebf1e46..6f98f10 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -17,6 +17,7 @@
 #include "ti-agent/common_helper.h"
 
 #include <stdio.h>
+#include <sstream>
 
 #include "art_method.h"
 #include "jni.h"
@@ -39,10 +40,46 @@
   env->AddCapabilities(&caps);
 }
 
+bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) {
+  if (error == JVMTI_ERROR_NONE) {
+    return false;
+  }
+
+  ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+  if (rt_exception.get() == nullptr) {
+    // CNFE should be pending.
+    return true;
+  }
+
+  char* err;
+  jvmti_env->GetErrorName(error, &err);
+
+  env->ThrowNew(rt_exception.get(), err);
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  return true;
+}
+
 namespace common_redefine {
 
+static void throwRedefinitionError(jvmtiEnv* jvmti, JNIEnv* env, jclass target, jvmtiError res) {
+  std::stringstream err;
+  char* signature = nullptr;
+  char* generic = nullptr;
+  jvmti->GetClassSignature(target, &signature, &generic);
+  char* error = nullptr;
+  jvmti->GetErrorName(res, &error);
+  err << "Failed to redefine class <" << signature << "> due to " << error;
+  std::string message = err.str();
+  jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
+  jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
+  jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
+  env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
+}
+
 using RedefineDirectFunction = jvmtiError (*)(jvmtiEnv*, jclass, jint, const unsigned char*);
-static void DoClassTransformation(jvmtiEnv* jvmti_env, JNIEnv* env,
+static void DoClassTransformation(jvmtiEnv* jvmti_env,
+                                  JNIEnv* env,
                                   jclass target,
                                   jbyteArray class_file_bytes,
                                   jbyteArray dex_file_bytes) {
@@ -63,7 +100,7 @@
     res = f(jvmti_env, target, len, redef_bytes);
   }
   if (res != JVMTI_ERROR_NONE) {
-    printf("Redefinition failed!");
+    throwRedefinitionError(jvmti_env, env, target, res);
   }
 }
 
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index 76543fe..642ca03 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -65,6 +65,8 @@
 
 void SetAllCapabilities(jvmtiEnv* env);
 
+bool JvmtiErrorToException(JNIEnv* env, jvmtiError error);
+
 }  // namespace art
 
 #endif  // ART_TEST_TI_AGENT_COMMON_HELPER_H_
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 79c17d7..e309a89 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -37,6 +37,9 @@
 #include "911-get-stack-trace/stack_trace.h"
 #include "912-classes/classes.h"
 #include "913-heaps/heaps.h"
+#include "918-fields/fields.h"
+#include "920-objects/objects.h"
+#include "922-properties/properties.h"
 
 namespace art {
 
@@ -70,6 +73,11 @@
   { "915-obsolete-2", common_redefine::OnLoad, nullptr },
   { "916-obsolete-jit", common_redefine::OnLoad, nullptr },
   { "917-fields-transformation", common_redefine::OnLoad, nullptr },
+  { "918-fields", Test918Fields::OnLoad, nullptr },
+  { "919-obsolete-fields", common_redefine::OnLoad, nullptr },
+  { "920-objects", Test920Objects::OnLoad, nullptr },
+  { "921-hello-failure", common_redefine::OnLoad, nullptr },
+  { "922-properties", Test922Properties::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {
diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt
index fbc99b1..452a174 100644
--- a/test/valgrind-target-suppressions.txt
+++ b/test/valgrind-target-suppressions.txt
@@ -36,8 +36,7 @@
    MemCpySelfAssign
    Memcheck:Overlap
    fun:memcpy
-   fun:je_tsd_set
-   fun:je_tsd_fetch
+   ...
    fun:je_malloc_tsd_boot0
 }
 
@@ -59,3 +58,12 @@
    ...
    fun:_ZN3art7Runtime17InitNativeMethodsEv
 }
+
+# art::MemMap::MapInternal() uses msync() to check for the existence of memory mappings.
+{
+  art::MemMap::MapInternal()
+  Memcheck:Param
+  msync(start)
+  fun:msync
+  fun:_ZN3art6MemMap11MapInternalEPvmiiilb
+}