am 5be30072: ART: Remove wrong DCHECK

* commit '5be30072c5a750617dc3f9380776d074f26d9f32':
  ART: Remove wrong DCHECK
diff --git a/Android.mk b/Android.mk
index 15e8308..1edd543 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,15 +24,16 @@
 
 include $(art_path)/build/Android.common_path.mk
 
-# following the example of build's dont_bother for clean targets
-ifneq (,$(filter clean-oat,$(MAKECMDGOALS)))
-art_dont_bother := true
+# Following the example of build's dont_bother for clean targets.
+art_dont_bother := false
+ifneq (,$(filter clean-oat%,$(MAKECMDGOALS)))
+  art_dont_bother := true
 endif
-ifneq (,$(filter clean-oat-host,$(MAKECMDGOALS)))
-art_dont_bother := true
-endif
-ifneq (,$(filter clean-oat-target,$(MAKECMDGOALS)))
-art_dont_bother := true
+
+# Don't bother with tests unless there is a test-art* or build-art* target.
+art_test_bother := false
+ifneq (,$(filter %tests test-art% build-art%,$(MAKECMDGOALS)))
+  art_test_bother := true
 endif
 
 .PHONY: clean-oat
@@ -77,7 +78,8 @@
 
 .PHONY: clean-oat-target
 clean-oat-target:
-	adb remount
+	adb root
+	adb wait-for-device remount
 	adb shell rm -rf $(ART_TARGET_NATIVETEST_DIR)
 	adb shell rm -rf $(ART_TARGET_TEST_DIR)
 	adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/*
@@ -123,10 +125,15 @@
 ifdef TARGET_2ND_ARCH
 ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
 endif
+ifdef HOST_2ND_ARCH
+ART_HOST_DEPENDENCIES += $(2ND_HOST_OUT_SHARED_LIBRARIES)/libjavacore.so
+endif
 
 ########################################################################
 # test rules
 
+ifeq ($(art_test_bother),true)
+
 # All the dependencies that must be built ahead of sync-ing them onto the target device.
 TEST_ART_TARGET_SYNC_DEPS :=
 
@@ -137,7 +144,8 @@
 # Sync test files to the target, depends upon all things that must be pushed to the target.
 .PHONY: test-art-target-sync
 test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
-	adb remount
+	adb root
+	adb wait-for-device remount
 	adb sync
 
 # Undefine variable now its served its purpose.
@@ -229,6 +237,11 @@
 	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
 endif
 
+# Valgrind. Currently only 32b gtests.
+.PHONY: valgrind-test-art-host
+valgrind-test-art-host: valgrind-test-art-host-gtest32
+	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+
 ########################################################################
 # target test rules
 
@@ -290,6 +303,8 @@
 	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
 endif
 
+endif  # art_test_bother
+
 ########################################################################
 # oat-target and oat-target-sync rules
 
@@ -340,7 +355,8 @@
 
 .PHONY: oat-target-sync
 oat-target-sync: oat-target
-	adb remount
+	adb root
+	adb wait-for-device remount
 	adb sync
 
 ########################################################################
@@ -349,51 +365,39 @@
 build-art: build-art-host build-art-target
 
 .PHONY: build-art-host
-build-art-host:   $(ART_HOST_EXECUTABLES)   $(ART_HOST_GTEST_EXECUTABLES)   $(HOST_CORE_IMG_OUT)   $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION)
+build-art-host:   $(HOST_OUT_EXECUTABLES)/art $(ART_HOST_DEPENDENCIES) $(HOST_CORE_IMG_OUT) $(2ND_HOST_CORE_IMG_OUT)
 
 .PHONY: build-art-target
-build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_GTEST_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
-
-########################################################################
-# "m art-host" for just building the files needed to run the art script
-.PHONY: art-host
-ifeq ($(HOST_PREFER_32_BIT),true)
-art-host:   $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/bin/dalvikvm
-else
-art-host:   $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm64 $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/lib64/libjavacore.so $(HOST_OUT)/bin/dalvikvm
-endif
-
-.PHONY: art-host-debug
-art-host-debug:   art-host $(HOST_OUT)/lib/libartd.so $(HOST_OUT)/bin/dex2oatd $(HOST_OUT)/bin/patchoatd
+build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT)
 
 ########################################################################
 # targets to switch back and forth from libdvm to libart
 
 .PHONY: use-art
 use-art:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
 	adb shell start
 
 .PHONY: use-artd
 use-artd:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
 	adb shell start
 
 .PHONY: use-dalvik
 use-dalvik:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell setprop persist.sys.dalvik.vm.lib.2 libdvm.so
 	adb shell start
 
 .PHONY: use-art-full
 use-art-full:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
 	adb shell setprop dalvik.vm.dex2oat-filter ""
 	adb shell setprop dalvik.vm.image-dex2oat-filter ""
@@ -402,8 +406,8 @@
 
 .PHONY: use-artd-full
 use-artd-full:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
 	adb shell setprop dalvik.vm.dex2oat-filter ""
 	adb shell setprop dalvik.vm.image-dex2oat-filter ""
@@ -412,8 +416,8 @@
 
 .PHONY: use-art-smart
 use-art-smart:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
 	adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
 	adb shell setprop dalvik.vm.image-dex2oat-filter ""
@@ -422,8 +426,8 @@
 
 .PHONY: use-art-interpret-only
 use-art-interpret-only:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
 	adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
 	adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
@@ -432,8 +436,8 @@
 
 .PHONY: use-artd-interpret-only
 use-artd-interpret-only:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
 	adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
 	adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
@@ -442,8 +446,8 @@
 
 .PHONY: use-art-verify-none
 use-art-verify-none:
-	adb root && sleep 3
-	adb shell stop
+	adb root
+	adb wait-for-device shell stop
 	adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
 	adb shell setprop dalvik.vm.dex2oat-filter "verify-none"
 	adb shell setprop dalvik.vm.image-dex2oat-filter "verify-none"
@@ -453,3 +457,7 @@
 ########################################################################
 
 endif # !art_dont_bother
+
+# Clear locally used variables.
+art_dont_bother :=
+art_test_bother :=
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 0dcefea..386128e 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -158,6 +158,7 @@
   -Wno-unused-parameter \
   -Wstrict-aliasing \
   -fstrict-aliasing \
+  -Wunreachable-code \
   -fvisibility=protected
 
 ART_TARGET_CLANG_CFLAGS :=
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 7e38157..ee72706 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -26,18 +26,6 @@
 # List of known broken tests that we won't attempt to execute. The test name must be the full
 # rule name such as test-art-host-oat-optimizing-HelloWorld64.
 ART_TEST_KNOWN_BROKEN := \
-  test-art-host-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
-  test-art-host-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
-  test-art-host-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
-  test-art-host-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
-  test-art-host-run-test-gcverify-optimizing-no-prebuild-004-SignalTest32 \
-  test-art-host-run-test-gcverify-optimizing-prebuild-004-SignalTest32 \
-  test-art-host-run-test-gcverify-optimizing-norelocate-004-SignalTest32 \
-  test-art-host-run-test-gcverify-optimizing-relocate-004-SignalTest32 \
-  test-art-host-run-test-optimizing-no-prebuild-004-SignalTest32 \
-  test-art-host-run-test-optimizing-prebuild-004-SignalTest32 \
-  test-art-host-run-test-optimizing-norelocate-004-SignalTest32 \
-  test-art-host-run-test-optimizing-relocate-004-SignalTest32 \
   test-art-target-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
   test-art-target-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
   test-art-target-run-test-gcstress-default-prebuild-004-SignalTest32 \
@@ -45,7 +33,16 @@
   test-art-target-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
   test-art-target-run-test-gcstress-default-relocate-004-SignalTest32 \
   test-art-target-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
-  test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32
+  test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32 \
+  test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC32 \
+  test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC32 \
+  test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC32 \
+  test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC64 \
+  test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC64 \
+  test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC64
+
+# Failing valgrind tests.
+# Note: *all* 64b tests involving the runtime do not work currently. b/15170219.
 
 # List of known failing tests that when executed won't cause test execution to not finish.
 # The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64.
@@ -55,7 +52,13 @@
 ART_TEST_KEEP_GOING ?= true
 
 # Do you want all tests, even those that are time consuming?
-ART_TEST_FULL ?= true
+ART_TEST_FULL ?= false
+
+# Do you want default compiler tests run?
+ART_TEST_DEFAULT_COMPILER ?= true
+
+# Do you want interpreter tests run?
+ART_TEST_INTERPRETER ?= $(ART_TEST_FULL)
 
 # Do you want optimizing compiler tests run?
 ART_TEST_OPTIMIZING ?= $(ART_TEST_FULL)
@@ -69,17 +72,23 @@
 # Do you want tests with the GC stress mode enabled run?
 ART_TEST_GC_STRESS ?= $(ART_TEST_FULL)
 
-# Do you want run-tests with relocation enabled?
-ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL)
+# Do you want tests with the JNI forcecopy mode enabled run?
+ART_TEST_JNI_FORCECOPY ?= $(ART_TEST_FULL)
 
-# Do you want run-tests with relocation disabled?
+# Do you want run-tests with relocation disabled run?
 ART_TEST_RUN_TEST_NO_RELOCATE ?= $(ART_TEST_FULL)
 
-# Do you want run-tests with prebuild disabled?
+# Do you want run-tests with no prebuilding enabled run?
 ART_TEST_RUN_TEST_NO_PREBUILD ?= $(ART_TEST_FULL)
 
-# Do you want run-tests with prebuild enabled?
-ART_TEST_RUN_TEST_PREBUILD ?= true
+# Do you want run-tests without a pregenerated core.art?
+ART_TEST_RUN_TEST_NO_IMAGE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation enabled but patchoat failing?
+ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT ?= $(ART_TEST_FULL)
+
+# Do you want run-tests without a dex2oat?
+ART_TEST_RUN_TEST_NO_DEX2OAT ?= $(ART_TEST_FULL)
 
 # Do you want failed tests to have their artifacts cleaned up?
 ART_TEST_RUN_TEST_ALWAYS_CLEAN ?= true
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a7d852b..af43a3c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -48,7 +48,7 @@
 # Dex file dependencies for each gtest.
 ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod
-ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature
+ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Nested
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
@@ -61,6 +61,9 @@
 # The elf writer test has dependencies on core.oat.
 ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_OAT_OUT) $(2ND_HOST_CORE_OAT_OUT)
 ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_OAT_OUT) $(2ND_TARGET_CORE_OAT_OUT)
+ART_GTEST_jni_internal_test_TARGET_DEPS := $(TARGET_CORE_DEX_FILES)
+ART_GTEST_proxy_test_TARGET_DEPS := $(TARGET_CORE_DEX_FILES)
+ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_OAT_OUT) $(2ND_HOST_CORE_OAT_OUT)
 
 # The path for which all the source files are relative, not actually the current directory.
 LOCAL_PATH := art
@@ -137,18 +140,24 @@
   compiler/jni/jni_compiler_test.cc \
   compiler/oat_test.cc \
   compiler/optimizing/codegen_test.cc \
+  compiler/optimizing/dead_code_elimination_test.cc \
+  compiler/optimizing/constant_propagation_test.cc \
   compiler/optimizing/dominator_test.cc \
   compiler/optimizing/find_loops_test.cc \
+  compiler/optimizing/graph_checker_test.cc \
   compiler/optimizing/graph_test.cc \
+  compiler/optimizing/gvn_test.cc \
   compiler/optimizing/linearize_test.cc \
   compiler/optimizing/liveness_test.cc \
   compiler/optimizing/live_interval_test.cc \
   compiler/optimizing/live_ranges_test.cc \
+  compiler/optimizing/nodes_test.cc \
   compiler/optimizing/parallel_move_test.cc \
   compiler/optimizing/pretty_printer_test.cc \
   compiler/optimizing/register_allocator_test.cc \
   compiler/optimizing/ssa_test.cc \
   compiler/optimizing/stack_map_test.cc \
+  compiler/optimizing/suspend_check_test.cc \
   compiler/output_stream_test.cc \
   compiler/utils/arena_allocator_test.cc \
   compiler/utils/dedupe_set_test.cc \
@@ -440,7 +449,14 @@
   endif
 
   rule_name := $(3)test-art-$(1)-gtest$(4)
-  dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES)
+  ifeq ($(3),valgrind-)
+    ifneq ($(1),host)
+      $$(error valgrind tests only wired up for the host)
+    endif
+    dependencies := $$(ART_TEST_$(2)_VALGRIND_GTEST$(4)_RULES)
+  else
+    dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES)
+  endif
 
 .PHONY: $$(rule_name)
 $$(rule_name): $$(dependencies)
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 10936a4..1c462eb 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -25,8 +25,10 @@
 
 # Use dex2oat debug version for better error reporting
 # $(1): 2ND_ or undefined, 2ND_ for 32-bit host builds.
+# NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for
+# run-test --no-image
 define create-core-oat-host-rules
-$$($(1)HOST_CORE_IMG_OUT): $$(HOST_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY)
+$$($(1)HOST_CORE_IMG_OUT): $$(HOST_CORE_DEX_LOCATIONS) $$(DEX2OATD_DEPENDENCY)
 	@echo "host dex2oat: $$@ ($$?)"
 	@mkdir -p $$(dir $$@)
 	$$(hide) $$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 69f9387..8b5e6d5 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -48,6 +48,7 @@
 	dex/quick/mips/target_mips.cc \
 	dex/quick/mips/utility_mips.cc \
 	dex/quick/mir_to_lir.cc \
+	dex/quick/quick_compiler.cc \
 	dex/quick/ralloc_util.cc \
 	dex/quick/resource_mask.cc \
 	dex/quick/x86/assemble_x86.cc \
@@ -62,6 +63,7 @@
 	dex/mir_method_info.cc \
 	dex/mir_optimization.cc \
 	dex/bb_optimizations.cc \
+	dex/compiler_ir.cc \
 	dex/post_opt_passes.cc \
 	dex/pass_driver_me_opts.cc \
 	dex/pass_driver_me_post_opt.cc \
@@ -82,12 +84,18 @@
 	jni/quick/x86_64/calling_convention_x86_64.cc \
 	jni/quick/calling_convention.cc \
 	jni/quick/jni_compiler.cc \
+	llvm/llvm_compiler.cc \
 	optimizing/builder.cc \
 	optimizing/code_generator.cc \
 	optimizing/code_generator_arm.cc \
 	optimizing/code_generator_x86.cc \
 	optimizing/code_generator_x86_64.cc \
+	optimizing/constant_propagation.cc \
+	optimizing/dead_code_elimination.cc \
+	optimizing/graph_checker.cc \
 	optimizing/graph_visualizer.cc \
+	optimizing/gvn.cc \
+	optimizing/instruction_simplifier.cc \
 	optimizing/locations.cc \
 	optimizing/nodes.cc \
 	optimizing/optimizing_compiler.cc \
@@ -107,6 +115,7 @@
 	utils/arm64/assembler_arm64.cc \
 	utils/arm64/managed_register_arm64.cc \
 	utils/assembler.cc \
+	utils/dwarf_cfi.cc \
 	utils/mips/assembler_mips.cc \
 	utils/mips/managed_register_mips.cc \
 	utils/x86/assembler_x86.cc \
@@ -115,10 +124,8 @@
 	utils/x86_64/managed_register_x86_64.cc \
 	utils/scoped_arena_allocator.cc \
 	buffered_output_stream.cc \
-	compilers.cc \
 	compiler.cc \
 	elf_fixup.cc \
-	elf_patcher.cc \
 	elf_stripper.cc \
 	elf_writer.cc \
 	elf_writer_quick.cc \
@@ -282,10 +289,10 @@
 endef
 
 # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-libart-compiler,host,ndebug))
 endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-libart-compiler,host,debug))
 endif
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 1e6c038..fbaed9f 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -217,11 +217,8 @@
     // No code? You must mean to go into the interpreter.
     // Or the generic JNI...
     if (!method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
-      const void* method_code = GetPortableToInterpreterBridge();
-#else
-      const void* method_code = GetQuickToInterpreterBridge();
-#endif
+      const void* method_code = kUsePortableCompiler ? GetPortableToInterpreterBridge()
+          : GetQuickToInterpreterBridge();
       OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
       oat_method.LinkMethod(method);
       method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -234,7 +231,6 @@
     }
   }
   // Create bridges to transition between different kinds of compiled bridge.
-#if defined(ART_USE_PORTABLE_COMPILER)
   if (method->GetEntryPointFromPortableCompiledCode() == nullptr) {
     method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
   } else {
@@ -242,9 +238,6 @@
     method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
     method->SetIsPortableCompiled();
   }
-#else
-  CHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
-#endif
 }
 
 void CommonCompilerTest::MakeExecutable(const void* code_start, size_t code_length) {
@@ -387,9 +380,9 @@
   CompileMethod(method);
 }
 
-void CommonCompilerTest::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
-                                              const char* method_name, const char* signature)
-SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void CommonCompilerTest::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader,
+                                              const char* class_name, const char* method_name,
+                                              const char* signature) {
   std::string class_descriptor(DotToDescriptor(class_name));
   Thread* self = Thread::Current();
   mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index f098a34..698bf3b 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -148,16 +148,40 @@
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
                                const uint32_t fp_spill_mask,
+                               SrcMap* src_mapping_table,
                                const std::vector<uint8_t>& mapping_table,
                                const std::vector<uint8_t>& vmap_table,
                                const std::vector<uint8_t>& native_gc_map,
-                               const std::vector<uint8_t>* cfi_info)
+                               const std::vector<uint8_t>* cfi_info,
+                               const ArrayRef<LinkerPatch>& patches)
     : CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes),
       core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
-  mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
-  vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
-  gc_map_(driver->DeduplicateGCMap(native_gc_map)),
-  cfi_info_(driver->DeduplicateCFIInfo(cfi_info)) {
+      src_mapping_table_(driver->DeduplicateSrcMappingTable(src_mapping_table->Arrange())),
+      mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+      vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
+      gc_map_(driver->DeduplicateGCMap(native_gc_map)),
+      cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
+      patches_(patches.begin(), patches.end()) {
+}
+
+CompiledMethod::CompiledMethod(CompilerDriver* driver,
+                               InstructionSet instruction_set,
+                               const std::vector<uint8_t>& quick_code,
+                               const size_t frame_size_in_bytes,
+                               const uint32_t core_spill_mask,
+                               const uint32_t fp_spill_mask,
+                               const std::vector<uint8_t>& mapping_table,
+                               const std::vector<uint8_t>& stack_map)
+    : CompiledCode(driver, instruction_set, quick_code),
+      frame_size_in_bytes_(frame_size_in_bytes),
+      core_spill_mask_(core_spill_mask),
+      fp_spill_mask_(fp_spill_mask),
+      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+      mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+      vmap_table_(driver->DeduplicateVMapTable(stack_map)),
+      gc_map_(nullptr),
+      cfi_info_(nullptr),
+      patches_() {
 }
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver,
@@ -165,14 +189,17 @@
                                const std::vector<uint8_t>& code,
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
-                               const uint32_t fp_spill_mask)
+                               const uint32_t fp_spill_mask,
+                               const std::vector<uint8_t>* cfi_info)
     : CompiledCode(driver, instruction_set, code),
       frame_size_in_bytes_(frame_size_in_bytes),
       core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
+      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
       mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
       vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
       gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
-      cfi_info_(nullptr) {
+      cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
+      patches_() {
 }
 
 // Constructs a CompiledMethod for the Portable compiler.
@@ -181,19 +208,26 @@
                                const std::string& symbol)
     : CompiledCode(driver, instruction_set, code, symbol),
       frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0), gc_map_(driver->DeduplicateGCMap(gc_map)) {
-  mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
-  vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
+      fp_spill_mask_(0),
+      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+      mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
+      vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
+      gc_map_(driver->DeduplicateGCMap(gc_map)),
+      cfi_info_(nullptr),
+      patches_() {
 }
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
                                const std::string& code, const std::string& symbol)
     : CompiledCode(driver, instruction_set, code, symbol),
       frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0) {
-  mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
-  vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
-  gc_map_ = driver->DeduplicateGCMap(std::vector<uint8_t>());
+      fp_spill_mask_(0),
+      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+      mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
+      vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
+      gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
+      cfi_info_(nullptr),
+      patches_() {
 }
 
 }  // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index b8cd851..cdae8d2 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -22,7 +22,9 @@
 #include <vector>
 
 #include "instruction_set.h"
+#include "method_reference.h"
 #include "utils.h"
+#include "utils/array_ref.h"
 
 namespace llvm {
   class Function;
@@ -100,9 +102,189 @@
   std::vector<uint32_t> oatdata_offsets_to_compiled_code_offset_;
 };
 
-class CompiledMethod : public CompiledCode {
+class SrcMapElem {
  public:
-  // Constructs a CompiledMethod for the non-LLVM compilers.
+  uint32_t from_;
+  int32_t to_;
+
+  explicit operator int64_t() const {
+    return (static_cast<int64_t>(to_) << 32) | from_;
+  }
+
+  bool operator<(const SrcMapElem& sme) const {
+    return int64_t(*this) < int64_t(sme);
+  }
+
+  bool operator==(const SrcMapElem& sme) const {
+    return int64_t(*this) == int64_t(sme);
+  }
+
+  explicit operator uint8_t() const {
+    return static_cast<uint8_t>(from_ + to_);
+  }
+};
+
+class SrcMap FINAL : public std::vector<SrcMapElem> {
+ public:
+  void SortByFrom() {
+    std::sort(begin(), end(), [] (const SrcMapElem& lhs, const SrcMapElem& rhs) -> bool {
+      return lhs.from_ < rhs.from_;
+    });
+  }
+
+  const_iterator FindByTo(int32_t to) const {
+    return std::lower_bound(begin(), end(), SrcMapElem({0, to}));
+  }
+
+  SrcMap& Arrange() {
+    if (!empty()) {
+      std::sort(begin(), end());
+      resize(std::unique(begin(), end()) - begin());
+      shrink_to_fit();
+    }
+    return *this;
+  }
+
+  void DeltaFormat(const SrcMapElem& start, uint32_t highest_pc) {
+    // Convert from abs values to deltas.
+    if (!empty()) {
+      SortByFrom();
+
+      // TODO: one PC can be mapped to several Java src lines.
+      // do we want such a one-to-many correspondence?
+
+      // get rid of the highest values
+      size_t i = size() - 1;
+      for (; i > 0 ; i--) {
+        if ((*this)[i].from_ < highest_pc) {
+          break;
+        }
+      }
+      this->resize(i + 1);
+
+      for (size_t i = size(); --i >= 1; ) {
+        (*this)[i].from_ -= (*this)[i-1].from_;
+        (*this)[i].to_ -= (*this)[i-1].to_;
+      }
+      DCHECK((*this)[0].from_ >= start.from_);
+      (*this)[0].from_ -= start.from_;
+      (*this)[0].to_ -= start.to_;
+    }
+  }
+};
+
+enum LinkerPatchType {
+  kLinkerPatchMethod,
+  kLinkerPatchCall,
+  kLinkerPatchCallRelative,  // NOTE: Actual patching is instruction_set-dependent.
+  kLinkerPatchType,
+};
+
+class LinkerPatch {
+ public:
+  static LinkerPatch MethodPatch(size_t literal_offset,
+                                 const DexFile* target_dex_file,
+                                 uint32_t target_method_idx) {
+    return LinkerPatch(literal_offset, kLinkerPatchMethod,
+                       target_method_idx, target_dex_file);
+  }
+
+  static LinkerPatch CodePatch(size_t literal_offset,
+                               const DexFile* target_dex_file,
+                               uint32_t target_method_idx) {
+    return LinkerPatch(literal_offset, kLinkerPatchCall,
+                       target_method_idx, target_dex_file);
+  }
+
+  static LinkerPatch RelativeCodePatch(size_t literal_offset,
+                                       const DexFile* target_dex_file,
+                                       uint32_t target_method_idx) {
+    return LinkerPatch(literal_offset, kLinkerPatchCallRelative,
+                       target_method_idx, target_dex_file);
+  }
+
+  static LinkerPatch TypePatch(size_t literal_offset,
+                               const DexFile* target_dex_file,
+                               uint32_t target_type_idx) {
+    return LinkerPatch(literal_offset, kLinkerPatchType, target_type_idx, target_dex_file);
+  }
+
+  LinkerPatch(const LinkerPatch& other) = default;
+  LinkerPatch& operator=(const LinkerPatch& other) = default;
+
+  size_t LiteralOffset() const {
+    return literal_offset_;
+  }
+
+  LinkerPatchType Type() const {
+    return patch_type_;
+  }
+
+  MethodReference TargetMethod() const {
+    DCHECK(patch_type_ == kLinkerPatchMethod ||
+           patch_type_ == kLinkerPatchCall || patch_type_ == kLinkerPatchCallRelative);
+    return MethodReference(target_dex_file_, target_idx_);
+  }
+
+  const DexFile* TargetTypeDexFile() const {
+    DCHECK(patch_type_ == kLinkerPatchType);
+    return target_dex_file_;
+  }
+
+  uint32_t TargetTypeIndex() const {
+    DCHECK(patch_type_ == kLinkerPatchType);
+    return target_idx_;
+  }
+
+ private:
+  LinkerPatch(size_t literal_offset, LinkerPatchType patch_type,
+              uint32_t target_idx, const DexFile* target_dex_file)
+      : literal_offset_(literal_offset),
+        patch_type_(patch_type),
+        target_idx_(target_idx),
+        target_dex_file_(target_dex_file) {
+  }
+
+  size_t literal_offset_;
+  LinkerPatchType patch_type_;
+  uint32_t target_idx_;  // Method index (Call/Method patches) or type index (Type patches).
+  const DexFile* target_dex_file_;
+
+  friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
+  friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
+};
+
+inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
+  return lhs.literal_offset_ == rhs.literal_offset_ &&
+      lhs.patch_type_ == rhs.patch_type_ &&
+      lhs.target_idx_ == rhs.target_idx_ &&
+      lhs.target_dex_file_ == rhs.target_dex_file_;
+}
+
+inline bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs) {
+  return (lhs.literal_offset_ != rhs.literal_offset_) ? lhs.literal_offset_ < rhs.literal_offset_
+      : (lhs.patch_type_ != rhs.patch_type_) ? lhs.patch_type_ < rhs.patch_type_
+      : (lhs.target_idx_ != rhs.target_idx_) ? lhs.target_idx_ < rhs.target_idx_
+      : lhs.target_dex_file_ < rhs.target_dex_file_;
+}
+
+class CompiledMethod FINAL : public CompiledCode {
+ public:
+  // Constructs a CompiledMethod for Quick.
+  CompiledMethod(CompilerDriver* driver,
+                 InstructionSet instruction_set,
+                 const std::vector<uint8_t>& quick_code,
+                 const size_t frame_size_in_bytes,
+                 const uint32_t core_spill_mask,
+                 const uint32_t fp_spill_mask,
+                 SrcMap* src_mapping_table,
+                 const std::vector<uint8_t>& mapping_table,
+                 const std::vector<uint8_t>& vmap_table,
+                 const std::vector<uint8_t>& native_gc_map,
+                 const std::vector<uint8_t>* cfi_info,
+                 const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
+
+  // Constructs a CompiledMethod for Optimizing.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
                  const std::vector<uint8_t>& quick_code,
@@ -110,9 +292,7 @@
                  const uint32_t core_spill_mask,
                  const uint32_t fp_spill_mask,
                  const std::vector<uint8_t>& mapping_table,
-                 const std::vector<uint8_t>& vmap_table,
-                 const std::vector<uint8_t>& native_gc_map,
-                 const std::vector<uint8_t>* cfi_info);
+                 const std::vector<uint8_t>& vmap_table);
 
   // Constructs a CompiledMethod for the QuickJniCompiler.
   CompiledMethod(CompilerDriver* driver,
@@ -120,7 +300,8 @@
                  const std::vector<uint8_t>& quick_code,
                  const size_t frame_size_in_bytes,
                  const uint32_t core_spill_mask,
-                 const uint32_t fp_spill_mask);
+                 const uint32_t fp_spill_mask,
+                 const std::vector<uint8_t>* cfi_info);
 
   // Constructs a CompiledMethod for the Portable compiler.
   CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
@@ -144,6 +325,11 @@
     return fp_spill_mask_;
   }
 
+  const SrcMap& GetSrcMappingTable() const {
+    DCHECK(src_mapping_table_ != nullptr);
+    return *src_mapping_table_;
+  }
+
   const std::vector<uint8_t>& GetMappingTable() const {
     DCHECK(mapping_table_ != nullptr);
     return *mapping_table_;
@@ -154,15 +340,18 @@
     return *vmap_table_;
   }
 
-  const std::vector<uint8_t>& GetGcMap() const {
-    DCHECK(gc_map_ != nullptr);
-    return *gc_map_;
+  std::vector<uint8_t> const* GetGcMap() const {
+    return gc_map_;
   }
 
   const std::vector<uint8_t>* GetCFIInfo() const {
     return cfi_info_;
   }
 
+  const std::vector<LinkerPatch>& GetPatches() const {
+    return patches_;
+  }
+
  private:
   // For quick code, the size of the activation used by the code.
   const size_t frame_size_in_bytes_;
@@ -170,6 +359,8 @@
   const uint32_t core_spill_mask_;
   // For quick code, a bit mask describing spilled FPR callee-save registers.
   const uint32_t fp_spill_mask_;
+  // For quick code, a set of pairs (PC, Line) mapping from native PC offset to Java line
+  SrcMap* src_mapping_table_;
   // For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to
   // native PC offset. Size prefixed.
   std::vector<uint8_t>* mapping_table_;
@@ -180,6 +371,8 @@
   std::vector<uint8_t>* gc_map_;
   // For quick code, a FDE entry for the debug_frame section.
   std::vector<uint8_t>* cfi_info_;
+  // For quick code, linker patches needed by the method.
+  std::vector<LinkerPatch> patches_;
 };
 
 }  // namespace art
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index a832c31..fbfd8e6 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -15,14 +15,12 @@
  */
 
 #include "compiler.h"
-#include "compilers.h"
-#include "driver/compiler_driver.h"
-#include "mirror/art_method-inl.h"
 
-#ifdef ART_USE_PORTABLE_COMPILER
-#include "dex/portable/mir_to_gbc.h"
-#include "elf_writer_mclinker.h"
-#endif
+#include "base/logging.h"
+#include "dex/quick/quick_compiler.h"
+#include "driver/compiler_driver.h"
+#include "llvm/llvm_compiler.h"
+#include "optimizing/optimizing_compiler.h"
 
 namespace art {
 
@@ -60,137 +58,21 @@
   return nullptr;
 }
 
-
-#ifdef ART_USE_PORTABLE_COMPILER
-
-extern "C" void ArtInitCompilerContext(art::CompilerDriver* driver);
-
-extern "C" void ArtUnInitCompilerContext(art::CompilerDriver* driver);
-
-extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver* driver,
-                                                 const art::DexFile::CodeItem* code_item,
-                                                 uint32_t access_flags,
-                                                 art::InvokeType invoke_type,
-                                                 uint16_t class_def_idx,
-                                                 uint32_t method_idx,
-                                                 jobject class_loader,
-                                                 const art::DexFile& dex_file);
-
-extern "C" art::CompiledMethod* ArtLLVMJniCompileMethod(art::CompilerDriver* driver,
-                                                        uint32_t access_flags, uint32_t method_idx,
-                                                        const art::DexFile& dex_file);
-
-extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver* driver,
-                                               std::string const& filename);
-
-
-class LLVMCompiler FINAL : public Compiler {
- public:
-  explicit LLVMCompiler(CompilerDriver* driver) : Compiler(driver, 1000) {}
-
-  void Init() const OVERRIDE {
-    ArtInitCompilerContext(GetCompilerDriver());
-  }
-
-  void UnInit() const OVERRIDE {
-    ArtUnInitCompilerContext(GetCompilerDriver());
-  }
-
-  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
-                          uint32_t access_flags,
-                          InvokeType invoke_type,
-                          uint16_t class_def_idx,
-                          uint32_t method_idx,
-                          jobject class_loader,
-                          const DexFile& dex_file) const OVERRIDE {
-    CompiledMethod* method = TryCompileWithSeaIR(code_item,
-                                                 access_flags,
-                                                 invoke_type,
-                                                 class_def_idx,
-                                                 method_idx,
-                                                 class_loader,
-                                                 dex_file);
-    if (method != nullptr) {
-      return method;
-    }
-
-    return ArtCompileMethod(GetCompilerDriver(),
-                            code_item,
-                            access_flags,
-                            invoke_type,
-                            class_def_idx,
-                            method_idx,
-                            class_loader,
-                            dex_file);
-  }
-
-  CompiledMethod* JniCompile(uint32_t access_flags,
-                             uint32_t method_idx,
-                             const DexFile& dex_file) const OVERRIDE {
-    return ArtLLVMJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
-  }
-
-  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const {
-    return reinterpret_cast<uintptr_t>(method->GetEntryPointFromPortableCompiledCode());
-  }
-
-  bool WriteElf(art::File* file,
-                OatWriter* oat_writer,
-                const std::vector<const art::DexFile*>& dex_files,
-                const std::string& android_root,
-                bool is_host, const CompilerDriver& driver) const
-      OVERRIDE
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return art::ElfWriterMclinker::Create(
-        file, oat_writer, dex_files, android_root, is_host, driver);
-  }
-
-  Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
-    return PortableCodeGenerator(
-        cu, cu->mir_graph.get(), &cu->arena,
-        reinterpret_cast<art::llvm::LlvmCompilationUnit*>(compilation_unit));
-  }
-
-  void InitCompilationUnit(CompilationUnit& cu) const {
-      // Fused long branches not currently useful in bitcode.
-    cu.disable_opt |=
-        (1 << kBranchFusing) |
-        (1 << kSuppressExceptionEdges);
-  }
-
-  bool IsPortable() const OVERRIDE {
-    return true;
-  }
-
-  void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
-    typedef void (*SetBitcodeFileNameFn)(const CompilerDriver&, const std::string&);
-
-    SetBitcodeFileNameFn set_bitcode_file_name =
-      reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName);
-
-    set_bitcode_file_name(driver, filename);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LLVMCompiler);
-};
-#endif
-
 Compiler* Compiler::Create(CompilerDriver* driver, Compiler::Kind kind) {
   switch (kind) {
     case kQuick:
-      return new QuickCompiler(driver);
-      break;
+      return CreateQuickCompiler(driver);
+
     case kOptimizing:
-      return new OptimizingCompiler(driver);
-      break;
+      return CreateOptimizingCompiler(driver);
+
     case kPortable:
-#ifdef ART_USE_PORTABLE_COMPILER
-      return new LLVMCompiler(driver);
-#else
-      LOG(FATAL) << "Portable compiler not compiled";
-#endif
-      break;
+      {
+        Compiler* compiler = CreateLLVMCompiler(driver);
+        CHECK(compiler != nullptr) << "Portable compiler not compiled";
+        return compiler;
+      }
+
     default:
       LOG(FATAL) << "UNREACHABLE";
   }
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 4caebf3..05fa858 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -26,13 +26,19 @@
 struct CompilationUnit;
 class CompilerDriver;
 class CompiledMethod;
-class MIRGraph;
 class OatWriter;
 
 namespace mirror {
   class ArtMethod;
 }
 
+// Base class for compiler-specific thread-local storage for compiler worker threads
+class CompilerTls {
+  public:
+    CompilerTls() {}
+    ~CompilerTls() {}
+};
+
 class Compiler {
  public:
   enum Kind {
@@ -47,6 +53,9 @@
 
   virtual void UnInit() const = 0;
 
+  virtual bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu)
+      const = 0;
+
   virtual CompiledMethod* Compile(const DexFile::CodeItem* code_item,
                                   uint32_t access_flags,
                                   InvokeType invoke_type,
@@ -109,6 +118,10 @@
     return nullptr;
   }
 
+  virtual CompilerTls* CreateNewCompilerTls() {
+    return nullptr;
+  }
+
  protected:
   explicit Compiler(CompilerDriver* driver, uint64_t warning) :
       driver_(driver), maximum_compilation_time_before_warning_(warning) {
diff --git a/compiler/compilers.cc b/compiler/compilers.cc
deleted file mode 100644
index 250924a..0000000
--- a/compiler/compilers.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "compilers.h"
-
-#include "dex/mir_graph.h"
-#include "dex/quick/mir_to_lir.h"
-#include "elf_writer_quick.h"
-#include "mirror/art_method-inl.h"
-
-namespace art {
-
-extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver* driver);
-extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver* driver);
-extern "C" art::CompiledMethod* ArtQuickCompileMethod(art::CompilerDriver* driver,
-                                                      const art::DexFile::CodeItem* code_item,
-                                                      uint32_t access_flags,
-                                                      art::InvokeType invoke_type,
-                                                      uint16_t class_def_idx,
-                                                      uint32_t method_idx,
-                                                      jobject class_loader,
-                                                      const art::DexFile& dex_file);
-
-extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver* driver,
-                                                         uint32_t access_flags, uint32_t method_idx,
-                                                         const art::DexFile& dex_file);
-
-// Hack for CFI CIE initialization
-extern std::vector<uint8_t>* X86CFIInitialization(bool is_x86_64);
-
-void QuickCompiler::Init() const {
-  ArtInitQuickCompilerContext(GetCompilerDriver());
-}
-
-void QuickCompiler::UnInit() const {
-  ArtUnInitQuickCompilerContext(GetCompilerDriver());
-}
-
-CompiledMethod* QuickCompiler::Compile(const DexFile::CodeItem* code_item,
-                                       uint32_t access_flags,
-                                       InvokeType invoke_type,
-                                       uint16_t class_def_idx,
-                                       uint32_t method_idx,
-                                       jobject class_loader,
-                                       const DexFile& dex_file) const {
-  CompiledMethod* method = TryCompileWithSeaIR(code_item,
-                                               access_flags,
-                                               invoke_type,
-                                               class_def_idx,
-                                               method_idx,
-                                               class_loader,
-                                               dex_file);
-  if (method != nullptr) {
-    return method;
-  }
-
-  return ArtQuickCompileMethod(GetCompilerDriver(),
-                               code_item,
-                               access_flags,
-                               invoke_type,
-                               class_def_idx,
-                               method_idx,
-                               class_loader,
-                               dex_file);
-}
-
-CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags,
-                                          uint32_t method_idx,
-                                          const DexFile& dex_file) const {
-  return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
-}
-
-uintptr_t QuickCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
-  return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode());
-}
-
-bool QuickCompiler::WriteElf(art::File* file,
-                             OatWriter* oat_writer,
-                             const std::vector<const art::DexFile*>& dex_files,
-                             const std::string& android_root,
-                             bool is_host) const {
-  return art::ElfWriterQuick::Create(file, oat_writer, dex_files, android_root, is_host,
-                                     *GetCompilerDriver());
-}
-
-Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
-  Mir2Lir* mir_to_lir = nullptr;
-  switch (cu->instruction_set) {
-    case kThumb2:
-      mir_to_lir = ArmCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
-      break;
-    case kArm64:
-      mir_to_lir = Arm64CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
-      break;
-    case kMips:
-      mir_to_lir = MipsCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
-      break;
-    case kX86:
-      // Fall-through.
-    case kX86_64:
-      mir_to_lir = X86CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
-      break;
-    default:
-      LOG(FATAL) << "Unexpected instruction set: " << cu->instruction_set;
-  }
-
-  /* The number of compiler temporaries depends on backend so set it up now if possible */
-  if (mir_to_lir) {
-    size_t max_temps = mir_to_lir->GetMaxPossibleCompilerTemps();
-    bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps);
-    CHECK(set_max);
-  }
-  return mir_to_lir;
-}
-
-std::vector<uint8_t>* QuickCompiler::GetCallFrameInformationInitialization(
-    const CompilerDriver& driver) const {
-  if (driver.GetInstructionSet() == kX86) {
-    return X86CFIInitialization(false);
-  }
-  if (driver.GetInstructionSet() == kX86_64) {
-    return X86CFIInitialization(true);
-  }
-  return nullptr;
-}
-
-CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
-                                            uint32_t access_flags,
-                                            InvokeType invoke_type,
-                                            uint16_t class_def_idx,
-                                            uint32_t method_idx,
-                                            jobject class_loader,
-                                            const DexFile& dex_file) const {
-  CompiledMethod* method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
-                                      method_idx, class_loader, dex_file);
-  if (method != nullptr) {
-    return method;
-  }
-
-  return QuickCompiler::Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
-                                class_loader, dex_file);
-}
-
-}  // namespace art
diff --git a/compiler/compilers.h b/compiler/compilers.h
deleted file mode 100644
index 2c231e1..0000000
--- a/compiler/compilers.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_COMPILERS_H_
-#define ART_COMPILER_COMPILERS_H_
-
-#include "compiler.h"
-
-namespace art {
-
-class QuickCompiler : public Compiler {
- public:
-  explicit QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {}
-
-  void Init() const OVERRIDE;
-
-  void UnInit() const OVERRIDE;
-
-  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
-                          uint32_t access_flags,
-                          InvokeType invoke_type,
-                          uint16_t class_def_idx,
-                          uint32_t method_idx,
-                          jobject class_loader,
-                          const DexFile& dex_file) const OVERRIDE;
-
-  CompiledMethod* JniCompile(uint32_t access_flags,
-                             uint32_t method_idx,
-                             const DexFile& dex_file) const OVERRIDE;
-
-  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  bool WriteElf(art::File* file,
-                OatWriter* oat_writer,
-                const std::vector<const art::DexFile*>& dex_files,
-                const std::string& android_root,
-                bool is_host) const
-    OVERRIDE
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
-
-  void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE {}
-
-  /*
-   * @brief Generate and return Dwarf CFI initialization, if supported by the
-   * backend.
-   * @param driver CompilerDriver for this compile.
-   * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF
-   * information.
-   * @note This is used for backtrace information in generated code.
-   */
-  std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) const
-      OVERRIDE;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
-};
-
-class OptimizingCompiler FINAL : public QuickCompiler {
- public:
-  explicit OptimizingCompiler(CompilerDriver* driver);
-
-  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
-                          uint32_t access_flags,
-                          InvokeType invoke_type,
-                          uint16_t class_def_idx,
-                          uint32_t method_idx,
-                          jobject class_loader,
-                          const DexFile& dex_file) const OVERRIDE;
-
-  CompiledMethod* TryCompile(const DexFile::CodeItem* code_item,
-                             uint32_t access_flags,
-                             InvokeType invoke_type,
-                             uint16_t class_def_idx,
-                             uint32_t method_idx,
-                             jobject class_loader,
-                             const DexFile& dex_file) const;
-
- private:
-  std::unique_ptr<std::ostream> visualizer_output_;
-
-  DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
-};
-
-}  // namespace art
-
-#endif  // ART_COMPILER_COMPILERS_H_
diff --git a/compiler/dex/backend.h b/compiler/dex/backend.h
index 1f24849..cab3427 100644
--- a/compiler/dex/backend.h
+++ b/compiler/dex/backend.h
@@ -38,14 +38,15 @@
 
     /*
      * Return the number of reservable vector registers supported
-     * @param fp_used  ‘true’ if floating point computations will be
-     * executed while vector registers are reserved.
+     * @param long_or_fp ‘true’ if floating point computations will be
+     * executed or the operations will be long type while vector
+     * registers are reserved.
      * @return the number of vector registers that are available
      * @note The backend should ensure that sufficient vector registers
      * are held back to generate scalar code without exhausting vector
      * registers, if scalar code also uses the vector registers.
      */
-    virtual int NumReservableVectorRegisters(bool fp_used) { return 0; }
+    virtual int NumReservableVectorRegisters(bool long_or_fp) { return 0; }
 
   protected:
     explicit Backend(ArenaAllocator* arena) : arena_(arena) {}
diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc
index 920cde2..6a610ab 100644
--- a/compiler/dex/bb_optimizations.cc
+++ b/compiler/dex/bb_optimizations.cc
@@ -23,9 +23,9 @@
 /*
  * Code Layout pass implementation start.
  */
-bool CodeLayout::Worker(const PassDataHolder* data) const {
+bool CodeLayout::Worker(PassDataHolder* data) const {
   DCHECK(data != nullptr);
-  const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
   CompilationUnit* c_unit = pass_me_data_holder->c_unit;
   DCHECK(c_unit != nullptr);
   BasicBlock* bb = pass_me_data_holder->bb;
@@ -38,9 +38,9 @@
 /*
  * BasicBlock Combine pass implementation start.
  */
-bool BBCombine::Worker(const PassDataHolder* data) const {
+bool BBCombine::Worker(PassDataHolder* data) const {
   DCHECK(data != nullptr);
-  const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
   CompilationUnit* c_unit = pass_me_data_holder->c_unit;
   DCHECK(c_unit != nullptr);
   BasicBlock* bb = pass_me_data_holder->bb;
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index 7395324..b2c348b 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_
 #define ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_
 
+#include "base/casts.h"
 #include "compiler_internals.h"
 #include "pass_me.h"
 
@@ -33,16 +34,16 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->DoCacheFieldLoweringInfo();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->DoCacheFieldLoweringInfo();
   }
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->HasFieldAccess();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->HasFieldAccess();
   }
 };
 
@@ -57,16 +58,16 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->DoCacheMethodLoweringInfo();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->DoCacheMethodLoweringInfo();
   }
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->HasInvokes();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->HasInvokes();
   }
 };
 
@@ -83,35 +84,35 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->InlineSpecialMethodsGate();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->InlineSpecialMethodsGate();
   }
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->InlineSpecialMethodsStart();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->InlineSpecialMethodsStart();
   }
 
-  bool Worker(const PassDataHolder* data) const {
+  bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    cUnit->mir_graph->InlineSpecialMethods(bb);
+    c_unit->mir_graph->InlineSpecialMethods(bb);
     // No need of repeating, so just return false.
     return false;
   }
 
   void End(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->InlineSpecialMethodsEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->InlineSpecialMethodsEnd();
   }
 };
 
@@ -126,12 +127,13 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->VerifyDataflow();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->VerifyDataflow();
+    c_unit->mir_graph->ClearAllVisitedFlags();
   }
 
-  bool Worker(const PassDataHolder* data) const;
+  bool Worker(PassDataHolder* data) const;
 };
 
 /**
@@ -146,26 +148,26 @@
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->EliminateNullChecksAndInferTypesStart();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->EliminateNullChecksAndInferTypesStart();
   }
 
-  bool Worker(const PassDataHolder* data) const {
+  bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    return cUnit->mir_graph->EliminateNullChecksAndInferTypes(bb);
+    return c_unit->mir_graph->EliminateNullChecksAndInferTypes(bb);
   }
 
   void End(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->EliminateNullChecksAndInferTypesEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->EliminateNullChecksAndInferTypesEnd();
   }
 };
 
@@ -177,26 +179,26 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->EliminateClassInitChecksGate();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->EliminateClassInitChecksGate();
   }
 
-  bool Worker(const PassDataHolder* data) const {
+  bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    return cUnit->mir_graph->EliminateClassInitChecks(bb);
+    return c_unit->mir_graph->EliminateClassInitChecks(bb);
   }
 
   void End(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->EliminateClassInitChecksEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->EliminateClassInitChecksEnd();
   }
 };
 
@@ -212,26 +214,26 @@
 
   bool Gate(const PassDataHolder* data) const OVERRIDE {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return cUnit->mir_graph->ApplyGlobalValueNumberingGate();
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->ApplyGlobalValueNumberingGate();
   }
 
-  bool Worker(const PassDataHolder* data) const OVERRIDE {
+  bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
-    CompilationUnit* cUnit = pass_me_data_holder->c_unit;
-    DCHECK(cUnit != nullptr);
+    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
     BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    return cUnit->mir_graph->ApplyGlobalValueNumbering(bb);
+    return c_unit->mir_graph->ApplyGlobalValueNumbering(bb);
   }
 
   void End(PassDataHolder* data) const OVERRIDE {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    cUnit->mir_graph->ApplyGlobalValueNumberingEnd();
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->ApplyGlobalValueNumberingEnd();
   }
 };
 
@@ -246,12 +248,12 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return ((cUnit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
   }
 
-  bool Worker(const PassDataHolder* data) const;
+  bool Worker(PassDataHolder* data) const;
 };
 
 /**
@@ -265,9 +267,9 @@
 
   bool Gate(const PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-    DCHECK(cUnit != nullptr);
-    return ((cUnit->disable_opt & (1 << kBBOpt)) == 0);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return ((c_unit->disable_opt & (1 << kBBOpt)) == 0);
   }
 
   void Start(PassDataHolder* data) const;
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index dcc67c3..e4003bf 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -107,14 +107,52 @@
 enum ExtendedMIROpcode {
   kMirOpFirst = kNumPackedOpcodes,
   kMirOpPhi = kMirOpFirst,
+
+  // @brief Copy from one VR to another.
+  // @details
+  // vA: destination VR
+  // vB: source VR
   kMirOpCopy,
+
+  // @brief Used to do float comparison with less-than bias.
+  // @details Unlike cmpl-float, this does not store result of comparison in VR.
+  // vA: left-hand side VR for comparison.
+  // vB: right-hand side VR for comparison.
   kMirOpFusedCmplFloat,
+
+  // @brief Used to do float comparison with greater-than bias.
+  // @details Unlike cmpg-float, this does not store result of comparison in VR.
+  // vA: left-hand side VR for comparison.
+  // vB: right-hand side VR for comparison.
   kMirOpFusedCmpgFloat,
+
+  // @brief Used to do double comparison with less-than bias.
+  // @details Unlike cmpl-double, this does not store result of comparison in VR.
+  // vA: left-hand side wide VR for comparison.
+  // vB: right-hand side wide VR for comparison.
   kMirOpFusedCmplDouble,
+
+  // @brief Used to do double comparison with greater-than bias.
+  // @details Unlike cmpl-double, this does not store result of comparison in VR.
+  // vA: left-hand side wide VR for comparison.
+  // vB: right-hand side wide VR for comparison.
   kMirOpFusedCmpgDouble,
+
+  // @brief Used to do comparison of 64-bit long integers.
+  // @details Unlike cmp-long, this does not store result of comparison in VR.
+  // vA: left-hand side wide VR for comparison.
+  // vB: right-hand side wide VR for comparison.
   kMirOpFusedCmpLong,
+
+  // @brief This represents no-op.
   kMirOpNop,
+
+  // @brief Do a null check on the object register.
+  // @details The backends may implement this implicitly or explicitly. This MIR is guaranteed
+  // to have the correct offset as an exception thrower.
+  // vA: object register
   kMirOpNullCheck,
+
   kMirOpRangeCheck,
   kMirOpDivZeroCheck,
   kMirOpCheck,
@@ -218,16 +256,47 @@
   // vC: TypeSize
   kMirOpPackedSet,
 
-  // @brief Reserve N vector registers (named 0..N-1)
-  // vA: Number of registers
+  // @brief Reserve a range of vector registers.
+  // vA: Start vector register to reserve.
+  // vB: Inclusive end vector register to reserve.
   // @note: The backend may choose to map vector numbers used in vector opcodes.
   //  Reserved registers are removed from the list of backend temporary pool.
   kMirOpReserveVectorRegisters,
 
-  // @brief Free Reserved vector registers
+  // @brief Free a range of reserved vector registers
+  // vA: Start vector register to unreserve.
+  // vB: Inclusive end vector register to unreserve.
   // @note: All currently reserved vector registers are returned to the temporary pool.
   kMirOpReturnVectorRegisters,
 
+  // @brief Create a memory barrier.
+  // vA: a constant defined by enum MemBarrierKind.
+  kMirOpMemBarrier,
+
+  // @brief Used to fill a vector register with array values.
+  // @details Just as with normal arrays, access on null object register must ensure NullPointerException
+  // and invalid index must ensure ArrayIndexOutOfBoundsException. Exception behavior must be the same
+  // as the aget it replaced and must happen at same index. Therefore, it is generally recommended that
+  // before using this MIR, it is proven that exception is guaranteed to not be thrown and marked with
+  // MIR_IGNORE_NULL_CHECK and MIR_IGNORE_RANGE_CHECK.
+  // vA: destination vector register
+  // vB: array register
+  // vC: index register
+  // arg[0]: TypeSize (most other vector opcodes have this in vC)
+  kMirOpPackedArrayGet,
+
+  // @brief Used to store a vector register into array.
+  // @details Just as with normal arrays, access on null object register must ensure NullPointerException
+  // and invalid index must ensure ArrayIndexOutOfBoundsException. Exception behavior must be the same
+  // as the aget it replaced and must happen at same index. Therefore, it is generally recommended that
+  // before using this MIR, it is proven that exception is guaranteed to not be thrown and marked with
+  // MIR_IGNORE_NULL_CHECK and MIR_IGNORE_RANGE_CHECK.
+  // vA: source vector register
+  // vB: array register
+  // vC: index register
+  // arg[0]: TypeSize (most other vector opcodes have this in vC)
+  kMirOpPackedArrayPut,
+
   kMirOpLast,
 };
 
@@ -243,6 +312,7 @@
   kMIRIgnoreSuspendCheck,
   kMIRDup,
   kMIRMark,                           // Temporary node mark.
+  kMIRStoreNonTemporal,
   kMIRLastMIRFlag,
 };
 
@@ -447,12 +517,15 @@
  * -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrierafter each volatile load.
  * -# Use StoreStore barrier after all stores but before return from any constructor whose
  *    class has final fields.
+ * -# Use NTStoreStore to order non-temporal stores with respect to all later
+ *    store-to-memory instructions.  Only generated together with non-temporal stores.
  */
 enum MemBarrierKind {
   kAnyStore,
   kLoadAny,
   kStoreStore,
-  kAnyAny
+  kAnyAny,
+  kNTStoreStore,
 };
 
 std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind);
@@ -528,6 +601,7 @@
   kFixupLoad,        // Mostly for immediates.
   kFixupVLoad,       // FP load which *may* be pc-relative.
   kFixupCBxZ,        // Cbz, Cbnz.
+  kFixupTBxZ,        // Tbz, Tbnz.
   kFixupPushPop,     // Not really pc relative, but changes size based on args.
   kFixupCondBranch,  // Conditional branch
   kFixupT1Branch,    // Thumb1 Unconditional branch
diff --git a/compiler/dex/compiler_internals.h b/compiler/dex/compiler_internals.h
index 9dd0272..2019f0b 100644
--- a/compiler/dex/compiler_internals.h
+++ b/compiler/dex/compiler_internals.h
@@ -23,14 +23,9 @@
 #include <stdio.h>
 
 #include "base/logging.h"
-#include "class_linker.h"
-#include "driver/compiler_driver.h"
-#include "quick/mir_to_lir.h"
 #include "mir_graph.h"
 #include "compiler_ir.h"
-#include "frontend.h"
-#include "monitor.h"
-#include "thread.h"
+#include "frontend.h"  // Debug flags.
 #include "utils.h"
 
 #endif  // ART_COMPILER_DEX_COMPILER_INTERNALS_H_
diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc
new file mode 100644
index 0000000..909c995
--- /dev/null
+++ b/compiler/dex/compiler_ir.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler_ir.h"
+
+#include "backend.h"
+#include "frontend.h"
+#include "mir_graph.h"
+
+namespace art {
+
+CompilationUnit::CompilationUnit(ArenaPool* pool)
+  : compiler_driver(nullptr),
+    class_linker(nullptr),
+    dex_file(nullptr),
+    class_loader(nullptr),
+    class_def_idx(0),
+    method_idx(0),
+    access_flags(0),
+    invoke_type(kDirect),
+    shorty(nullptr),
+    disable_opt(0),
+    enable_debug(0),
+    verbose(false),
+    compiler(nullptr),
+    instruction_set(kNone),
+    target64(false),
+    compiler_flip_match(false),
+    arena(pool),
+    arena_stack(pool),
+    mir_graph(nullptr),
+    cg(nullptr),
+    timings("QuickCompiler", true, false),
+    print_pass(false) {
+}
+
+CompilationUnit::~CompilationUnit() {
+  overridden_pass_options.clear();
+}
+
+void CompilationUnit::StartTimingSplit(const char* label) {
+  if (compiler_driver->GetDumpPasses()) {
+    timings.StartTiming(label);
+  }
+}
+
+void CompilationUnit::NewTimingSplit(const char* label) {
+  if (compiler_driver->GetDumpPasses()) {
+    timings.EndTiming();
+    timings.StartTiming(label);
+  }
+}
+
+void CompilationUnit::EndTiming() {
+  if (compiler_driver->GetDumpPasses()) {
+    timings.EndTiming();
+    if (enable_debug & (1 << kDebugTimings)) {
+      LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file);
+      LOG(INFO) << Dumpable<TimingLogger>(timings);
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 66fb608..37e3a7a 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -17,25 +17,28 @@
 #ifndef ART_COMPILER_DEX_COMPILER_IR_H_
 #define ART_COMPILER_DEX_COMPILER_IR_H_
 
+#include <string>
 #include <vector>
 
 #include "compiler_enums.h"
-#include "dex/quick/mir_to_lir.h"
-#include "dex_instruction.h"
 #include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "safe_map.h"
 #include "utils/scoped_arena_allocator.h"
 #include "base/timing_logger.h"
 #include "utils/arena_allocator.h"
 
 namespace art {
 
-struct ArenaMemBlock;
 class Backend;
-struct Memstats;
+class ClassLinker;
 class MIRGraph;
-class Mir2Lir;
+
+/*
+ * TODO: refactoring pass to move these (and other) typedefs towards usage style of runtime to
+ * add type safety (see runtime/offsets.h).
+ */
+typedef uint32_t DexOffset;          // Dex offset in code units.
+typedef uint16_t NarrowDexOffset;    // For use in structs, Dex offsets range from 0 .. 0xffff.
+typedef uint32_t CodeOffset;         // Native code offset in bytes.
 
 struct CompilationUnit {
   explicit CompilationUnit(ArenaPool* pool);
@@ -55,7 +58,6 @@
   jobject class_loader;                // compiling method's class loader.
   uint16_t class_def_idx;              // compiling method's defining class definition index.
   uint32_t method_idx;                 // compiling method's index into method_ids of DexFile.
-  const DexFile::CodeItem* code_item;  // compiling method's DexFile code_item.
   uint32_t access_flags;               // compiling method's access flags.
   InvokeType invoke_type;              // compiling method's invocation type.
   const char* shorty;                  // compiling method's shorty.
@@ -69,12 +71,6 @@
   InstructionSetFeatures GetInstructionSetFeatures() {
     return compiler_driver->GetInstructionSetFeatures();
   }
-  // TODO: much of this info available elsewhere.  Go to the original source?
-  uint16_t num_dalvik_registers;        // method->registers_size.
-  const uint16_t* insns;
-  uint16_t num_ins;
-  uint16_t num_outs;
-  uint16_t num_regs;            // Unlike num_dalvik_registers, does not include ins.
 
   // If non-empty, apply optimizer/debug flags only to matching methods.
   std::string compiler_method_match;
@@ -89,6 +85,15 @@
   std::unique_ptr<Backend> cg;           // Target-specific codegen.
   TimingLogger timings;
   bool print_pass;                 // Do we want to print a pass or not?
+
+  /**
+   * @brief Holds pass options for current pass being applied to compilation unit.
+   * @details This is updated for every pass to contain the overridden pass options
+   * that were specified by user. The pass itself will check this to see if the
+   * default settings have been changed. The key is simply the option string without
+   * the pass name.
+   */
+  SafeMap<const std::string, int> overridden_pass_options;
 };
 
 }  // namespace art
diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h
index d1abf7f..89f2b5c 100644
--- a/compiler/dex/dataflow_iterator-inl.h
+++ b/compiler/dex/dataflow_iterator-inl.h
@@ -28,7 +28,7 @@
   // Are we not yet at the end?
   if (idx_ < end_idx_) {
     // Get the next index.
-    BasicBlockId bb_id = block_id_list_->Get(idx_);
+    BasicBlockId bb_id = (*block_id_list_)[idx_];
     res = mir_graph_->GetBasicBlock(bb_id);
     idx_++;
   }
@@ -51,7 +51,7 @@
   // Are we not yet at the end?
   if (idx_ < end_idx_) {
     // Get the BasicBlockId.
-    BasicBlockId bb_id = block_id_list_->Get(idx_);
+    BasicBlockId bb_id = (*block_id_list_)[idx_];
     res = mir_graph_->GetBasicBlock(bb_id);
     idx_++;
   }
@@ -66,7 +66,7 @@
   // Are we not yet at the end?
   if (idx_ >= 0) {
     // Get the BasicBlockId.
-    BasicBlockId bb_id = block_id_list_->Get(idx_);
+    BasicBlockId bb_id = (*block_id_list_)[idx_];
     res = mir_graph_->GetBasicBlock(bb_id);
     idx_--;
   }
@@ -89,7 +89,7 @@
   // Are we not yet done?
   if (idx_ >= 0) {
     // Get the BasicBlockId.
-    BasicBlockId bb_id = block_id_list_->Get(idx_);
+    BasicBlockId bb_id = (*block_id_list_)[idx_];
     res = mir_graph_->GetBasicBlock(bb_id);
     idx_--;
   }
@@ -97,34 +97,28 @@
   return res;
 }
 
-// AllNodes uses the existing GrowableArray iterator, and should be considered unordered.
+// AllNodes uses the existing block list, and should be considered unordered.
 inline BasicBlock* AllNodesIterator::Next(bool had_change) {
-  BasicBlock* res = NULL;
-
-  // Suppose we want to keep looking.
-  bool keep_looking = true;
-
-  // Find the next BasicBlock.
-  while (keep_looking == true) {
-    // Get next BasicBlock.
-    res = all_nodes_iterator_.Next();
-
-    // Are we done or is the BasicBlock not hidden?
-    if ((res == NULL) || (res->hidden == false)) {
-      keep_looking = false;
-    }
-  }
-
   // Update changed: if had_changed is true, we remember it for the whole iteration.
   changed_ |= had_change;
 
+  BasicBlock* res = nullptr;
+  while (idx_ != end_idx_) {
+    BasicBlock* bb = mir_graph_->GetBlockList()[idx_++];
+    DCHECK(bb != nullptr);
+    if (!bb->hidden) {
+      res = bb;
+      break;
+    }
+  }
+
   return res;
 }
 
 inline BasicBlock* LoopRepeatingTopologicalSortIterator::Next(bool had_change) {
   if (idx_ != 0) {
     // Mark last processed block visited.
-    BasicBlock* bb = mir_graph_->GetBasicBlock(block_id_list_->Get(idx_ - 1));
+    BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx_ - 1]);
     bb->visited = true;
     if (had_change) {
       // If we had a change we need to revisit the children.
@@ -138,16 +132,17 @@
   while (true) {
     // Pop loops we have left and check if we need to recalculate one of them.
     // NOTE: We need to do this even if idx_ == end_idx_.
-    while (loop_head_stack_->Size() != 0u &&
-        loop_ends_->Get(loop_head_stack_->Peek().first) == idx_) {
-      auto top = loop_head_stack_->Peek();
+    while (loop_head_stack_->size() != 0u &&
+        (*loop_ends_)[loop_head_stack_->back().first] == idx_) {
+      auto top = loop_head_stack_->back();
       uint16_t loop_head_idx = top.first;
       bool recalculated = top.second;
-      loop_head_stack_->Pop();
-      BasicBlock* loop_head = mir_graph_->GetBasicBlock(block_id_list_->Get(loop_head_idx));
+      loop_head_stack_->pop_back();
+      BasicBlock* loop_head = mir_graph_->GetBasicBlock((*block_id_list_)[loop_head_idx]);
       DCHECK(loop_head != nullptr);
       if (!recalculated || !loop_head->visited) {
-        loop_head_stack_->Insert(std::make_pair(loop_head_idx, true));  // Recalculating this loop.
+        // Recalculating this loop.
+        loop_head_stack_->push_back(std::make_pair(loop_head_idx, true));
         idx_ = loop_head_idx + 1;
         return loop_head;
       }
@@ -160,11 +155,11 @@
     // Get next block and return it if unvisited.
     BasicBlockId idx = idx_;
     idx_ += 1;
-    BasicBlock* bb = mir_graph_->GetBasicBlock(block_id_list_->Get(idx));
+    BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]);
     DCHECK(bb != nullptr);
     if (!bb->visited) {
-      if (loop_ends_->Get(idx) != 0u) {
-        loop_head_stack_->Insert(std::make_pair(idx, false));  // Not recalculating.
+      if ((*loop_ends_)[idx] != 0u) {
+        loop_head_stack_->push_back(std::make_pair(idx, false));  // Not recalculating.
       }
       return bb;
     }
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
index 06d6832..188f1d9 100644
--- a/compiler/dex/dataflow_iterator.h
+++ b/compiler/dex/dataflow_iterator.h
@@ -104,7 +104,7 @@
       MIRGraph* const mir_graph_;                       /**< @brief the MIRGraph */
       const int32_t start_idx_;                         /**< @brief the start index for the iteration */
       const int32_t end_idx_;                           /**< @brief the last index for the iteration */
-      GrowableArray<BasicBlockId>* block_id_list_;      /**< @brief the list of BasicBlocks we want to iterate on */
+      const ArenaVector<BasicBlockId>* block_id_list_;  /**< @brief the list of BasicBlocks we want to iterate on */
       int32_t idx_;                                     /**< @brief Current index for the iterator */
       int32_t repeats_;                                 /**< @brief Number of repeats over the iteration */
       bool changed_;                                    /**< @brief Has something changed during the current iteration? */
@@ -124,7 +124,7 @@
           : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
         // Extra setup for the PreOrderDfsIterator.
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetDfsOrder();
+        block_id_list_ = &mir_graph->GetDfsOrder();
       }
 
       /**
@@ -155,7 +155,7 @@
           : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
         // Extra setup for the RepeatingPreOrderDfsIterator.
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetDfsOrder();
+        block_id_list_ = &mir_graph->GetDfsOrder();
       }
 
       /**
@@ -186,7 +186,7 @@
           : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
         // Extra setup for the RepeatingPostOrderDfsIterator.
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetDfsPostOrder();
+        block_id_list_ = &mir_graph->GetDfsPostOrder();
       }
 
       /**
@@ -216,7 +216,7 @@
           : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
         // Extra setup for the ReversePostOrderDfsIterator.
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetDfsPostOrder();
+        block_id_list_ = &mir_graph->GetDfsPostOrder();
       }
 
       /**
@@ -247,7 +247,7 @@
           : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
         // Extra setup for the RepeatingReversePostOrderDfsIterator
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetDfsPostOrder();
+        block_id_list_ = &mir_graph->GetDfsPostOrder();
       }
 
       /**
@@ -277,7 +277,7 @@
           : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
         // Extra setup for thePostOrderDOMIterator.
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetDomPostOrder();
+        block_id_list_ = &mir_graph->GetDomPostOrder();
       }
 
       /**
@@ -304,15 +304,14 @@
        * @param mir_graph The MIRGraph considered.
        */
       explicit AllNodesIterator(MIRGraph* mir_graph)
-          : DataflowIterator(mir_graph, 0, 0),
-            all_nodes_iterator_(mir_graph->GetBlockList()) {
+          : DataflowIterator(mir_graph, 0, mir_graph->GetBlockList().size()) {
       }
 
       /**
        * @brief Resetting the iterator.
        */
       void Reset() {
-        all_nodes_iterator_.Reset();
+        idx_ = 0;
       }
 
       /**
@@ -321,9 +320,6 @@
        * @return the next BasicBlock following the iteration order, 0 if finished.
        */
       virtual BasicBlock* Next(bool had_change = false) ALWAYS_INLINE;
-
-    private:
-      GrowableArray<BasicBlock*>::Iterator all_nodes_iterator_;    /**< @brief The list of all the nodes */
   };
 
   /**
@@ -337,10 +333,10 @@
        * @param mir_graph The MIRGraph considered.
        */
       explicit TopologicalSortIterator(MIRGraph* mir_graph)
-          : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) {
+          : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()) {
         // Extra setup for TopologicalSortIterator.
         idx_ = start_idx_;
-        block_id_list_ = mir_graph->GetTopologicalSortOrder();
+        block_id_list_ = &mir_graph->GetTopologicalSortOrder();
       }
 
       /**
@@ -369,10 +365,10 @@
       * @param mir_graph The MIRGraph considered.
       */
      explicit RepeatingTopologicalSortIterator(MIRGraph* mir_graph)
-         : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) {
+         : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()) {
        // Extra setup for RepeatingTopologicalSortIterator.
        idx_ = start_idx_;
-       block_id_list_ = mir_graph->GetTopologicalSortOrder();
+       block_id_list_ = &mir_graph->GetTopologicalSortOrder();
      }
 
      /**
@@ -408,19 +404,19 @@
       * @param mir_graph The MIRGraph considered.
       */
      explicit LoopRepeatingTopologicalSortIterator(MIRGraph* mir_graph)
-         : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()),
-           loop_ends_(mir_graph->GetTopologicalSortOrderLoopEnds()),
+         : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()),
+           loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()),
            loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) {
        // Extra setup for RepeatingTopologicalSortIterator.
        idx_ = start_idx_;
-       block_id_list_ = mir_graph->GetTopologicalSortOrder();
+       block_id_list_ = &mir_graph->GetTopologicalSortOrder();
        // Clear visited flags and check that the loop head stack is empty.
        mir_graph->ClearAllVisitedFlags();
-       DCHECK_EQ(loop_head_stack_->Size(), 0u);
+       DCHECK_EQ(loop_head_stack_->size(), 0u);
      }
 
      ~LoopRepeatingTopologicalSortIterator() {
-       DCHECK_EQ(loop_head_stack_->Size(), 0u);
+       DCHECK_EQ(loop_head_stack_->size(), 0u);
      }
 
      /**
@@ -431,8 +427,8 @@
      virtual BasicBlock* Next(bool had_change = false) OVERRIDE;
 
     private:
-     const GrowableArray<BasicBlockId>* const loop_ends_;
-     GrowableArray<std::pair<uint16_t, bool>>* const loop_head_stack_;
+     const ArenaVector<BasicBlockId>* const loop_ends_;
+     ArenaVector<std::pair<uint16_t, bool>>* const loop_head_stack_;
   };
 
 }  // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index b9f9437..f9a05c2 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -121,15 +121,25 @@
         break;
 
       case Instruction::IPUT:
-      case Instruction::IPUT_BOOLEAN:
-      case Instruction::IPUT_BYTE:
-      case Instruction::IPUT_CHAR:
-      case Instruction::IPUT_SHORT:
-        // These opcodes have the same implementation in interpreter so group
-        // them under IPUT_QUICK.
         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
         break;
 
+      case Instruction::IPUT_BOOLEAN:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN_QUICK, true);
+        break;
+
+      case Instruction::IPUT_BYTE:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE_QUICK, true);
+        break;
+
+      case Instruction::IPUT_CHAR:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR_QUICK, true);
+        break;
+
+      case Instruction::IPUT_SHORT:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT_QUICK, true);
+        break;
+
       case Instruction::IPUT_WIDE:
         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
         break;
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 45fc19e..07f3033 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
+#include "frontend.h"
+
 #include <cstdint>
 
+#include "backend.h"
 #include "compiler.h"
 #include "compiler_internals.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
-#include "dataflow_iterator-inl.h"
-#include "leb128.h"
 #include "mirror/object.h"
 #include "pass_driver_me_opts.h"
 #include "runtime.h"
@@ -32,22 +33,14 @@
 
 namespace art {
 
-extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver* driver) {
-  CHECK(driver->GetCompilerContext() == nullptr);
-}
-
-extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver* driver) {
-  CHECK(driver->GetCompilerContext() == nullptr);
-}
-
 /* Default optimizer/debug setting for the compiler. */
 static uint32_t kCompilerOptimizerDisableFlags = 0 |  // Disable specific optimizations
-  (1 << kLoadStoreElimination) |
+  // (1 << kLoadStoreElimination) |
   // (1 << kLoadHoisting) |
   // (1 << kSuppressLoads) |
   // (1 << kNullCheckElimination) |
   // (1 << kClassInitCheckElimination) |
-  (1 << kGlobalValueNumbering) |
+  // (1 << kGlobalValueNumbering) |
   // (1 << kPromoteRegs) |
   // (1 << kTrackLiveTemps) |
   // (1 << kSafeOptimizations) |
@@ -81,542 +74,8 @@
   // (1 << kDebugCodegenDump) |
   0;
 
-COMPILE_ASSERT(0U == static_cast<size_t>(kNone), kNone_not_0);
-COMPILE_ASSERT(1U == static_cast<size_t>(kArm), kArm_not_1);
-COMPILE_ASSERT(2U == static_cast<size_t>(kArm64), kArm64_not_2);
-COMPILE_ASSERT(3U == static_cast<size_t>(kThumb2), kThumb2_not_3);
-COMPILE_ASSERT(4U == static_cast<size_t>(kX86), kX86_not_4);
-COMPILE_ASSERT(5U == static_cast<size_t>(kX86_64), kX86_64_not_5);
-COMPILE_ASSERT(6U == static_cast<size_t>(kMips), kMips_not_6);
-COMPILE_ASSERT(7U == static_cast<size_t>(kMips64), kMips64_not_7);
-
-// Additional disabled optimizations (over generally disabled) per instruction set.
-static constexpr uint32_t kDisabledOptimizationsPerISA[] = {
-    // 0 = kNone.
-    ~0U,
-    // 1 = kArm, unused (will use kThumb2).
-    ~0U,
-    // 2 = kArm64.
-    0,
-    // 3 = kThumb2.
-    0,
-    // 4 = kX86.
-    (1 << kLoadStoreElimination) |
-    0,
-    // 5 = kX86_64.
-    (1 << kLoadStoreElimination) |
-    0,
-    // 6 = kMips.
-    (1 << kLoadStoreElimination) |
-    (1 << kLoadHoisting) |
-    (1 << kSuppressLoads) |
-    (1 << kNullCheckElimination) |
-    (1 << kPromoteRegs) |
-    (1 << kTrackLiveTemps) |
-    (1 << kSafeOptimizations) |
-    (1 << kBBOpt) |
-    (1 << kMatch) |
-    (1 << kPromoteCompilerTemps) |
-    0,
-    // 7 = kMips64.
-    ~0U
-};
-COMPILE_ASSERT(sizeof(kDisabledOptimizationsPerISA) == 8 * sizeof(uint32_t), kDisabledOpts_unexp);
-
-// Supported shorty types per instruction set. nullptr means that all are available.
-// Z : boolean
-// B : byte
-// S : short
-// C : char
-// I : int
-// J : long
-// F : float
-// D : double
-// L : reference(object, array)
-// V : void
-static const char* kSupportedTypes[] = {
-    // 0 = kNone.
-    "",
-    // 1 = kArm, unused (will use kThumb2).
-    "",
-    // 2 = kArm64.
-    nullptr,
-    // 3 = kThumb2.
-    nullptr,
-    // 4 = kX86.
-    nullptr,
-    // 5 = kX86_64.
-    nullptr,
-    // 6 = kMips.
-    nullptr,
-    // 7 = kMips64.
-    ""
-};
-COMPILE_ASSERT(sizeof(kSupportedTypes) == 8 * sizeof(char*), kSupportedTypes_unexp);
-
-static int kAllOpcodes[] = {
-    Instruction::NOP,
-    Instruction::MOVE,
-    Instruction::MOVE_FROM16,
-    Instruction::MOVE_16,
-    Instruction::MOVE_WIDE,
-    Instruction::MOVE_WIDE_FROM16,
-    Instruction::MOVE_WIDE_16,
-    Instruction::MOVE_OBJECT,
-    Instruction::MOVE_OBJECT_FROM16,
-    Instruction::MOVE_OBJECT_16,
-    Instruction::MOVE_RESULT,
-    Instruction::MOVE_RESULT_WIDE,
-    Instruction::MOVE_RESULT_OBJECT,
-    Instruction::MOVE_EXCEPTION,
-    Instruction::RETURN_VOID,
-    Instruction::RETURN,
-    Instruction::RETURN_WIDE,
-    Instruction::RETURN_OBJECT,
-    Instruction::CONST_4,
-    Instruction::CONST_16,
-    Instruction::CONST,
-    Instruction::CONST_HIGH16,
-    Instruction::CONST_WIDE_16,
-    Instruction::CONST_WIDE_32,
-    Instruction::CONST_WIDE,
-    Instruction::CONST_WIDE_HIGH16,
-    Instruction::CONST_STRING,
-    Instruction::CONST_STRING_JUMBO,
-    Instruction::CONST_CLASS,
-    Instruction::MONITOR_ENTER,
-    Instruction::MONITOR_EXIT,
-    Instruction::CHECK_CAST,
-    Instruction::INSTANCE_OF,
-    Instruction::ARRAY_LENGTH,
-    Instruction::NEW_INSTANCE,
-    Instruction::NEW_ARRAY,
-    Instruction::FILLED_NEW_ARRAY,
-    Instruction::FILLED_NEW_ARRAY_RANGE,
-    Instruction::FILL_ARRAY_DATA,
-    Instruction::THROW,
-    Instruction::GOTO,
-    Instruction::GOTO_16,
-    Instruction::GOTO_32,
-    Instruction::PACKED_SWITCH,
-    Instruction::SPARSE_SWITCH,
-    Instruction::CMPL_FLOAT,
-    Instruction::CMPG_FLOAT,
-    Instruction::CMPL_DOUBLE,
-    Instruction::CMPG_DOUBLE,
-    Instruction::CMP_LONG,
-    Instruction::IF_EQ,
-    Instruction::IF_NE,
-    Instruction::IF_LT,
-    Instruction::IF_GE,
-    Instruction::IF_GT,
-    Instruction::IF_LE,
-    Instruction::IF_EQZ,
-    Instruction::IF_NEZ,
-    Instruction::IF_LTZ,
-    Instruction::IF_GEZ,
-    Instruction::IF_GTZ,
-    Instruction::IF_LEZ,
-    Instruction::UNUSED_3E,
-    Instruction::UNUSED_3F,
-    Instruction::UNUSED_40,
-    Instruction::UNUSED_41,
-    Instruction::UNUSED_42,
-    Instruction::UNUSED_43,
-    Instruction::AGET,
-    Instruction::AGET_WIDE,
-    Instruction::AGET_OBJECT,
-    Instruction::AGET_BOOLEAN,
-    Instruction::AGET_BYTE,
-    Instruction::AGET_CHAR,
-    Instruction::AGET_SHORT,
-    Instruction::APUT,
-    Instruction::APUT_WIDE,
-    Instruction::APUT_OBJECT,
-    Instruction::APUT_BOOLEAN,
-    Instruction::APUT_BYTE,
-    Instruction::APUT_CHAR,
-    Instruction::APUT_SHORT,
-    Instruction::IGET,
-    Instruction::IGET_WIDE,
-    Instruction::IGET_OBJECT,
-    Instruction::IGET_BOOLEAN,
-    Instruction::IGET_BYTE,
-    Instruction::IGET_CHAR,
-    Instruction::IGET_SHORT,
-    Instruction::IPUT,
-    Instruction::IPUT_WIDE,
-    Instruction::IPUT_OBJECT,
-    Instruction::IPUT_BOOLEAN,
-    Instruction::IPUT_BYTE,
-    Instruction::IPUT_CHAR,
-    Instruction::IPUT_SHORT,
-    Instruction::SGET,
-    Instruction::SGET_WIDE,
-    Instruction::SGET_OBJECT,
-    Instruction::SGET_BOOLEAN,
-    Instruction::SGET_BYTE,
-    Instruction::SGET_CHAR,
-    Instruction::SGET_SHORT,
-    Instruction::SPUT,
-    Instruction::SPUT_WIDE,
-    Instruction::SPUT_OBJECT,
-    Instruction::SPUT_BOOLEAN,
-    Instruction::SPUT_BYTE,
-    Instruction::SPUT_CHAR,
-    Instruction::SPUT_SHORT,
-    Instruction::INVOKE_VIRTUAL,
-    Instruction::INVOKE_SUPER,
-    Instruction::INVOKE_DIRECT,
-    Instruction::INVOKE_STATIC,
-    Instruction::INVOKE_INTERFACE,
-    Instruction::RETURN_VOID_BARRIER,
-    Instruction::INVOKE_VIRTUAL_RANGE,
-    Instruction::INVOKE_SUPER_RANGE,
-    Instruction::INVOKE_DIRECT_RANGE,
-    Instruction::INVOKE_STATIC_RANGE,
-    Instruction::INVOKE_INTERFACE_RANGE,
-    Instruction::UNUSED_79,
-    Instruction::UNUSED_7A,
-    Instruction::NEG_INT,
-    Instruction::NOT_INT,
-    Instruction::NEG_LONG,
-    Instruction::NOT_LONG,
-    Instruction::NEG_FLOAT,
-    Instruction::NEG_DOUBLE,
-    Instruction::INT_TO_LONG,
-    Instruction::INT_TO_FLOAT,
-    Instruction::INT_TO_DOUBLE,
-    Instruction::LONG_TO_INT,
-    Instruction::LONG_TO_FLOAT,
-    Instruction::LONG_TO_DOUBLE,
-    Instruction::FLOAT_TO_INT,
-    Instruction::FLOAT_TO_LONG,
-    Instruction::FLOAT_TO_DOUBLE,
-    Instruction::DOUBLE_TO_INT,
-    Instruction::DOUBLE_TO_LONG,
-    Instruction::DOUBLE_TO_FLOAT,
-    Instruction::INT_TO_BYTE,
-    Instruction::INT_TO_CHAR,
-    Instruction::INT_TO_SHORT,
-    Instruction::ADD_INT,
-    Instruction::SUB_INT,
-    Instruction::MUL_INT,
-    Instruction::DIV_INT,
-    Instruction::REM_INT,
-    Instruction::AND_INT,
-    Instruction::OR_INT,
-    Instruction::XOR_INT,
-    Instruction::SHL_INT,
-    Instruction::SHR_INT,
-    Instruction::USHR_INT,
-    Instruction::ADD_LONG,
-    Instruction::SUB_LONG,
-    Instruction::MUL_LONG,
-    Instruction::DIV_LONG,
-    Instruction::REM_LONG,
-    Instruction::AND_LONG,
-    Instruction::OR_LONG,
-    Instruction::XOR_LONG,
-    Instruction::SHL_LONG,
-    Instruction::SHR_LONG,
-    Instruction::USHR_LONG,
-    Instruction::ADD_FLOAT,
-    Instruction::SUB_FLOAT,
-    Instruction::MUL_FLOAT,
-    Instruction::DIV_FLOAT,
-    Instruction::REM_FLOAT,
-    Instruction::ADD_DOUBLE,
-    Instruction::SUB_DOUBLE,
-    Instruction::MUL_DOUBLE,
-    Instruction::DIV_DOUBLE,
-    Instruction::REM_DOUBLE,
-    Instruction::ADD_INT_2ADDR,
-    Instruction::SUB_INT_2ADDR,
-    Instruction::MUL_INT_2ADDR,
-    Instruction::DIV_INT_2ADDR,
-    Instruction::REM_INT_2ADDR,
-    Instruction::AND_INT_2ADDR,
-    Instruction::OR_INT_2ADDR,
-    Instruction::XOR_INT_2ADDR,
-    Instruction::SHL_INT_2ADDR,
-    Instruction::SHR_INT_2ADDR,
-    Instruction::USHR_INT_2ADDR,
-    Instruction::ADD_LONG_2ADDR,
-    Instruction::SUB_LONG_2ADDR,
-    Instruction::MUL_LONG_2ADDR,
-    Instruction::DIV_LONG_2ADDR,
-    Instruction::REM_LONG_2ADDR,
-    Instruction::AND_LONG_2ADDR,
-    Instruction::OR_LONG_2ADDR,
-    Instruction::XOR_LONG_2ADDR,
-    Instruction::SHL_LONG_2ADDR,
-    Instruction::SHR_LONG_2ADDR,
-    Instruction::USHR_LONG_2ADDR,
-    Instruction::ADD_FLOAT_2ADDR,
-    Instruction::SUB_FLOAT_2ADDR,
-    Instruction::MUL_FLOAT_2ADDR,
-    Instruction::DIV_FLOAT_2ADDR,
-    Instruction::REM_FLOAT_2ADDR,
-    Instruction::ADD_DOUBLE_2ADDR,
-    Instruction::SUB_DOUBLE_2ADDR,
-    Instruction::MUL_DOUBLE_2ADDR,
-    Instruction::DIV_DOUBLE_2ADDR,
-    Instruction::REM_DOUBLE_2ADDR,
-    Instruction::ADD_INT_LIT16,
-    Instruction::RSUB_INT,
-    Instruction::MUL_INT_LIT16,
-    Instruction::DIV_INT_LIT16,
-    Instruction::REM_INT_LIT16,
-    Instruction::AND_INT_LIT16,
-    Instruction::OR_INT_LIT16,
-    Instruction::XOR_INT_LIT16,
-    Instruction::ADD_INT_LIT8,
-    Instruction::RSUB_INT_LIT8,
-    Instruction::MUL_INT_LIT8,
-    Instruction::DIV_INT_LIT8,
-    Instruction::REM_INT_LIT8,
-    Instruction::AND_INT_LIT8,
-    Instruction::OR_INT_LIT8,
-    Instruction::XOR_INT_LIT8,
-    Instruction::SHL_INT_LIT8,
-    Instruction::SHR_INT_LIT8,
-    Instruction::USHR_INT_LIT8,
-    Instruction::IGET_QUICK,
-    Instruction::IGET_WIDE_QUICK,
-    Instruction::IGET_OBJECT_QUICK,
-    Instruction::IPUT_QUICK,
-    Instruction::IPUT_WIDE_QUICK,
-    Instruction::IPUT_OBJECT_QUICK,
-    Instruction::INVOKE_VIRTUAL_QUICK,
-    Instruction::INVOKE_VIRTUAL_RANGE_QUICK,
-    Instruction::UNUSED_EB,
-    Instruction::UNUSED_EC,
-    Instruction::UNUSED_ED,
-    Instruction::UNUSED_EE,
-    Instruction::UNUSED_EF,
-    Instruction::UNUSED_F0,
-    Instruction::UNUSED_F1,
-    Instruction::UNUSED_F2,
-    Instruction::UNUSED_F3,
-    Instruction::UNUSED_F4,
-    Instruction::UNUSED_F5,
-    Instruction::UNUSED_F6,
-    Instruction::UNUSED_F7,
-    Instruction::UNUSED_F8,
-    Instruction::UNUSED_F9,
-    Instruction::UNUSED_FA,
-    Instruction::UNUSED_FB,
-    Instruction::UNUSED_FC,
-    Instruction::UNUSED_FD,
-    Instruction::UNUSED_FE,
-    Instruction::UNUSED_FF,
-    // ----- ExtendedMIROpcode -----
-    kMirOpPhi,
-    kMirOpCopy,
-    kMirOpFusedCmplFloat,
-    kMirOpFusedCmpgFloat,
-    kMirOpFusedCmplDouble,
-    kMirOpFusedCmpgDouble,
-    kMirOpFusedCmpLong,
-    kMirOpNop,
-    kMirOpNullCheck,
-    kMirOpRangeCheck,
-    kMirOpDivZeroCheck,
-    kMirOpCheck,
-    kMirOpCheckPart2,
-    kMirOpSelect,
-};
-
-// Unsupported opcodes. nullptr can be used when everything is supported. Size of the lists is
-// recorded below.
-static const int* kUnsupportedOpcodes[] = {
-    // 0 = kNone.
-    kAllOpcodes,
-    // 1 = kArm, unused (will use kThumb2).
-    kAllOpcodes,
-    // 2 = kArm64.
-    nullptr,
-    // 3 = kThumb2.
-    nullptr,
-    // 4 = kX86.
-    nullptr,
-    // 5 = kX86_64.
-    nullptr,
-    // 6 = kMips.
-    nullptr,
-    // 7 = kMips64.
-    kAllOpcodes
-};
-COMPILE_ASSERT(sizeof(kUnsupportedOpcodes) == 8 * sizeof(int*), kUnsupportedOpcodes_unexp);
-
-// Size of the arrays stored above.
-static const size_t kUnsupportedOpcodesSize[] = {
-    // 0 = kNone.
-    arraysize(kAllOpcodes),
-    // 1 = kArm, unused (will use kThumb2).
-    arraysize(kAllOpcodes),
-    // 2 = kArm64.
-    0,
-    // 3 = kThumb2.
-    0,
-    // 4 = kX86.
-    0,
-    // 5 = kX86_64.
-    0,
-    // 6 = kMips.
-    0,
-    // 7 = kMips64.
-    arraysize(kAllOpcodes),
-};
-COMPILE_ASSERT(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t),
-               kUnsupportedOpcodesSize_unexp);
-
-// The maximum amount of Dalvik register in a method for which we will start compiling. Tries to
-// avoid an abort when we need to manage more SSA registers than we can.
-static constexpr size_t kMaxAllowedDalvikRegisters = INT16_MAX / 2;
-
-CompilationUnit::CompilationUnit(ArenaPool* pool)
-  : compiler_driver(nullptr),
-    class_linker(nullptr),
-    dex_file(nullptr),
-    class_loader(nullptr),
-    class_def_idx(0),
-    method_idx(0),
-    code_item(nullptr),
-    access_flags(0),
-    invoke_type(kDirect),
-    shorty(nullptr),
-    disable_opt(0),
-    enable_debug(0),
-    verbose(false),
-    compiler(nullptr),
-    instruction_set(kNone),
-    target64(false),
-    num_dalvik_registers(0),
-    insns(nullptr),
-    num_ins(0),
-    num_outs(0),
-    num_regs(0),
-    compiler_flip_match(false),
-    arena(pool),
-    arena_stack(pool),
-    mir_graph(nullptr),
-    cg(nullptr),
-    timings("QuickCompiler", true, false),
-    print_pass(false) {
-}
-
-CompilationUnit::~CompilationUnit() {
-}
-
-void CompilationUnit::StartTimingSplit(const char* label) {
-  if (compiler_driver->GetDumpPasses()) {
-    timings.StartTiming(label);
-  }
-}
-
-void CompilationUnit::NewTimingSplit(const char* label) {
-  if (compiler_driver->GetDumpPasses()) {
-    timings.EndTiming();
-    timings.StartTiming(label);
-  }
-}
-
-void CompilationUnit::EndTiming() {
-  if (compiler_driver->GetDumpPasses()) {
-    timings.EndTiming();
-    if (enable_debug & (1 << kDebugTimings)) {
-      LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file);
-      LOG(INFO) << Dumpable<TimingLogger>(timings);
-    }
-  }
-}
-
-static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) {
-  const char* supported_types = kSupportedTypes[instruction_set];
-  if (supported_types == nullptr) {
-    // Everything available.
-    return true;
-  }
-
-  uint32_t shorty_size = strlen(shorty);
-  CHECK_GE(shorty_size, 1u);
-
-  for (uint32_t i = 0; i < shorty_size; i++) {
-    if (strchr(supported_types, shorty[i]) == nullptr) {
-      return false;
-    }
-  }
-  return true;
-};
-
-// Skip the method that we do not support currently.
-static bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
-                             CompilationUnit& cu) {
-  // This is a limitation in mir_graph. See MirGraph::SetNumSSARegs.
-  if (cu.num_dalvik_registers > kMaxAllowedDalvikRegisters) {
-    VLOG(compiler) << "Too many dalvik registers : " << cu.num_dalvik_registers;
-    return false;
-  }
-
-  // Check whether we do have limitations at all.
-  if (kSupportedTypes[cu.instruction_set] == nullptr &&
-      kUnsupportedOpcodesSize[cu.instruction_set] == 0U) {
-    return true;
-  }
-
-  // Check if we can compile the prototype.
-  const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
-  if (!CanCompileShorty(shorty, cu.instruction_set)) {
-    VLOG(compiler) << "Unsupported shorty : " << shorty;
-    return false;
-  }
-
-  const int *unsupport_list = kUnsupportedOpcodes[cu.instruction_set];
-  int unsupport_list_size = kUnsupportedOpcodesSize[cu.instruction_set];
-
-  for (unsigned int idx = 0; idx < cu.mir_graph->GetNumBlocks(); idx++) {
-    BasicBlock* bb = cu.mir_graph->GetBasicBlock(idx);
-    if (bb == NULL) continue;
-    if (bb->block_type == kDead) continue;
-    for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
-      int opcode = mir->dalvikInsn.opcode;
-      // Check if we support the byte code.
-      if (std::find(unsupport_list, unsupport_list + unsupport_list_size,
-                    opcode) != unsupport_list + unsupport_list_size) {
-        if (!MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
-          VLOG(compiler) << "Unsupported dalvik byte code : "
-              << mir->dalvikInsn.opcode;
-        } else {
-          VLOG(compiler) << "Unsupported extended MIR opcode : "
-              << MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst];
-        }
-        return false;
-      }
-      // Check if it invokes a prototype that we cannot support.
-      if (Instruction::INVOKE_VIRTUAL == opcode ||
-          Instruction::INVOKE_SUPER == opcode ||
-          Instruction::INVOKE_DIRECT == opcode ||
-          Instruction::INVOKE_STATIC == opcode ||
-          Instruction::INVOKE_INTERFACE == opcode) {
-        uint32_t invoke_method_idx = mir->dalvikInsn.vB;
-        const char* invoke_method_shorty = dex_file.GetMethodShorty(
-            dex_file.GetMethodId(invoke_method_idx));
-        if (!CanCompileShorty(invoke_method_shorty, cu.instruction_set)) {
-          VLOG(compiler) << "Unsupported to invoke '"
-              << PrettyMethod(invoke_method_idx, dex_file)
-              << "' with shorty : " << invoke_method_shorty;
-          return false;
-        }
-      }
-    }
-  }
-  return true;
-}
-
 static CompiledMethod* CompileMethod(CompilerDriver& driver,
-                                     Compiler* compiler,
+                                     const Compiler* compiler,
                                      const DexFile::CodeItem* code_item,
                                      uint32_t access_flags, InvokeType invoke_type,
                                      uint16_t class_def_idx, uint32_t method_idx,
@@ -662,8 +121,6 @@
         (cu.instruction_set == kX86_64) ||
         (cu.instruction_set == kMips));
 
-  /* Adjust this value accordingly once inlining is performed */
-  cu.num_dalvik_registers = code_item->registers_size_;
   // TODO: set this from command line
   cu.compiler_flip_match = false;
   bool use_match = !cu.compiler_method_match.empty();
@@ -698,9 +155,6 @@
 
   compiler->InitCompilationUnit(cu);
 
-  // Disable optimizations according to instruction set.
-  cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set];
-
   cu.StartTimingSplit("BuildMIRGraph");
   cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
 
@@ -720,7 +174,7 @@
   cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
                               class_loader, dex_file);
 
-  if (!CanCompileMethod(method_idx, dex_file, cu)) {
+  if (!compiler->CanCompileMethod(method_idx, dex_file, &cu)) {
     VLOG(compiler)  << cu.instruction_set << ": Cannot compile method : "
         << PrettyMethod(method_idx, dex_file);
     return nullptr;
@@ -802,8 +256,8 @@
   return result;
 }
 
-CompiledMethod* CompileOneMethod(CompilerDriver& driver,
-                                 Compiler* compiler,
+CompiledMethod* CompileOneMethod(CompilerDriver* driver,
+                                 const Compiler* compiler,
                                  const DexFile::CodeItem* code_item,
                                  uint32_t access_flags,
                                  InvokeType invoke_type,
@@ -812,22 +266,8 @@
                                  jobject class_loader,
                                  const DexFile& dex_file,
                                  void* compilation_unit) {
-  return CompileMethod(driver, compiler, code_item, access_flags, invoke_type, class_def_idx,
+  return CompileMethod(*driver, compiler, code_item, access_flags, invoke_type, class_def_idx,
                        method_idx, class_loader, dex_file, compilation_unit);
 }
 
 }  // namespace art
-
-extern "C" art::CompiledMethod*
-    ArtQuickCompileMethod(art::CompilerDriver& driver,
-                          const art::DexFile::CodeItem* code_item,
-                          uint32_t access_flags, art::InvokeType invoke_type,
-                          uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
-                          const art::DexFile& dex_file) {
-  // TODO: check method fingerprint here to determine appropriate backend type.  Until then, use
-  // build default.
-  art::Compiler* compiler = driver.GetCompiler();
-  return art::CompileOneMethod(driver, compiler, code_item, access_flags, invoke_type,
-                               class_def_idx, method_idx, class_loader, dex_file,
-                               NULL /* use thread llvm_info */);
-}
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
index f4cbdfb..51b6d68 100644
--- a/compiler/dex/frontend.h
+++ b/compiler/dex/frontend.h
@@ -20,16 +20,11 @@
 #include "dex_file.h"
 #include "invoke_type.h"
 
-namespace llvm {
-  class Module;
-  class LLVMContext;
-}
-
 namespace art {
-namespace llvm {
-  class IntrinsicHelper;
-  class IRBuilder;
-}
+
+class CompiledMethod;
+class Compiler;
+class CompilerDriver;
 
 /*
  * Assembly is an iterative process, and usually terminates within
@@ -81,48 +76,17 @@
   kDebugCodegenDump
 };
 
-class LLVMInfo {
-  public:
-    LLVMInfo();
-    ~LLVMInfo();
-
-    ::llvm::LLVMContext* GetLLVMContext() {
-      return llvm_context_.get();
-    }
-
-    ::llvm::Module* GetLLVMModule() {
-      return llvm_module_;
-    }
-
-    art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
-      return intrinsic_helper_.get();
-    }
-
-    art::llvm::IRBuilder* GetIRBuilder() {
-      return ir_builder_.get();
-    }
-
-  private:
-    std::unique_ptr< ::llvm::LLVMContext> llvm_context_;
-    ::llvm::Module* llvm_module_;  // Managed by context_.
-    std::unique_ptr<art::llvm::IntrinsicHelper> intrinsic_helper_;
-    std::unique_ptr<art::llvm::IRBuilder> ir_builder_;
-};
-
-class CompiledMethod;
-class CompilerDriver;
+CompiledMethod* CompileOneMethod(CompilerDriver* driver,
+                                 const Compiler* compiler,
+                                 const DexFile::CodeItem* code_item,
+                                 uint32_t access_flags,
+                                 InvokeType invoke_type,
+                                 uint16_t class_def_idx,
+                                 uint32_t method_idx,
+                                 jobject class_loader,
+                                 const DexFile& dex_file,
+                                 void* compilation_unit);
 
 }  // namespace art
 
-extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver,
-                                                 const art::DexFile::CodeItem* code_item,
-                                                 uint32_t access_flags,
-                                                 art::InvokeType invoke_type,
-                                                 uint16_t class_def_idx,
-                                                 uint32_t method_idx,
-                                                 jobject class_loader,
-                                                 const art::DexFile& dex_file);
-
-
-
 #endif  // ART_COMPILER_DEX_FRONTEND_H_
diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc
index d7ef6f0..af57529 100644
--- a/compiler/dex/global_value_numbering.cc
+++ b/compiler/dex/global_value_numbering.cc
@@ -56,8 +56,11 @@
     return nullptr;
   }
   if (UNLIKELY(bbs_processed_ == max_bbs_to_process_)) {
-    last_value_ = kNoValue;  // Make bad.
-    return nullptr;
+    // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations.
+    if (!modifications_allowed_) {
+      last_value_ = kNoValue;  // Make bad.
+      return nullptr;
+    }
   }
   if (allocator == nullptr) {
     allocator = allocator_;
@@ -67,7 +70,7 @@
   if (bb->block_type == kEntryBlock) {
     if ((cu_->access_flags & kAccStatic) == 0) {
       // If non-static method, mark "this" as non-null
-      int this_reg = cu_->num_dalvik_registers - cu_->num_ins;
+      int this_reg = cu_->mir_graph->GetFirstInVR();
       uint16_t value_name = work_lvn_->GetSRegValueName(this_reg);
       work_lvn_->SetValueNameNullChecked(value_name);
     }
@@ -86,21 +89,20 @@
     // the loop head stack will also be empty and there will be nothing to merge anyway.
     bool use_all_predecessors = true;
     uint16_t loop_head_idx = 0u;  // Used only if !use_all_predecessors.
-    if (mir_graph_->GetTopologicalSortOrderLoopHeadStack()->Size() != 0) {
+    if (mir_graph_->GetTopologicalSortOrderLoopHeadStack()->size() != 0) {
       // Full GVN inside a loop, see if we're at the loop head for the first time.
-      auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->Peek();
+      auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->back();
       loop_head_idx = top.first;
       bool recalculating = top.second;
       use_all_predecessors = recalculating ||
-          loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()->Get(bb->id);
+          loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()[bb->id];
     }
-    GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
-    for (BasicBlock* pred_bb = mir_graph_->GetBasicBlock(iter.Next());
-         pred_bb != nullptr; pred_bb = mir_graph_->GetBasicBlock(iter.Next())) {
-      if (lvns_[pred_bb->id] != nullptr &&
+    for (BasicBlockId pred_id : bb->predecessors) {
+      DCHECK_NE(pred_id, NullBasicBlockId);
+      if (lvns_[pred_id] != nullptr &&
           (use_all_predecessors ||
-              mir_graph_->GetTopologicalSortOrderIndexes()->Get(pred_bb->id) < loop_head_idx)) {
-        merge_lvns_.push_back(lvns_[pred_bb->id]);
+              mir_graph_->GetTopologicalSortOrderIndexes()[pred_id] < loop_head_idx)) {
+        merge_lvns_.push_back(lvns_[pred_id]);
       }
     }
     // Determine merge type.
diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h
index c06ff6f..27183bf 100644
--- a/compiler/dex/global_value_numbering.h
+++ b/compiler/dex/global_value_numbering.h
@@ -79,7 +79,7 @@
   static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
     return (static_cast<uint64_t>(op) << 48 | static_cast<uint64_t>(operand1) << 32 |
             static_cast<uint64_t>(operand2) << 16 | static_cast<uint64_t>(modifier));
-  };
+  }
 
   // Look up a value in the global value map, adding a new entry if there was none before.
   uint16_t LookupValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
@@ -93,7 +93,7 @@
       global_value_map_.PutBefore(lb, key, res);
     }
     return res;
-  };
+  }
 
   // Check if the exact value is stored in the global value map.
   bool HasValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier,
@@ -105,7 +105,7 @@
     uint64_t key = BuildKey(op, operand1, operand2, modifier);
     ValueMap::const_iterator it = global_value_map_.find(key);
     return (it != global_value_map_.end() && it->second == value);
-  };
+  }
 
   // FieldReference represents a unique resolved field.
   struct FieldReference {
@@ -214,7 +214,7 @@
   static constexpr uint32_t kMaxBbsToProcessMultiplyFactor = 20u;
 
   uint32_t bbs_processed_;
-  uint32_t max_bbs_to_process_;
+  uint32_t max_bbs_to_process_;  // Doesn't apply after the main GVN has converged.
 
   // We have 32-bit last_value_ so that we can detect when we run out of value names, see Good().
   // We usually don't check Good() until the end of LVN unless we're about to modify code.
diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc
index e8501cd..1d9920d 100644
--- a/compiler/dex/global_value_numbering_test.cc
+++ b/compiler/dex/global_value_numbering_test.cc
@@ -129,8 +129,8 @@
     { bb, static_cast<Instruction::Code>(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } }
 
   void DoPrepareIFields(const IFieldDef* defs, size_t count) {
-    cu_.mir_graph->ifield_lowering_infos_.Reset();
-    cu_.mir_graph->ifield_lowering_infos_.Resize(count);
+    cu_.mir_graph->ifield_lowering_infos_.clear();
+    cu_.mir_graph->ifield_lowering_infos_.reserve(count);
     for (size_t i = 0u; i != count; ++i) {
       const IFieldDef* def = &defs[i];
       MirIFieldLoweringInfo field_info(def->field_idx);
@@ -140,7 +140,7 @@
         field_info.flags_ = 0u |  // Without kFlagIsStatic.
             (def->is_volatile ? MirIFieldLoweringInfo::kFlagIsVolatile : 0u);
       }
-      cu_.mir_graph->ifield_lowering_infos_.Insert(field_info);
+      cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
     }
   }
 
@@ -150,8 +150,8 @@
   }
 
   void DoPrepareSFields(const SFieldDef* defs, size_t count) {
-    cu_.mir_graph->sfield_lowering_infos_.Reset();
-    cu_.mir_graph->sfield_lowering_infos_.Resize(count);
+    cu_.mir_graph->sfield_lowering_infos_.clear();
+    cu_.mir_graph->sfield_lowering_infos_.reserve(count);
     for (size_t i = 0u; i != count; ++i) {
       const SFieldDef* def = &defs[i];
       MirSFieldLoweringInfo field_info(def->field_idx);
@@ -163,7 +163,7 @@
         field_info.declaring_field_idx_ = def->declaring_field_idx;
         field_info.flags_ |= (def->is_volatile ? MirSFieldLoweringInfo::kFlagIsVolatile : 0u);
       }
-      cu_.mir_graph->sfield_lowering_infos_.Insert(field_info);
+      cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
     }
   }
 
@@ -174,41 +174,33 @@
 
   void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
     cu_.mir_graph->block_id_map_.clear();
-    cu_.mir_graph->block_list_.Reset();
+    cu_.mir_graph->block_list_.clear();
     ASSERT_LT(3u, count);  // null, entry, exit and at least one bytecode block.
     ASSERT_EQ(kNullBlock, defs[0].type);
     ASSERT_EQ(kEntryBlock, defs[1].type);
     ASSERT_EQ(kExitBlock, defs[2].type);
     for (size_t i = 0u; i != count; ++i) {
       const BBDef* def = &defs[i];
-      BasicBlock* bb = cu_.mir_graph->NewMemBB(def->type, i);
-      cu_.mir_graph->block_list_.Insert(bb);
+      BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
       if (def->num_successors <= 2) {
         bb->successor_block_list_type = kNotUsed;
-        bb->successor_blocks = nullptr;
         bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
         bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
       } else {
         bb->successor_block_list_type = kPackedSwitch;
         bb->fall_through = 0u;
         bb->taken = 0u;
-        bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
-            &cu_.arena, def->num_successors, kGrowableArraySuccessorBlocks);
+        bb->successor_blocks.reserve(def->num_successors);
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
                                                                kArenaAllocSuccessor));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
-          bb->successor_blocks->Insert(successor_block_info);
+          bb->successor_blocks.push_back(successor_block_info);
         }
       }
-      bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
-          &cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
-      for (size_t j = 0u; j != def->num_predecessors; ++j) {
-        ASSERT_NE(0u, def->predecessors[j]);
-        bb->predecessors->Insert(def->predecessors[j]);
-      }
+      bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
       if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
         bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
             cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
@@ -216,10 +208,10 @@
       }
     }
     cu_.mir_graph->num_blocks_ = count;
-    ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
-    cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
+    ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
+    cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
-    cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
+    cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
     ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
   }
 
@@ -235,24 +227,23 @@
     for (size_t i = 0u; i != count; ++i) {
       const MIRDef* def = &defs[i];
       MIR* mir = &mirs_[i];
-      ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.Size());
-      BasicBlock* bb = cu_.mir_graph->block_list_.Get(def->bbid);
+      ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size());
+      BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
       bb->AppendMIR(mir);
       mir->dalvikInsn.opcode = def->opcode;
       mir->dalvikInsn.vB = static_cast<int32_t>(def->value);
       mir->dalvikInsn.vB_wide = def->value;
       if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
-        ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.Size());
+        ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size());
         mir->meta.ifield_lowering_info = def->field_info;
       } else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
-        ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.Size());
+        ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size());
         mir->meta.sfield_lowering_info = def->field_info;
       } else if (def->opcode == static_cast<Instruction::Code>(kMirOpPhi)) {
         mir->meta.phi_incoming = static_cast<BasicBlockId*>(
             allocator_->Alloc(def->num_uses * sizeof(BasicBlockId), kArenaAllocDFInfo));
-        for (size_t i = 0; i != def->num_uses; ++i) {
-          mir->meta.phi_incoming[i] = bb->predecessors->Get(i);
-        }
+        ASSERT_EQ(def->num_uses, bb->predecessors.size());
+        std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming);
       }
       mir->ssa_rep = &ssa_reps_[i];
       mir->ssa_rep->num_uses = def->num_uses;
@@ -266,6 +257,10 @@
       mir->optimization_flags = 0u;
     }
     mirs_[count - 1u].next = nullptr;
+    DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
+        cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
+    code_item->insns_size_in_code_units_ = 2u * count;
+    cu_.mir_graph->current_code_item_ = code_item;
   }
 
   template <size_t count>
@@ -341,11 +336,11 @@
     allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack));
     // Bind all possible sregs to live vregs for test purposes.
     live_in_v_->SetInitialBits(kMaxSsaRegs);
-    cu_.mir_graph->ssa_base_vregs_ = new (&cu_.arena) GrowableArray<int>(&cu_.arena, kMaxSsaRegs);
-    cu_.mir_graph->ssa_subscripts_ = new (&cu_.arena) GrowableArray<int>(&cu_.arena, kMaxSsaRegs);
+    cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs);
+    cu_.mir_graph->ssa_subscripts_.reserve(kMaxSsaRegs);
     for (unsigned int i = 0; i < kMaxSsaRegs; i++) {
-      cu_.mir_graph->ssa_base_vregs_->Insert(i);
-      cu_.mir_graph->ssa_subscripts_->Insert(0);
+      cu_.mir_graph->ssa_base_vregs_.push_back(i);
+      cu_.mir_graph->ssa_subscripts_.push_back(0);
     }
   }
 
@@ -434,12 +429,10 @@
   // Add successor block info to the check block.
   BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
   check_bb->successor_block_list_type = kCatch;
-  check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
-      &cu_.arena, 2, kGrowableArraySuccessorBlocks);
   SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
       (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
   successor_block_info->block = catch_handler->id;
-  check_bb->successor_blocks->Insert(successor_block_info);
+  check_bb->successor_blocks.push_back(successor_block_info);
 }
 
 class GlobalValueNumberingTestTwoConsecutiveLoops : public GlobalValueNumberingTest {
@@ -2116,12 +2109,10 @@
   // Add successor block info to the check block.
   BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
   check_bb->successor_block_list_type = kCatch;
-  check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
-      &cu_.arena, 2, kGrowableArraySuccessorBlocks);
   SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
       (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
   successor_block_info->block = catch_handler->id;
-  check_bb->successor_blocks->Insert(successor_block_info);
+  check_bb->successor_blocks.push_back(successor_block_info);
   BasicBlock* merge_block = cu_.mir_graph->GetBasicBlock(4u);
   std::swap(merge_block->taken, merge_block->fall_through);
   PrepareMIRs(mirs);
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index 5997568..eb0806b 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -464,7 +464,7 @@
     const MIR* mir = fall_through_bb->first_mir_insn;
     DCHECK(mir != nullptr);
     // Only INVOKEs can leak and clobber non-aliasing references if they throw.
-    if ((Instruction::FlagsOf(mir->dalvikInsn.opcode) & Instruction::kInvoke) != 0) {
+    if ((mir->dalvikInsn.FlagsOf() & Instruction::kInvoke) != 0) {
       for (uint16_t i = 0u; i != mir->ssa_rep->num_uses; ++i) {
         uint16_t value_name = lvn->GetOperandValue(mir->ssa_rep->uses[i]);
         non_aliasing_refs_.erase(value_name);
@@ -656,13 +656,37 @@
   }
 }
 
-void LocalValueNumbering::MergeNullChecked(const ValueNameSet::value_type& entry,
-                                           ValueNameSet::iterator hint) {
-  // Merge null_checked_ for this ref.
-  merge_names_.clear();
-  merge_names_.resize(gvn_->merge_lvns_.size(), entry);
-  if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
-    null_checked_.insert(hint, entry);
+void LocalValueNumbering::MergeNullChecked() {
+  DCHECK_GE(gvn_->merge_lvns_.size(), 2u);
+
+  // Find the LVN with the least entries in the set.
+  const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0];
+  for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
+    if (lvn->null_checked_.size() < least_entries_lvn->null_checked_.size()) {
+      least_entries_lvn = lvn;
+    }
+  }
+
+  // For each null-checked value name check if it's null-checked in all the LVNs.
+  for (const auto& value_name : least_entries_lvn->null_checked_) {
+    // Merge null_checked_ for this ref.
+    merge_names_.clear();
+    merge_names_.resize(gvn_->merge_lvns_.size(), value_name);
+    if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
+      null_checked_.insert(null_checked_.end(), value_name);
+    }
+  }
+
+  // Now check if the least_entries_lvn has a null-check as the last insn.
+  const BasicBlock* least_entries_bb = gvn_->GetBasicBlock(least_entries_lvn->Id());
+  if (gvn_->HasNullCheckLastInsn(least_entries_bb, id_)) {
+    int s_reg = least_entries_bb->last_mir_insn->ssa_rep->uses[0];
+    uint32_t value_name = least_entries_lvn->GetSRegValueName(s_reg);
+    merge_names_.clear();
+    merge_names_.resize(gvn_->merge_lvns_.size(), value_name);
+    if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
+      null_checked_.insert(value_name);
+    }
   }
 }
 
@@ -896,8 +920,7 @@
   IntersectSets<RangeCheckSet, &LocalValueNumbering::range_checked_>();
 
   // Merge null_checked_. We may later insert more, such as merged object field values.
-  MergeSets<ValueNameSet, &LocalValueNumbering::null_checked_,
-            &LocalValueNumbering::MergeNullChecked>();
+  MergeNullChecked();
 
   if (merge_type == kCatchMerge) {
     // Memory is clobbered. New memory version already created, don't merge aliasing locations.
@@ -1146,8 +1169,9 @@
   const MirFieldInfo& field_info = gvn_->GetMirGraph()->GetIFieldLoweringInfo(mir);
   uint16_t res;
   if (!field_info.IsResolved() || field_info.IsVolatile()) {
-    // Volatile fields always get a new memory version; field id is irrelevant.
     // Unresolved fields may be volatile, so handle them as such to be safe.
+    HandleInvokeOrClInitOrAcquireOp(mir);  // Volatile GETs have acquire semantics.
+    // Volatile fields always get a new memory version; field id is irrelevant.
     // Use result s_reg - will be unique.
     res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue);
   } else {
@@ -1246,14 +1270,16 @@
 
 uint16_t LocalValueNumbering::HandleSGet(MIR* mir, uint16_t opcode) {
   const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir);
-  if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
-    // Class initialization can call arbitrary functions, we need to wipe aliasing values.
-    HandleInvokeOrClInit(mir);
+  if (!field_info.IsResolved() || field_info.IsVolatile() ||
+      (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0)) {
+    // Volatile SGETs (and unresolved fields are potentially volatile) have acquire semantics
+    // and class initialization can call arbitrary functions, we need to wipe aliasing values.
+    HandleInvokeOrClInitOrAcquireOp(mir);
   }
   uint16_t res;
   if (!field_info.IsResolved() || field_info.IsVolatile()) {
-    // Volatile fields always get a new memory version; field id is irrelevant.
     // Unresolved fields may be volatile, so handle them as such to be safe.
+    // Volatile fields always get a new memory version; field id is irrelevant.
     // Use result s_reg - will be unique.
     res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue);
   } else {
@@ -1283,7 +1309,7 @@
   const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir);
   if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
     // Class initialization can call arbitrary functions, we need to wipe aliasing values.
-    HandleInvokeOrClInit(mir);
+    HandleInvokeOrClInitOrAcquireOp(mir);
   }
   uint16_t type = opcode - Instruction::SPUT;
   if (!field_info.IsResolved()) {
@@ -1328,7 +1354,7 @@
   }
 }
 
-void LocalValueNumbering::HandleInvokeOrClInit(MIR* mir) {
+void LocalValueNumbering::HandleInvokeOrClInitOrAcquireOp(MIR* mir) {
   // Use mir->offset as modifier; without elaborate inlining, it will be unique.
   global_memory_version_ =
       gvn_->LookupValue(kInvokeMemoryVersionBumpOp, 0u, 0u, mir->offset);
@@ -1381,9 +1407,7 @@
 
     case Instruction::MONITOR_ENTER:
       HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0]));
-      // NOTE: Keeping all aliasing values intact. Programs that rely on loads/stores of the
-      // same non-volatile locations outside and inside a synchronized block being different
-      // contain races that we cannot fix.
+      HandleInvokeOrClInitOrAcquireOp(mir);  // Acquire operation.
       break;
 
     case Instruction::MONITOR_EXIT:
@@ -1445,7 +1469,7 @@
           uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]);
           non_aliasing_refs_.erase(reg);
         }
-        HandleInvokeOrClInit(mir);
+        HandleInvokeOrClInitOrAcquireOp(mir);
       }
       break;
 
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 855d66d..c60da32 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -122,19 +122,19 @@
 
   void SetOperandValue(uint16_t s_reg, uint16_t value) {
     SetOperandValueImpl(s_reg, value, &sreg_value_map_);
-  };
+  }
 
   uint16_t GetOperandValue(int s_reg) const {
     return GetOperandValueImpl(s_reg, &sreg_value_map_);
-  };
+  }
 
   void SetOperandValueWide(uint16_t s_reg, uint16_t value) {
     SetOperandValueImpl(s_reg, value, &sreg_wide_value_map_);
-  };
+  }
 
   uint16_t GetOperandValueWide(int s_reg) const {
     return GetOperandValueImpl(s_reg, &sreg_wide_value_map_);
-  };
+  }
 
   struct RangeCheckKey {
     uint16_t array;
@@ -308,7 +308,7 @@
   uint16_t HandleSGet(MIR* mir, uint16_t opcode);
   void HandleSPut(MIR* mir, uint16_t opcode);
   void RemoveSFieldsForType(uint16_t type);
-  void HandleInvokeOrClInit(MIR* mir);
+  void HandleInvokeOrClInitOrAcquireOp(MIR* mir);
 
   bool SameMemoryVersion(const LocalValueNumbering& other) const;
 
@@ -343,11 +343,11 @@
                                      EscapedIFieldClobberSet::iterator hint);
   void MergeEscapedArrayClobberSets(const EscapedArrayClobberSet::value_type& entry,
                                     EscapedArrayClobberSet::iterator hint);
-  void MergeNullChecked(const ValueNameSet::value_type& entry, ValueNameSet::iterator hint);
   void MergeSFieldValues(const SFieldToValueMap::value_type& entry,
                          SFieldToValueMap::iterator hint);
   void MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry,
                                     IFieldLocToValueMap::iterator hint);
+  void MergeNullChecked();
 
   template <typename Map, Map LocalValueNumbering::*map_ptr, typename Versions>
   void MergeAliasingValues(const typename Map::value_type& entry, typename Map::iterator hint);
diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc
index e4e944e..067bea2 100644
--- a/compiler/dex/local_value_numbering_test.cc
+++ b/compiler/dex/local_value_numbering_test.cc
@@ -86,8 +86,8 @@
     { opcode, 0u, 0u, 0, { }, 1, { reg } }  // CONST_CLASS, CONST_STRING, NEW_ARRAY, ...
 
   void DoPrepareIFields(const IFieldDef* defs, size_t count) {
-    cu_.mir_graph->ifield_lowering_infos_.Reset();
-    cu_.mir_graph->ifield_lowering_infos_.Resize(count);
+    cu_.mir_graph->ifield_lowering_infos_.clear();
+    cu_.mir_graph->ifield_lowering_infos_.reserve(count);
     for (size_t i = 0u; i != count; ++i) {
       const IFieldDef* def = &defs[i];
       MirIFieldLoweringInfo field_info(def->field_idx);
@@ -97,7 +97,7 @@
         field_info.flags_ = 0u |  // Without kFlagIsStatic.
             (def->is_volatile ? MirIFieldLoweringInfo::kFlagIsVolatile : 0u);
       }
-      cu_.mir_graph->ifield_lowering_infos_.Insert(field_info);
+      cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
     }
   }
 
@@ -107,8 +107,8 @@
   }
 
   void DoPrepareSFields(const SFieldDef* defs, size_t count) {
-    cu_.mir_graph->sfield_lowering_infos_.Reset();
-    cu_.mir_graph->sfield_lowering_infos_.Resize(count);
+    cu_.mir_graph->sfield_lowering_infos_.clear();
+    cu_.mir_graph->sfield_lowering_infos_.reserve(count);
     for (size_t i = 0u; i != count; ++i) {
       const SFieldDef* def = &defs[i];
       MirSFieldLoweringInfo field_info(def->field_idx);
@@ -120,7 +120,7 @@
         field_info.declaring_field_idx_ = def->declaring_field_idx;
         field_info.flags_ |= (def->is_volatile ? MirSFieldLoweringInfo::kFlagIsVolatile : 0u);
       }
-      cu_.mir_graph->sfield_lowering_infos_.Insert(field_info);
+      cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
     }
   }
 
@@ -140,10 +140,10 @@
       mir->dalvikInsn.vB = static_cast<int32_t>(def->value);
       mir->dalvikInsn.vB_wide = def->value;
       if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
-        ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.Size());
+        ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size());
         mir->meta.ifield_lowering_info = def->field_info;
       } else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
-        ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.Size());
+        ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size());
         mir->meta.sfield_lowering_info = def->field_info;
       }
       mir->ssa_rep = &ssa_reps_[i];
@@ -170,8 +170,8 @@
   }
 
   void MakeSFieldUninitialized(uint32_t sfield_index) {
-    CHECK_LT(sfield_index, cu_.mir_graph->sfield_lowering_infos_.Size());
-    cu_.mir_graph->sfield_lowering_infos_.GetRawStorage()[sfield_index].flags_ &=
+    CHECK_LT(sfield_index, cu_.mir_graph->sfield_lowering_infos_.size());
+    cu_.mir_graph->sfield_lowering_infos_[sfield_index].flags_ &=
         ~MirSFieldLoweringInfo::kFlagIsInitialized;
   }
 
@@ -338,16 +338,19 @@
       DEF_IGET(Instruction::IGET, 1u,  0u, 0u),  // Non-volatile.
       DEF_IGET(Instruction::IGET, 2u, 10u, 1u),  // Volatile.
       DEF_IGET(Instruction::IGET, 3u,  2u, 1u),  // Non-volatile.
+      DEF_IGET(Instruction::IGET, 4u,  0u, 0u),  // Non-volatile.
   };
 
   PrepareIFields(ifields);
   PrepareMIRs(mirs);
   PerformLVN();
-  ASSERT_EQ(value_names_.size(), 4u);
+  ASSERT_EQ(value_names_.size(), 5u);
   EXPECT_NE(value_names_[0], value_names_[2]);  // Volatile has always different value name.
   EXPECT_NE(value_names_[1], value_names_[3]);  // Used different base because of volatile.
+  EXPECT_NE(value_names_[1], value_names_[4]);  // Not guaranteed to be the same after "acquire".
+
   for (size_t i = 0; i != arraysize(mirs); ++i) {
-    EXPECT_EQ((i == 2u) ? MIR_IGNORE_NULL_CHECK : 0,
+    EXPECT_EQ((i == 2u || i == 4u) ? MIR_IGNORE_NULL_CHECK : 0,
               mirs_[i].optimization_flags) << i;
   }
 }
@@ -363,7 +366,7 @@
       DEF_IGET(Instruction::IGET, 1u, 20u, 0u),             // Resolved field #1, unique object.
       DEF_IGET(Instruction::IGET, 2u, 21u, 0u),             // Resolved field #1.
       DEF_IGET_WIDE(Instruction::IGET_WIDE, 3u, 21u, 1u),   // Resolved field #2.
-      DEF_IGET(Instruction::IGET, 4u, 22u, 2u),             // IGET doesn't clobber anything.
+      DEF_IGET(Instruction::IGET, 4u, 22u, 2u),             // Unresolved IGET can be "acquire".
       DEF_IGET(Instruction::IGET, 5u, 20u, 0u),             // Resolved field #1, unique object.
       DEF_IGET(Instruction::IGET, 6u, 21u, 0u),             // Resolved field #1.
       DEF_IGET_WIDE(Instruction::IGET_WIDE, 7u, 21u, 1u),   // Resolved field #2.
@@ -381,14 +384,15 @@
   PrepareMIRs(mirs);
   PerformLVN();
   ASSERT_EQ(value_names_.size(), 16u);
-  EXPECT_EQ(value_names_[1], value_names_[5]);
-  EXPECT_EQ(value_names_[2], value_names_[6]);
-  EXPECT_EQ(value_names_[3], value_names_[7]);
-  EXPECT_EQ(value_names_[1], value_names_[9]);
-  EXPECT_NE(value_names_[2], value_names_[10]);  // This aliased with unresolved IPUT.
-  EXPECT_EQ(value_names_[3], value_names_[11]);
-  EXPECT_EQ(value_names_[12], value_names_[15]);
-  EXPECT_NE(value_names_[1], value_names_[14]);  // This aliased with unresolved IPUT.
+  // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics.
+  EXPECT_EQ(value_names_[1], value_names_[5]);    // Unique object.
+  EXPECT_NE(value_names_[2], value_names_[6]);    // Not guaranteed to be the same after "acquire".
+  EXPECT_NE(value_names_[3], value_names_[7]);    // Not guaranteed to be the same after "acquire".
+  EXPECT_EQ(value_names_[1], value_names_[9]);    // Unique object.
+  EXPECT_NE(value_names_[6], value_names_[10]);   // This aliased with unresolved IPUT.
+  EXPECT_EQ(value_names_[7], value_names_[11]);   // Still the same after "release".
+  EXPECT_EQ(value_names_[12], value_names_[15]);  // Still the same after "release".
+  EXPECT_NE(value_names_[1], value_names_[14]);   // This aliased with unresolved IPUT.
   EXPECT_EQ(mirs_[0].optimization_flags, 0u);
   EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
   EXPECT_EQ(mirs_[2].optimization_flags, 0u);
@@ -409,7 +413,7 @@
   static const MIRDef mirs[] = {
       DEF_SGET(Instruction::SGET, 0u, 0u),            // Resolved field #1.
       DEF_SGET_WIDE(Instruction::SGET_WIDE, 1u, 1u),  // Resolved field #2.
-      DEF_SGET(Instruction::SGET, 2u, 2u),            // SGET doesn't clobber anything.
+      DEF_SGET(Instruction::SGET, 2u, 2u),            // Unresolved SGET can be "acquire".
       DEF_SGET(Instruction::SGET, 3u, 0u),            // Resolved field #1.
       DEF_SGET_WIDE(Instruction::SGET_WIDE, 4u, 1u),  // Resolved field #2.
       DEF_SPUT(Instruction::SPUT, 5u, 2u),            // SPUT clobbers field #1 (#2 is wide).
@@ -421,10 +425,11 @@
   PrepareMIRs(mirs);
   PerformLVN();
   ASSERT_EQ(value_names_.size(), 8u);
-  EXPECT_EQ(value_names_[0], value_names_[3]);
-  EXPECT_EQ(value_names_[1], value_names_[4]);
-  EXPECT_NE(value_names_[0], value_names_[6]);  // This aliased with unresolved IPUT.
-  EXPECT_EQ(value_names_[1], value_names_[7]);
+  // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics.
+  EXPECT_NE(value_names_[0], value_names_[3]);  // Not guaranteed to be the same after "acquire".
+  EXPECT_NE(value_names_[1], value_names_[4]);  // Not guaranteed to be the same after "acquire".
+  EXPECT_NE(value_names_[3], value_names_[6]);  // This aliased with unresolved IPUT.
+  EXPECT_EQ(value_names_[4], value_names_[7]);  // Still the same after "release".
   for (size_t i = 0u; i != mir_count_; ++i) {
     EXPECT_EQ(0, mirs_[i].optimization_flags) << i;
   }
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 3de4483..ee48796 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -29,818 +29,911 @@
 
 namespace art {
 
-  // Instruction characteristics used to statically identify computation-intensive methods.
-const uint32_t MIRGraph::analysis_attributes_[kMirOpLast] = {
+enum InstructionAnalysisAttributeOps : uint8_t {
+  kUninterestingOp = 0,
+  kArithmeticOp,
+  kFpOp,
+  kSingleOp,
+  kDoubleOp,
+  kIntOp,
+  kLongOp,
+  kBranchOp,
+  kInvokeOp,
+  kArrayOp,
+  kHeavyweightOp,
+  kSimpleConstOp,
+  kMoveOp,
+  kSwitch
+};
+
+enum InstructionAnalysisAttributeMasks : uint16_t {
+  kAnNone = 1 << kUninterestingOp,
+  kAnMath = 1 << kArithmeticOp,
+  kAnFp = 1 << kFpOp,
+  kAnLong = 1 << kLongOp,
+  kAnInt = 1 << kIntOp,
+  kAnSingle = 1 << kSingleOp,
+  kAnDouble = 1 << kDoubleOp,
+  kAnFloatMath = 1 << kFpOp,
+  kAnBranch = 1 << kBranchOp,
+  kAnInvoke = 1 << kInvokeOp,
+  kAnArrayOp = 1 << kArrayOp,
+  kAnHeavyWeight = 1 << kHeavyweightOp,
+  kAnSimpleConst = 1 << kSimpleConstOp,
+  kAnMove = 1 << kMoveOp,
+  kAnSwitch = 1 << kSwitch,
+  kAnComputational = kAnMath | kAnArrayOp | kAnMove | kAnSimpleConst,
+};
+
+// Instruction characteristics used to statically identify computation-intensive methods.
+static const uint16_t kAnalysisAttributes[kMirOpLast] = {
   // 00 NOP
-  AN_NONE,
+  kAnNone,
 
   // 01 MOVE vA, vB
-  AN_MOVE,
+  kAnMove,
 
   // 02 MOVE_FROM16 vAA, vBBBB
-  AN_MOVE,
+  kAnMove,
 
   // 03 MOVE_16 vAAAA, vBBBB
-  AN_MOVE,
+  kAnMove,
 
   // 04 MOVE_WIDE vA, vB
-  AN_MOVE,
+  kAnMove,
 
   // 05 MOVE_WIDE_FROM16 vAA, vBBBB
-  AN_MOVE,
+  kAnMove,
 
   // 06 MOVE_WIDE_16 vAAAA, vBBBB
-  AN_MOVE,
+  kAnMove,
 
   // 07 MOVE_OBJECT vA, vB
-  AN_MOVE,
+  kAnMove,
 
   // 08 MOVE_OBJECT_FROM16 vAA, vBBBB
-  AN_MOVE,
+  kAnMove,
 
   // 09 MOVE_OBJECT_16 vAAAA, vBBBB
-  AN_MOVE,
+  kAnMove,
 
   // 0A MOVE_RESULT vAA
-  AN_MOVE,
+  kAnMove,
 
   // 0B MOVE_RESULT_WIDE vAA
-  AN_MOVE,
+  kAnMove,
 
   // 0C MOVE_RESULT_OBJECT vAA
-  AN_MOVE,
+  kAnMove,
 
   // 0D MOVE_EXCEPTION vAA
-  AN_MOVE,
+  kAnMove,
 
   // 0E RETURN_VOID
-  AN_BRANCH,
+  kAnBranch,
 
   // 0F RETURN vAA
-  AN_BRANCH,
+  kAnBranch,
 
   // 10 RETURN_WIDE vAA
-  AN_BRANCH,
+  kAnBranch,
 
   // 11 RETURN_OBJECT vAA
-  AN_BRANCH,
+  kAnBranch,
 
   // 12 CONST_4 vA, #+B
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 13 CONST_16 vAA, #+BBBB
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 14 CONST vAA, #+BBBBBBBB
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 15 CONST_HIGH16 VAA, #+BBBB0000
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 16 CONST_WIDE_16 vAA, #+BBBB
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 17 CONST_WIDE_32 vAA, #+BBBBBBBB
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
-  AN_SIMPLECONST,
+  kAnSimpleConst,
 
   // 1A CONST_STRING vAA, string@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB
-  AN_NONE,
+  kAnNone,
 
   // 1C CONST_CLASS vAA, type@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 1D MONITOR_ENTER vAA
-  AN_NONE,
+  kAnNone,
 
   // 1E MONITOR_EXIT vAA
-  AN_NONE,
+  kAnNone,
 
   // 1F CHK_CAST vAA, type@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 20 INSTANCE_OF vA, vB, type@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 21 ARRAY_LENGTH vA, vB
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 22 NEW_INSTANCE vAA, type@BBBB
-  AN_HEAVYWEIGHT,
+  kAnHeavyWeight,
 
   // 23 NEW_ARRAY vA, vB, type@CCCC
-  AN_HEAVYWEIGHT,
+  kAnHeavyWeight,
 
   // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
-  AN_HEAVYWEIGHT,
+  kAnHeavyWeight,
 
   // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
-  AN_HEAVYWEIGHT,
+  kAnHeavyWeight,
 
   // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB
-  AN_NONE,
+  kAnNone,
 
   // 27 THROW vAA
-  AN_HEAVYWEIGHT | AN_BRANCH,
+  kAnHeavyWeight | kAnBranch,
 
   // 28 GOTO
-  AN_BRANCH,
+  kAnBranch,
 
   // 29 GOTO_16
-  AN_BRANCH,
+  kAnBranch,
 
   // 2A GOTO_32
-  AN_BRANCH,
+  kAnBranch,
 
   // 2B PACKED_SWITCH vAA, +BBBBBBBB
-  AN_SWITCH,
+  kAnSwitch,
 
   // 2C SPARSE_SWITCH vAA, +BBBBBBBB
-  AN_SWITCH,
+  kAnSwitch,
 
   // 2D CMPL_FLOAT vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // 2E CMPG_FLOAT vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // 2F CMPL_DOUBLE vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // 30 CMPG_DOUBLE vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // 31 CMP_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // 32 IF_EQ vA, vB, +CCCC
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 33 IF_NE vA, vB, +CCCC
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 34 IF_LT vA, vB, +CCCC
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 35 IF_GE vA, vB, +CCCC
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 36 IF_GT vA, vB, +CCCC
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 37 IF_LE vA, vB, +CCCC
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 38 IF_EQZ vAA, +BBBB
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 39 IF_NEZ vAA, +BBBB
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 3A IF_LTZ vAA, +BBBB
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 3B IF_GEZ vAA, +BBBB
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 3C IF_GTZ vAA, +BBBB
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 3D IF_LEZ vAA, +BBBB
-  AN_MATH | AN_BRANCH | AN_INT,
+  kAnMath | kAnBranch | kAnInt,
 
   // 3E UNUSED_3E
-  AN_NONE,
+  kAnNone,
 
   // 3F UNUSED_3F
-  AN_NONE,
+  kAnNone,
 
   // 40 UNUSED_40
-  AN_NONE,
+  kAnNone,
 
   // 41 UNUSED_41
-  AN_NONE,
+  kAnNone,
 
   // 42 UNUSED_42
-  AN_NONE,
+  kAnNone,
 
   // 43 UNUSED_43
-  AN_NONE,
+  kAnNone,
 
   // 44 AGET vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 45 AGET_WIDE vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 46 AGET_OBJECT vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 47 AGET_BOOLEAN vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 48 AGET_BYTE vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 49 AGET_CHAR vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 4A AGET_SHORT vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 4B APUT vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 4C APUT_WIDE vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 4D APUT_OBJECT vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 4E APUT_BOOLEAN vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 4F APUT_BYTE vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 50 APUT_CHAR vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 51 APUT_SHORT vAA, vBB, vCC
-  AN_ARRAYOP,
+  kAnArrayOp,
 
   // 52 IGET vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 53 IGET_WIDE vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 54 IGET_OBJECT vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 55 IGET_BOOLEAN vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 56 IGET_BYTE vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 57 IGET_CHAR vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 58 IGET_SHORT vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 59 IPUT vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 5A IPUT_WIDE vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 5B IPUT_OBJECT vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 5C IPUT_BOOLEAN vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 5D IPUT_BYTE vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 5E IPUT_CHAR vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 5F IPUT_SHORT vA, vB, field@CCCC
-  AN_NONE,
+  kAnNone,
 
   // 60 SGET vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 61 SGET_WIDE vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 62 SGET_OBJECT vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 63 SGET_BOOLEAN vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 64 SGET_BYTE vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 65 SGET_CHAR vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 66 SGET_SHORT vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 67 SPUT vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 68 SPUT_WIDE vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 69 SPUT_OBJECT vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 6A SPUT_BOOLEAN vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 6B SPUT_BYTE vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 6C SPUT_CHAR vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 6D SPUT_SHORT vAA, field@BBBB
-  AN_NONE,
+  kAnNone,
 
   // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 6F INVOKE_SUPER {vD, vE, vF, vG, vA}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 71 INVOKE_STATIC {vD, vE, vF, vG, vA}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 73 UNUSED_73
-  AN_NONE,
+  kAnNone,
 
   // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // 79 UNUSED_79
-  AN_NONE,
+  kAnNone,
 
   // 7A UNUSED_7A
-  AN_NONE,
+  kAnNone,
 
   // 7B NEG_INT vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 7C NOT_INT vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 7D NEG_LONG vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // 7E NOT_LONG vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // 7F NEG_FLOAT vA, vB
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // 80 NEG_DOUBLE vA, vB
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // 81 INT_TO_LONG vA, vB
-  AN_MATH | AN_INT | AN_LONG,
+  kAnMath | kAnInt | kAnLong,
 
   // 82 INT_TO_FLOAT vA, vB
-  AN_MATH | AN_FP | AN_INT | AN_SINGLE,
+  kAnMath | kAnFp | kAnInt | kAnSingle,
 
   // 83 INT_TO_DOUBLE vA, vB
-  AN_MATH | AN_FP | AN_INT | AN_DOUBLE,
+  kAnMath | kAnFp | kAnInt | kAnDouble,
 
   // 84 LONG_TO_INT vA, vB
-  AN_MATH | AN_INT | AN_LONG,
+  kAnMath | kAnInt | kAnLong,
 
   // 85 LONG_TO_FLOAT vA, vB
-  AN_MATH | AN_FP | AN_LONG | AN_SINGLE,
+  kAnMath | kAnFp | kAnLong | kAnSingle,
 
   // 86 LONG_TO_DOUBLE vA, vB
-  AN_MATH | AN_FP | AN_LONG | AN_DOUBLE,
+  kAnMath | kAnFp | kAnLong | kAnDouble,
 
   // 87 FLOAT_TO_INT vA, vB
-  AN_MATH | AN_FP | AN_INT | AN_SINGLE,
+  kAnMath | kAnFp | kAnInt | kAnSingle,
 
   // 88 FLOAT_TO_LONG vA, vB
-  AN_MATH | AN_FP | AN_LONG | AN_SINGLE,
+  kAnMath | kAnFp | kAnLong | kAnSingle,
 
   // 89 FLOAT_TO_DOUBLE vA, vB
-  AN_MATH | AN_FP | AN_SINGLE | AN_DOUBLE,
+  kAnMath | kAnFp | kAnSingle | kAnDouble,
 
   // 8A DOUBLE_TO_INT vA, vB
-  AN_MATH | AN_FP | AN_INT | AN_DOUBLE,
+  kAnMath | kAnFp | kAnInt | kAnDouble,
 
   // 8B DOUBLE_TO_LONG vA, vB
-  AN_MATH | AN_FP | AN_LONG | AN_DOUBLE,
+  kAnMath | kAnFp | kAnLong | kAnDouble,
 
   // 8C DOUBLE_TO_FLOAT vA, vB
-  AN_MATH | AN_FP | AN_SINGLE | AN_DOUBLE,
+  kAnMath | kAnFp | kAnSingle | kAnDouble,
 
   // 8D INT_TO_BYTE vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 8E INT_TO_CHAR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 8F INT_TO_SHORT vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 90 ADD_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 91 SUB_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 92 MUL_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 93 DIV_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 94 REM_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 95 AND_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 96 OR_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 97 XOR_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 98 SHL_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 99 SHR_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 9A USHR_INT vAA, vBB, vCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // 9B ADD_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // 9C SUB_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // 9D MUL_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // 9E DIV_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // 9F REM_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // A0 AND_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // A1 OR_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // A2 XOR_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // A3 SHL_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // A4 SHR_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // A5 USHR_LONG vAA, vBB, vCC
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // A6 ADD_FLOAT vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // A7 SUB_FLOAT vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // A8 MUL_FLOAT vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // A9 DIV_FLOAT vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // AA REM_FLOAT vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // AB ADD_DOUBLE vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // AC SUB_DOUBLE vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // AD MUL_DOUBLE vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // AE DIV_DOUBLE vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // AF REM_DOUBLE vAA, vBB, vCC
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // B0 ADD_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B1 SUB_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B2 MUL_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B3 DIV_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B4 REM_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B5 AND_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B6 OR_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B7 XOR_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B8 SHL_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // B9 SHR_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // BA USHR_INT_2ADDR vA, vB
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // BB ADD_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // BC SUB_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // BD MUL_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // BE DIV_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // BF REM_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // C0 AND_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // C1 OR_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // C2 XOR_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // C3 SHL_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // C4 SHR_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // C5 USHR_LONG_2ADDR vA, vB
-  AN_MATH | AN_LONG,
+  kAnMath | kAnLong,
 
   // C6 ADD_FLOAT_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // C7 SUB_FLOAT_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // C8 MUL_FLOAT_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // C9 DIV_FLOAT_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // CA REM_FLOAT_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_SINGLE,
+  kAnMath | kAnFp | kAnSingle,
 
   // CB ADD_DOUBLE_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // CC SUB_DOUBLE_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // CD MUL_DOUBLE_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // CE DIV_DOUBLE_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // CF REM_DOUBLE_2ADDR vA, vB
-  AN_MATH | AN_FP | AN_DOUBLE,
+  kAnMath | kAnFp | kAnDouble,
 
   // D0 ADD_INT_LIT16 vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D1 RSUB_INT vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D2 MUL_INT_LIT16 vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D3 DIV_INT_LIT16 vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D4 REM_INT_LIT16 vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D5 AND_INT_LIT16 vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D6 OR_INT_LIT16 vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D7 XOR_INT_LIT16 vA, vB, #+CCCC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D8 ADD_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // D9 RSUB_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // DA MUL_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // DB DIV_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // DC REM_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // DD AND_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // DE OR_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // DF XOR_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // E0 SHL_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // E1 SHR_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // E2 USHR_INT_LIT8 vAA, vBB, #+CC
-  AN_MATH | AN_INT,
+  kAnMath | kAnInt,
 
   // E3 IGET_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // E4 IPUT_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // E5 SGET_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // E6 SPUT_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // E7 IGET_OBJECT_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // E8 IGET_WIDE_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // E9 IPUT_WIDE_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // EA SGET_WIDE_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // EB SPUT_WIDE_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // EC BREAKPOINT
-  AN_NONE,
+  kAnNone,
 
   // ED THROW_VERIFICATION_ERROR
-  AN_HEAVYWEIGHT | AN_BRANCH,
+  kAnHeavyWeight | kAnBranch,
 
   // EE EXECUTE_INLINE
-  AN_NONE,
+  kAnNone,
 
   // EF EXECUTE_INLINE_RANGE
-  AN_NONE,
+  kAnNone,
 
   // F0 INVOKE_OBJECT_INIT_RANGE
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // F1 RETURN_VOID_BARRIER
-  AN_BRANCH,
+  kAnBranch,
 
   // F2 IGET_QUICK
-  AN_NONE,
+  kAnNone,
 
   // F3 IGET_WIDE_QUICK
-  AN_NONE,
+  kAnNone,
 
   // F4 IGET_OBJECT_QUICK
-  AN_NONE,
+  kAnNone,
 
   // F5 IPUT_QUICK
-  AN_NONE,
+  kAnNone,
 
   // F6 IPUT_WIDE_QUICK
-  AN_NONE,
+  kAnNone,
 
   // F7 IPUT_OBJECT_QUICK
-  AN_NONE,
+  kAnNone,
 
   // F8 INVOKE_VIRTUAL_QUICK
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // F9 INVOKE_VIRTUAL_QUICK_RANGE
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // FA INVOKE_SUPER_QUICK
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // FB INVOKE_SUPER_QUICK_RANGE
-  AN_INVOKE | AN_HEAVYWEIGHT,
+  kAnInvoke | kAnHeavyWeight,
 
   // FC IPUT_OBJECT_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // FD SGET_OBJECT_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // FE SPUT_OBJECT_VOLATILE
-  AN_NONE,
+  kAnNone,
 
   // FF UNUSED_FF
-  AN_NONE,
+  kAnNone,
 
   // Beginning of extended MIR opcodes
   // 100 MIR_PHI
-  AN_NONE,
+  kAnNone,
 
   // 101 MIR_COPY
-  AN_NONE,
+  kAnNone,
 
   // 102 MIR_FUSED_CMPL_FLOAT
-  AN_NONE,
+  kAnNone,
 
   // 103 MIR_FUSED_CMPG_FLOAT
-  AN_NONE,
+  kAnNone,
 
   // 104 MIR_FUSED_CMPL_DOUBLE
-  AN_NONE,
+  kAnNone,
 
   // 105 MIR_FUSED_CMPG_DOUBLE
-  AN_NONE,
+  kAnNone,
 
   // 106 MIR_FUSED_CMP_LONG
-  AN_NONE,
+  kAnNone,
 
   // 107 MIR_NOP
-  AN_NONE,
+  kAnNone,
 
   // 108 MIR_NULL_CHECK
-  AN_NONE,
+  kAnNone,
 
   // 109 MIR_RANGE_CHECK
-  AN_NONE,
+  kAnNone,
 
-  // 110 MIR_DIV_ZERO_CHECK
-  AN_NONE,
+  // 10A MIR_DIV_ZERO_CHECK
+  kAnNone,
 
-  // 111 MIR_CHECK
-  AN_NONE,
+  // 10B MIR_CHECK
+  kAnNone,
 
-  // 112 MIR_CHECKPART2
-  AN_NONE,
+  // 10C MIR_CHECKPART2
+  kAnNone,
 
-  // 113 MIR_SELECT
-  AN_NONE,
+  // 10D MIR_SELECT
+  kAnNone,
+
+  // 10E MirOpConstVector
+  kAnNone,
+
+  // 10F MirOpMoveVector
+  kAnNone,
+
+  // 110 MirOpPackedMultiply
+  kAnNone,
+
+  // 111 MirOpPackedAddition
+  kAnNone,
+
+  // 112 MirOpPackedSubtract
+  kAnNone,
+
+  // 113 MirOpPackedShiftLeft
+  kAnNone,
+
+  // 114 MirOpPackedSignedShiftRight
+  kAnNone,
+
+  // 115 MirOpPackedUnsignedShiftRight
+  kAnNone,
+
+  // 116 MirOpPackedAnd
+  kAnNone,
+
+  // 117 MirOpPackedOr
+  kAnNone,
+
+  // 118 MirOpPackedXor
+  kAnNone,
+
+  // 119 MirOpPackedAddReduce
+  kAnNone,
+
+  // 11A MirOpPackedReduce
+  kAnNone,
+
+  // 11B MirOpPackedSet
+  kAnNone,
+
+  // 11C MirOpReserveVectorRegisters
+  kAnNone,
+
+  // 11D MirOpReturnVectorRegisters
+  kAnNone,
+
+  // 11E MirOpMemBarrier
+  kAnNone,
+
+  // 11F MirOpPackedArrayGet
+  kAnArrayOp,
+
+  // 120 MirOpPackedArrayPut
+  kAnArrayOp,
 };
 
 struct MethodStats {
@@ -872,10 +965,10 @@
    */
   BasicBlock* ending_bb = bb;
   if (ending_bb->last_mir_insn != NULL) {
-    uint32_t ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode];
-    while ((ending_flags & AN_BRANCH) == 0) {
+    uint32_t ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode];
+    while ((ending_flags & kAnBranch) == 0) {
       ending_bb = GetBasicBlock(ending_bb->fall_through);
-      ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode];
+      ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode];
     }
   }
   /*
@@ -906,27 +999,27 @@
         // Skip any MIR pseudo-op.
         continue;
       }
-      uint32_t flags = analysis_attributes_[mir->dalvikInsn.opcode];
+      uint16_t flags = kAnalysisAttributes[mir->dalvikInsn.opcode];
       stats->dex_instructions += loop_scale_factor;
-      if ((flags & AN_BRANCH) == 0) {
-        computational_block &= ((flags & AN_COMPUTATIONAL) != 0);
+      if ((flags & kAnBranch) == 0) {
+        computational_block &= ((flags & kAnComputational) != 0);
       } else {
         stats->branch_ops += loop_scale_factor;
       }
-      if ((flags & AN_MATH) != 0) {
+      if ((flags & kAnMath) != 0) {
         stats->math_ops += loop_scale_factor;
         has_math = true;
       }
-      if ((flags & AN_FP) != 0) {
+      if ((flags & kAnFp) != 0) {
         stats->fp_ops += loop_scale_factor;
       }
-      if ((flags & AN_ARRAYOP) != 0) {
+      if ((flags & kAnArrayOp) != 0) {
         stats->array_ops += loop_scale_factor;
       }
-      if ((flags & AN_HEAVYWEIGHT) != 0) {
+      if ((flags & kAnHeavyWeight) != 0) {
         stats->heavyweight_ops += loop_scale_factor;
       }
-      if ((flags & AN_SWITCH) != 0) {
+      if ((flags & kAnSwitch) != 0) {
         stats->has_switch = true;
       }
     }
@@ -1037,6 +1130,7 @@
       default_cutoff = compiler_options.GetSmallMethodThreshold();
       break;
     case CompilerOptions::kSpeed:
+    case CompilerOptions::kTime:
       small_cutoff = compiler_options.GetHugeMethodThreshold();
       default_cutoff = compiler_options.GetHugeMethodThreshold();
       break;
@@ -1108,7 +1202,7 @@
 
 void MIRGraph::DoCacheFieldLoweringInfo() {
   // All IGET/IPUT/SGET/SPUT instructions take 2 code units and there must also be a RETURN.
-  const uint32_t max_refs = (current_code_item_->insns_size_in_code_units_ - 1u) / 2u;
+  const uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 2u;
   ScopedArenaAllocator allocator(&cu_->arena_stack);
   uint16_t* field_idxs =
       reinterpret_cast<uint16_t*>(allocator.Alloc(max_refs * sizeof(uint16_t), kArenaAllocMisc));
@@ -1124,12 +1218,11 @@
     for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
       if (mir->dalvikInsn.opcode >= Instruction::IGET &&
           mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
-        const Instruction* insn = Instruction::At(current_code_item_->insns_ + mir->offset);
         // Get field index and try to find it among existing indexes. If found, it's usually among
         // the last few added, so we'll start the search from ifield_pos/sfield_pos. Though this
         // is a linear search, it actually performs much better than map based approach.
         if (mir->dalvikInsn.opcode <= Instruction::IPUT_SHORT) {
-          uint16_t field_idx = insn->VRegC_22c();
+          uint16_t field_idx = mir->dalvikInsn.vC;
           size_t i = ifield_pos;
           while (i != 0u && field_idxs[i - 1] != field_idx) {
             --i;
@@ -1141,7 +1234,7 @@
             field_idxs[ifield_pos++] = field_idx;
           }
         } else {
-          uint16_t field_idx = insn->VRegB_21c();
+          uint16_t field_idx = mir->dalvikInsn.vB;
           size_t i = sfield_pos;
           while (i != max_refs && field_idxs[i] != field_idx) {
             ++i;
@@ -1160,25 +1253,25 @@
 
   if (ifield_pos != 0u) {
     // Resolve instance field infos.
-    DCHECK_EQ(ifield_lowering_infos_.Size(), 0u);
-    ifield_lowering_infos_.Resize(ifield_pos);
+    DCHECK_EQ(ifield_lowering_infos_.size(), 0u);
+    ifield_lowering_infos_.reserve(ifield_pos);
     for (size_t pos = 0u; pos != ifield_pos; ++pos) {
-      ifield_lowering_infos_.Insert(MirIFieldLoweringInfo(field_idxs[pos]));
+      ifield_lowering_infos_.push_back(MirIFieldLoweringInfo(field_idxs[pos]));
     }
     MirIFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(),
-                                ifield_lowering_infos_.GetRawStorage(), ifield_pos);
+                                   ifield_lowering_infos_.data(), ifield_pos);
   }
 
   if (sfield_pos != max_refs) {
     // Resolve static field infos.
-    DCHECK_EQ(sfield_lowering_infos_.Size(), 0u);
-    sfield_lowering_infos_.Resize(max_refs - sfield_pos);
+    DCHECK_EQ(sfield_lowering_infos_.size(), 0u);
+    sfield_lowering_infos_.reserve(max_refs - sfield_pos);
     for (size_t pos = max_refs; pos != sfield_pos;) {
       --pos;
-      sfield_lowering_infos_.Insert(MirSFieldLoweringInfo(field_idxs[pos]));
+      sfield_lowering_infos_.push_back(MirSFieldLoweringInfo(field_idxs[pos]));
     }
     MirSFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(),
-                                sfield_lowering_infos_.GetRawStorage(), max_refs - sfield_pos);
+                                   sfield_lowering_infos_.data(), max_refs - sfield_pos);
   }
 }
 
@@ -1221,7 +1314,7 @@
   ScopedArenaAllocator allocator(&cu_->arena_stack);
 
   // All INVOKE instructions take 3 code units and there must also be a RETURN.
-  uint32_t max_refs = (current_code_item_->insns_size_in_code_units_ - 1u) / 3u;
+  uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 3u;
 
   // Map invoke key (see MapEntry) to lowering info index and vice versa.
   // The invoke_map and sequential entries are essentially equivalent to Boost.MultiIndex's
@@ -1242,14 +1335,13 @@
           mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE_RANGE &&
           mir->dalvikInsn.opcode != Instruction::RETURN_VOID_BARRIER) {
         // Decode target method index and invoke type.
-        const Instruction* insn = Instruction::At(current_code_item_->insns_ + mir->offset);
         uint16_t target_method_idx;
         uint16_t invoke_type_idx;
         if (mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE) {
-          target_method_idx = insn->VRegB_35c();
+          target_method_idx = mir->dalvikInsn.vB;
           invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL;
         } else {
-          target_method_idx = insn->VRegB_3rc();
+          target_method_idx = mir->dalvikInsn.vB;
           invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL_RANGE;
         }
 
@@ -1280,9 +1372,9 @@
   }
 
   // Prepare unique method infos, set method info indexes for their MIRs.
-  DCHECK_EQ(method_lowering_infos_.Size(), 0u);
+  DCHECK_EQ(method_lowering_infos_.size(), 0u);
   const size_t count = invoke_map.size();
-  method_lowering_infos_.Resize(count);
+  method_lowering_infos_.reserve(count);
   for (size_t pos = 0u; pos != count; ++pos) {
     const MapEntry* entry = sequential_entries[pos];
     MirMethodLoweringInfo method_info(entry->target_method_idx,
@@ -1290,10 +1382,10 @@
     if (entry->devirt_target != nullptr) {
       method_info.SetDevirtualizationTarget(*entry->devirt_target);
     }
-    method_lowering_infos_.Insert(method_info);
+    method_lowering_infos_.push_back(method_info);
   }
   MirMethodLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(),
-                                 method_lowering_infos_.GetRawStorage(), count);
+                                 method_lowering_infos_.data(), count);
 }
 
 bool MIRGraph::SkipCompilationByName(const std::string& methodname) {
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index b82c5c7..246ae44 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -824,75 +824,84 @@
   DF_NOP,
 
   // 108 MIR_NULL_CHECK
-  0,
+  DF_UA | DF_REF_A | DF_NULL_CHK_0 | DF_LVN,
 
   // 109 MIR_RANGE_CHECK
   0,
 
-  // 110 MIR_DIV_ZERO_CHECK
+  // 10A MIR_DIV_ZERO_CHECK
   0,
 
-  // 111 MIR_CHECK
+  // 10B MIR_CHECK
   0,
 
-  // 112 MIR_CHECKPART2
+  // 10C MIR_CHECKPART2
   0,
 
-  // 113 MIR_SELECT
+  // 10D MIR_SELECT
   DF_DA | DF_UB,
 
-  // 114 MirOpConstVector
-  DF_DA,
-
-  // 115 MirOpMoveVector
+  // 10E MirOpConstVector
   0,
 
-  // 116 MirOpPackedMultiply
+  // 10F MirOpMoveVector
   0,
 
-  // 117 MirOpPackedAddition
+  // 110 MirOpPackedMultiply
   0,
 
-  // 118 MirOpPackedSubtract
+  // 111 MirOpPackedAddition
   0,
 
-  // 119 MirOpPackedShiftLeft
+  // 112 MirOpPackedSubtract
   0,
 
-  // 120 MirOpPackedSignedShiftRight
+  // 113 MirOpPackedShiftLeft
   0,
 
-  // 121 MirOpPackedUnsignedShiftRight
+  // 114 MirOpPackedSignedShiftRight
   0,
 
-  // 122 MirOpPackedAnd
+  // 115 MirOpPackedUnsignedShiftRight
   0,
 
-  // 123 MirOpPackedOr
+  // 116 MirOpPackedAnd
   0,
 
-  // 124 MirOpPackedXor
+  // 117 MirOpPackedOr
   0,
 
-  // 125 MirOpPackedAddReduce
-  DF_DA | DF_UA,
-
-  // 126 MirOpPackedReduce
-  DF_DA,
-
-  // 127 MirOpPackedSet
-  DF_UB,
-
-  // 128 MirOpReserveVectorRegisters
+  // 118 MirOpPackedXor
   0,
 
-  // 129 MirOpReturnVectorRegisters
+  // 119 MirOpPackedAddReduce
+  DF_FORMAT_EXTENDED,
+
+  // 11A MirOpPackedReduce
+  DF_FORMAT_EXTENDED,
+
+  // 11B MirOpPackedSet
+  DF_FORMAT_EXTENDED,
+
+  // 11C MirOpReserveVectorRegisters
   0,
+
+  // 11D MirOpReturnVectorRegisters
+  0,
+
+  // 11E MirOpMemBarrier
+  0,
+
+  // 11F MirOpPackedArrayGet
+  DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+
+  // 120 MirOpPackedArrayPut
+  DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
 };
 
 /* Return the base virtual register for a SSA name */
 int MIRGraph::SRegToVReg(int ssa_reg) const {
-  return ssa_base_vregs_->Get(ssa_reg);
+  return ssa_base_vregs_[ssa_reg];
 }
 
 /* Any register that is used before being defined is considered live-in */
@@ -912,7 +921,36 @@
 void MIRGraph::HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v,
                             ArenaBitVector* live_in_v,
                             const MIR::DecodedInstruction& d_insn) {
+  // For vector MIRs, vC contains type information
+  bool is_vector_type_wide = false;
+  int type_size = d_insn.vC >> 16;
+  if (type_size == k64 || type_size == kDouble) {
+    is_vector_type_wide = true;
+  }
+
   switch (static_cast<int>(d_insn.opcode)) {
+    case kMirOpPackedAddReduce:
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA);
+      if (is_vector_type_wide == true) {
+        HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA + 1);
+      }
+      HandleDef(def_v, d_insn.vA);
+      if (is_vector_type_wide == true) {
+        HandleDef(def_v, d_insn.vA + 1);
+      }
+      break;
+    case kMirOpPackedReduce:
+      HandleDef(def_v, d_insn.vA);
+      if (is_vector_type_wide == true) {
+        HandleDef(def_v, d_insn.vA + 1);
+      }
+      break;
+    case kMirOpPackedSet:
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB);
+      if (is_vector_type_wide == true) {
+        HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1);
+      }
+      break;
     default:
       LOG(ERROR) << "Unexpected Extended Opcode " << d_insn.opcode;
       break;
@@ -930,11 +968,11 @@
   if (bb->data_flow_info == NULL) return false;
 
   use_v = bb->data_flow_info->use_v =
-      new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapUse);
+      new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapUse);
   def_v = bb->data_flow_info->def_v =
-      new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapDef);
+      new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapDef);
   live_in_v = bb->data_flow_info->live_in_v =
-      new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapLiveIn);
+      new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapLiveIn);
 
   for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
     uint64_t df_attributes = GetDataFlowAttributes(mir);
@@ -984,31 +1022,30 @@
 }
 
 int MIRGraph::AddNewSReg(int v_reg) {
-  // Compiler temps always have a subscript of 0
-  int subscript = (v_reg < 0) ? 0 : ++ssa_last_defs_[v_reg];
+  int subscript = ++ssa_last_defs_[v_reg];
   uint32_t ssa_reg = GetNumSSARegs();
   SetNumSSARegs(ssa_reg + 1);
-  ssa_base_vregs_->Insert(v_reg);
-  ssa_subscripts_->Insert(subscript);
-  DCHECK_EQ(ssa_base_vregs_->Size(), ssa_subscripts_->Size());
+  ssa_base_vregs_.push_back(v_reg);
+  ssa_subscripts_.push_back(subscript);
+  DCHECK_EQ(ssa_base_vregs_.size(), ssa_subscripts_.size());
   // If we are expanding very late, update use counts too.
-  if (ssa_reg > 0 && use_counts_.Size() == ssa_reg) {
+  if (ssa_reg > 0 && use_counts_.size() == ssa_reg) {
     // Need to expand the counts.
-    use_counts_.Insert(0);
-    raw_use_counts_.Insert(0);
+    use_counts_.push_back(0);
+    raw_use_counts_.push_back(0);
   }
   return ssa_reg;
 }
 
 /* Find out the latest SSA register for a given Dalvik register */
 void MIRGraph::HandleSSAUse(int* uses, int dalvik_reg, int reg_index) {
-  DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers));
+  DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast<int>(GetNumOfCodeAndTempVRs())));
   uses[reg_index] = vreg_to_ssa_map_[dalvik_reg];
 }
 
 /* Setup a new SSA register for a given Dalvik register */
 void MIRGraph::HandleSSADef(int* defs, int dalvik_reg, int reg_index) {
-  DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers));
+  DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast<int>(GetNumOfCodeAndTempVRs())));
   int ssa_reg = AddNewSReg(dalvik_reg);
   vreg_to_ssa_map_[dalvik_reg] = ssa_reg;
   defs[reg_index] = ssa_reg;
@@ -1062,7 +1099,46 @@
 }
 
 void MIRGraph::DataFlowSSAFormatExtended(MIR* mir) {
+  const MIR::DecodedInstruction& d_insn = mir->dalvikInsn;
+  // For vector MIRs, vC contains type information
+  bool is_vector_type_wide = false;
+  int type_size = d_insn.vC >> 16;
+  if (type_size == k64 || type_size == kDouble) {
+    is_vector_type_wide = true;
+  }
+
   switch (static_cast<int>(mir->dalvikInsn.opcode)) {
+    case kMirOpPackedAddReduce:
+      // We have one use, plus one more for wide
+      AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vA, 0);
+      if (is_vector_type_wide == true) {
+        HandleSSAUse(mir->ssa_rep->uses, d_insn.vA + 1, 1);
+      }
+
+      // We have a def, plus one more for wide
+      AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1);
+      HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+      if (is_vector_type_wide == true) {
+        HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1);
+      }
+      break;
+    case kMirOpPackedReduce:
+      // We have a def, plus one more for wide
+      AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1);
+      HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+      if (is_vector_type_wide == true) {
+        HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1);
+      }
+      break;
+    case kMirOpPackedSet:
+      // We have one use, plus one more for wide
+      AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0);
+      if (is_vector_type_wide == true) {
+        HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1);
+      }
+      break;
     default:
       LOG(ERROR) << "Missing case for extended MIR: " << mir->dalvikInsn.opcode;
       break;
@@ -1085,7 +1161,7 @@
 
       // If not a pseudo-op, note non-leaf or can throw
     if (!MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) {
-      int flags = Instruction::FlagsOf(mir->dalvikInsn.opcode);
+      int flags = mir->dalvikInsn.FlagsOf();
 
       if ((flags & Instruction::kInvoke) != 0 && (mir->optimization_flags & MIR_INLINED) == 0) {
         attributes_ &= ~METHOD_IS_LEAF;
@@ -1188,70 +1264,23 @@
    * predecessor blocks.
    */
   bb->data_flow_info->vreg_to_ssa_map_exit =
-      static_cast<int*>(arena_->Alloc(sizeof(int) * cu_->num_dalvik_registers,
+      static_cast<int*>(arena_->Alloc(sizeof(int) * GetNumOfCodeAndTempVRs(),
                                       kArenaAllocDFInfo));
 
   memcpy(bb->data_flow_info->vreg_to_ssa_map_exit, vreg_to_ssa_map_,
-         sizeof(int) * cu_->num_dalvik_registers);
+         sizeof(int) * GetNumOfCodeAndTempVRs());
   return true;
 }
 
-/* Setup the basic data structures for SSA conversion */
-void MIRGraph::CompilerInitializeSSAConversion() {
-  size_t num_dalvik_reg = cu_->num_dalvik_registers;
-
-  ssa_base_vregs_ = new (arena_) GrowableArray<int>(arena_, num_dalvik_reg + GetDefCount() + 128,
-                                                    kGrowableArraySSAtoDalvikMap);
-  ssa_subscripts_ = new (arena_) GrowableArray<int>(arena_, num_dalvik_reg + GetDefCount() + 128,
-                                                    kGrowableArraySSAtoDalvikMap);
+void MIRGraph::InitializeBasicBlockDataFlow() {
   /*
-   * Initial number of SSA registers is equal to the number of Dalvik
-   * registers.
+   * Allocate the BasicBlockDataFlow structure for the entry and code blocks.
    */
-  SetNumSSARegs(num_dalvik_reg);
-
-  /*
-   * Initialize the SSA2Dalvik map list. For the first num_dalvik_reg elements,
-   * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
-   * into "(0 << 16) | i"
-   */
-  for (unsigned int i = 0; i < num_dalvik_reg; i++) {
-    ssa_base_vregs_->Insert(i);
-    ssa_subscripts_->Insert(0);
-  }
-
-  /*
-   * Initialize the DalvikToSSAMap map. There is one entry for each
-   * Dalvik register, and the SSA names for those are the same.
-   */
-  vreg_to_ssa_map_ =
-      static_cast<int*>(arena_->Alloc(sizeof(int) * num_dalvik_reg,
-                                      kArenaAllocDFInfo));
-  /* Keep track of the higest def for each dalvik reg */
-  ssa_last_defs_ =
-      static_cast<int*>(arena_->Alloc(sizeof(int) * num_dalvik_reg,
-                                      kArenaAllocDFInfo));
-
-  for (unsigned int i = 0; i < num_dalvik_reg; i++) {
-    vreg_to_ssa_map_[i] = i;
-    ssa_last_defs_[i] = 0;
-  }
-
-  // Create a compiler temporary for Method*. This is done after SSA initialization.
-  GetNewCompilerTemp(kCompilerTempSpecialMethodPtr, false);
-
-  /*
-   * Allocate the BasicBlockDataFlow structure for the entry and code blocks
-   */
-  GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
-
-  while (true) {
-    BasicBlock* bb = iterator.Next();
-    if (bb == NULL) break;
+  for (BasicBlock* bb : block_list_) {
     if (bb->hidden == true) continue;
     if (bb->block_type == kDalvikByteCode ||
-      bb->block_type == kEntryBlock ||
-      bb->block_type == kExitBlock) {
+        bb->block_type == kEntryBlock ||
+        bb->block_type == kExitBlock) {
       bb->data_flow_info =
           static_cast<BasicBlockDataFlow*>(arena_->Alloc(sizeof(BasicBlockDataFlow),
                                                          kArenaAllocDFInfo));
@@ -1259,54 +1288,54 @@
   }
 }
 
-/*
- * This function will make a best guess at whether the invoke will
- * end up using Method*.  It isn't critical to get it exactly right,
- * and attempting to do would involve more complexity than it's
- * worth.
- */
-bool MIRGraph::InvokeUsesMethodStar(MIR* mir) {
-  InvokeType type;
-  Instruction::Code opcode = mir->dalvikInsn.opcode;
-  switch (opcode) {
-    case Instruction::INVOKE_STATIC:
-    case Instruction::INVOKE_STATIC_RANGE:
-      type = kStatic;
-      break;
-    case Instruction::INVOKE_DIRECT:
-    case Instruction::INVOKE_DIRECT_RANGE:
-      type = kDirect;
-      break;
-    case Instruction::INVOKE_VIRTUAL:
-    case Instruction::INVOKE_VIRTUAL_RANGE:
-      type = kVirtual;
-      break;
-    case Instruction::INVOKE_INTERFACE:
-    case Instruction::INVOKE_INTERFACE_RANGE:
-      return false;
-    case Instruction::INVOKE_SUPER_RANGE:
-    case Instruction::INVOKE_SUPER:
-      type = kSuper;
-      break;
-    default:
-      LOG(WARNING) << "Unexpected invoke op: " << opcode;
-      return false;
+/* Setup the basic data structures for SSA conversion */
+void MIRGraph::CompilerInitializeSSAConversion() {
+  size_t num_reg = GetNumOfCodeAndTempVRs();
+
+  ssa_base_vregs_.clear();
+  ssa_base_vregs_.reserve(num_reg + GetDefCount() + 128);
+  ssa_subscripts_.clear();
+  ssa_subscripts_.reserve(num_reg + GetDefCount() + 128);
+
+  /*
+   * Initial number of SSA registers is equal to the number of Dalvik
+   * registers.
+   */
+  SetNumSSARegs(num_reg);
+
+  /*
+   * Initialize the SSA2Dalvik map list. For the first num_reg elements,
+   * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
+   * into "(0 << 16) | i"
+   */
+  for (unsigned int i = 0; i < num_reg; i++) {
+    ssa_base_vregs_.push_back(i);
+    ssa_subscripts_.push_back(0);
   }
-  DexCompilationUnit m_unit(cu_);
-  MethodReference target_method(cu_->dex_file, mir->dalvikInsn.vB);
-  int vtable_idx;
-  uintptr_t direct_code;
-  uintptr_t direct_method;
-  uint32_t current_offset = static_cast<uint32_t>(current_offset_);
-  bool fast_path =
-      cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset,
-                                              false, true,
-                                              &type, &target_method,
-                                              &vtable_idx,
-                                              &direct_code, &direct_method) &&
-                                              !(cu_->enable_debug & (1 << kDebugSlowInvokePath));
-  return (((type == kDirect) || (type == kStatic)) &&
-          fast_path && ((direct_code == 0) || (direct_method == 0)));
+
+  /*
+   * Initialize the DalvikToSSAMap map. There is one entry for each
+   * Dalvik register, and the SSA names for those are the same.
+   */
+  vreg_to_ssa_map_ =
+      static_cast<int*>(arena_->Alloc(sizeof(int) * num_reg,
+                                      kArenaAllocDFInfo));
+  /* Keep track of the higest def for each dalvik reg */
+  ssa_last_defs_ =
+      static_cast<int*>(arena_->Alloc(sizeof(int) * num_reg,
+                                      kArenaAllocDFInfo));
+
+  for (unsigned int i = 0; i < num_reg; i++) {
+    vreg_to_ssa_map_[i] = i;
+    ssa_last_defs_[i] = 0;
+  }
+
+  // Create a compiler temporary for Method*. This is done after SSA initialization.
+  CompilerTemp* method_temp = GetNewCompilerTemp(kCompilerTempSpecialMethodPtr, false);
+  // The MIR graph keeps track of the sreg for method pointer specially, so record that now.
+  method_sreg_ = method_temp->s_reg_low;
+
+  InitializeBasicBlockDataFlow();
 }
 
 /*
@@ -1327,8 +1356,8 @@
     }
     for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
       int s_reg = mir->ssa_rep->uses[i];
-      raw_use_counts_.Increment(s_reg);
-      use_counts_.Put(s_reg, use_counts_.Get(s_reg) + weight);
+      raw_use_counts_[s_reg] += 1u;
+      use_counts_[s_reg] += weight;
     }
     if (!(cu_->disable_opt & (1 << kPromoteCompilerTemps))) {
       uint64_t df_attributes = GetDataFlowAttributes(mir);
@@ -1343,8 +1372,8 @@
          * and save results for both here and GenInvoke.  For now, go ahead
          * and assume all invokes use method*.
          */
-        raw_use_counts_.Increment(method_sreg_);
-        use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + weight);
+        raw_use_counts_[method_sreg_] += 1u;
+        use_counts_[method_sreg_] += weight;
       }
     }
   }
@@ -1352,21 +1381,16 @@
 
 /* Verify if all the successor is connected with all the claimed predecessors */
 bool MIRGraph::VerifyPredInfo(BasicBlock* bb) {
-  GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
-
-  while (true) {
-    BasicBlock* pred_bb = GetBasicBlock(iter.Next());
-    if (!pred_bb) break;
+  for (BasicBlockId pred_id : bb->predecessors) {
+    BasicBlock* pred_bb = GetBasicBlock(pred_id);
+    DCHECK(pred_bb != nullptr);
     bool found = false;
     if (pred_bb->taken == bb->id) {
         found = true;
     } else if (pred_bb->fall_through == bb->id) {
         found = true;
     } else if (pred_bb->successor_block_list_type != kNotUsed) {
-      GrowableArray<SuccessorBlockInfo*>::Iterator iterator(pred_bb->successor_blocks);
-      while (true) {
-        SuccessorBlockInfo *successor_block_info = iterator.Next();
-        if (successor_block_info == NULL) break;
+      for (SuccessorBlockInfo* successor_block_info : pred_bb->successor_blocks) {
         BasicBlockId succ_bb = successor_block_info->block;
         if (succ_bb == bb->id) {
             found = true;
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 6aee563..dda9e77 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 #include <queue>
 
+#include "base/bit_vector-inl.h"
 #include "base/stl_util.h"
 #include "compiler_internals.h"
 #include "dex_file-inl.h"
@@ -28,6 +29,7 @@
 #include "dex/quick/dex_file_method_inliner.h"
 #include "leb128.h"
 #include "pass_driver_me_post_opt.h"
+#include "stack.h"
 #include "utils/scoped_arena_containers.h"
 
 namespace art {
@@ -65,29 +67,32 @@
   "PackedSet",
   "ReserveVectorRegisters",
   "ReturnVectorRegisters",
+  "MemBarrier",
+  "PackedArrayGet",
+  "PackedArrayPut",
 };
 
 MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
     : reg_location_(NULL),
       block_id_map_(std::less<unsigned int>(), arena->Adapter()),
       cu_(cu),
-      ssa_base_vregs_(NULL),
-      ssa_subscripts_(NULL),
+      ssa_base_vregs_(arena->Adapter(kArenaAllocSSAToDalvikMap)),
+      ssa_subscripts_(arena->Adapter(kArenaAllocSSAToDalvikMap)),
       vreg_to_ssa_map_(NULL),
       ssa_last_defs_(NULL),
       is_constant_v_(NULL),
       constant_values_(NULL),
-      use_counts_(arena, 256, kGrowableArrayMisc),
-      raw_use_counts_(arena, 256, kGrowableArrayMisc),
+      use_counts_(arena->Adapter()),
+      raw_use_counts_(arena->Adapter()),
       num_reachable_blocks_(0),
       max_num_reachable_blocks_(0),
-      dfs_order_(NULL),
-      dfs_post_order_(NULL),
-      dom_post_order_traversal_(NULL),
-      topological_order_(nullptr),
-      topological_order_loop_ends_(nullptr),
-      topological_order_indexes_(nullptr),
-      topological_order_loop_head_stack_(nullptr),
+      dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)),
+      dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)),
+      dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)),
+      topological_order_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
+      topological_order_loop_ends_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
+      topological_order_indexes_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
+      topological_order_loop_head_stack_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
       i_dom_list_(NULL),
       def_block_matrix_(NULL),
       temp_scoped_alloc_(),
@@ -95,13 +100,13 @@
       temp_bit_vector_size_(0u),
       temp_bit_vector_(nullptr),
       temp_gvn_(),
-      block_list_(arena, 100, kGrowableArrayBlockList),
+      block_list_(arena->Adapter(kArenaAllocBBList)),
       try_block_addr_(NULL),
       entry_block_(NULL),
       exit_block_(NULL),
       num_blocks_(0),
       current_code_item_(NULL),
-      dex_pc_to_block_map_(arena, 0, kGrowableArrayMisc),
+      dex_pc_to_block_map_(arena->Adapter()),
       m_units_(arena->Adapter()),
       method_stack_(arena->Adapter()),
       current_method_(kInvalidEntry),
@@ -116,21 +121,38 @@
       arena_(arena),
       backward_branches_(0),
       forward_branches_(0),
-      compiler_temps_(arena, 6, kGrowableArrayMisc),
       num_non_special_compiler_temps_(0),
-      max_available_non_special_compiler_temps_(0),
+      max_available_special_compiler_temps_(1),  // We only need the method ptr as a special temp for now.
+      requested_backend_temp_(false),
+      compiler_temps_committed_(false),
       punt_to_interpreter_(false),
       merged_df_flags_(0u),
-      ifield_lowering_infos_(arena, 0u),
-      sfield_lowering_infos_(arena, 0u),
-      method_lowering_infos_(arena, 0u),
-      gen_suspend_test_list_(arena, 0u) {
+      ifield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
+      sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
+      method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
+      gen_suspend_test_list_(arena->Adapter()) {
+  use_counts_.reserve(256);
+  raw_use_counts_.reserve(256);
+  block_list_.reserve(100);
   try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */);
-  max_available_special_compiler_temps_ = std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg))
-      - std::abs(static_cast<int>(kVRegTempBaseReg));
+
+
+  if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) {
+    // X86 requires a temp to keep track of the method address.
+    // TODO For x86_64, addressing can be done with RIP. When that is implemented,
+    // this needs to be updated to reserve 0 temps for BE.
+    max_available_non_special_compiler_temps_ = cu_->target64 ? 2 : 1;
+    reserved_temps_for_backend_ = max_available_non_special_compiler_temps_;
+  } else {
+    // Other architectures do not have a known lower bound for non-special temps.
+    // We allow the update of the max to happen at BE initialization stage and simply set 0 for now.
+    max_available_non_special_compiler_temps_ = 0;
+    reserved_temps_for_backend_ = 0;
+  }
 }
 
 MIRGraph::~MIRGraph() {
+  STLDeleteElements(&block_list_);
   STLDeleteElements(&m_units_);
 }
 
@@ -165,50 +187,54 @@
   if (insn == NULL) {
     LOG(FATAL) << "Break split failed";
   }
-  BasicBlock* bottom_block = NewMemBB(kDalvikByteCode, num_blocks_++);
-  block_list_.Insert(bottom_block);
+  BasicBlock* bottom_block = CreateNewBB(kDalvikByteCode);
 
   bottom_block->start_offset = code_offset;
   bottom_block->first_mir_insn = insn;
   bottom_block->last_mir_insn = orig_block->last_mir_insn;
 
-  /* If this block was terminated by a return, the flag needs to go with the bottom block */
+  /* If this block was terminated by a return, conditional branch or throw,
+   * the flag needs to go with the bottom block
+   */
   bottom_block->terminated_by_return = orig_block->terminated_by_return;
   orig_block->terminated_by_return = false;
 
+  bottom_block->conditional_branch = orig_block->conditional_branch;
+  orig_block->conditional_branch = false;
+
+  bottom_block->explicit_throw = orig_block->explicit_throw;
+  orig_block->explicit_throw = false;
+
   /* Handle the taken path */
   bottom_block->taken = orig_block->taken;
   if (bottom_block->taken != NullBasicBlockId) {
     orig_block->taken = NullBasicBlockId;
     BasicBlock* bb_taken = GetBasicBlock(bottom_block->taken);
-    bb_taken->predecessors->Delete(orig_block->id);
-    bb_taken->predecessors->Insert(bottom_block->id);
+    bb_taken->ErasePredecessor(orig_block->id);
+    bb_taken->predecessors.push_back(bottom_block->id);
   }
 
   /* Handle the fallthrough path */
   bottom_block->fall_through = orig_block->fall_through;
   orig_block->fall_through = bottom_block->id;
-  bottom_block->predecessors->Insert(orig_block->id);
+  bottom_block->predecessors.push_back(orig_block->id);
   if (bottom_block->fall_through != NullBasicBlockId) {
     BasicBlock* bb_fall_through = GetBasicBlock(bottom_block->fall_through);
-    bb_fall_through->predecessors->Delete(orig_block->id);
-    bb_fall_through->predecessors->Insert(bottom_block->id);
+    bb_fall_through->ErasePredecessor(orig_block->id);
+    bb_fall_through->predecessors.push_back(bottom_block->id);
   }
 
   /* Handle the successor list */
   if (orig_block->successor_block_list_type != kNotUsed) {
     bottom_block->successor_block_list_type = orig_block->successor_block_list_type;
-    bottom_block->successor_blocks = orig_block->successor_blocks;
+    bottom_block->successor_blocks.swap(orig_block->successor_blocks);
     orig_block->successor_block_list_type = kNotUsed;
-    orig_block->successor_blocks = nullptr;
-    GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_blocks);
-    while (true) {
-      SuccessorBlockInfo* successor_block_info = iterator.Next();
-      if (successor_block_info == nullptr) break;
+    DCHECK(orig_block->successor_blocks.empty());  // Empty after the swap() above.
+    for (SuccessorBlockInfo* successor_block_info : bottom_block->successor_blocks) {
       BasicBlock* bb = GetBasicBlock(successor_block_info->block);
       if (bb != nullptr) {
-        bb->predecessors->Delete(orig_block->id);
-        bb->predecessors->Insert(bottom_block->id);
+        bb->ErasePredecessor(orig_block->id);
+        bb->predecessors.push_back(bottom_block->id);
       }
     }
   }
@@ -232,9 +258,9 @@
   DCHECK_EQ(insn->offset, bottom_block->start_offset);
   DCHECK(static_cast<int>(insn->dalvikInsn.opcode) == kMirOpCheck ||
          !MIR::DecodedInstruction::IsPseudoMirOp(insn->dalvikInsn.opcode));
-  DCHECK_EQ(dex_pc_to_block_map_.Get(insn->offset), orig_block->id);
+  DCHECK_EQ(dex_pc_to_block_map_[insn->offset], orig_block->id);
   MIR* p = insn;
-  dex_pc_to_block_map_.Put(p->offset, bottom_block->id);
+  dex_pc_to_block_map_[p->offset] = bottom_block->id;
   while (p != bottom_block->last_mir_insn) {
     p = p->next;
     DCHECK(p != nullptr);
@@ -247,8 +273,8 @@
      * the first in a BasicBlock, we can't hit it here.
      */
     if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
-      DCHECK_EQ(dex_pc_to_block_map_.Get(p->offset), orig_block->id);
-      dex_pc_to_block_map_.Put(p->offset, bottom_block->id);
+      DCHECK_EQ(dex_pc_to_block_map_[p->offset], orig_block->id);
+      dex_pc_to_block_map_[p->offset] = bottom_block->id;
     }
   }
 
@@ -265,12 +291,12 @@
  */
 BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool split, bool create,
                                 BasicBlock** immed_pred_block_p) {
-  if (code_offset >= cu_->code_item->insns_size_in_code_units_) {
+  if (code_offset >= current_code_item_->insns_size_in_code_units_) {
     return NULL;
   }
 
-  int block_id = dex_pc_to_block_map_.Get(code_offset);
-  BasicBlock* bb = (block_id == 0) ? NULL : block_list_.Get(block_id);
+  int block_id = dex_pc_to_block_map_[code_offset];
+  BasicBlock* bb = GetBasicBlock(block_id);
 
   if ((bb != NULL) && (bb->start_offset == code_offset)) {
     // Does this containing block start with the desired instruction?
@@ -288,10 +314,9 @@
   }
 
   // Create a new block.
-  bb = NewMemBB(kDalvikByteCode, num_blocks_++);
-  block_list_.Insert(bb);
+  bb = CreateNewBB(kDalvikByteCode);
   bb->start_offset = code_offset;
-  dex_pc_to_block_map_.Put(bb->start_offset, bb->id);
+  dex_pc_to_block_map_[bb->start_offset] = bb->id;
   return bb;
 }
 
@@ -339,10 +364,10 @@
   // (We don't want to ignore all monitor-exit catches since one could enclose a synchronized
   // block in a try-block and catch the NPE, Error or Throwable and we should let it through;
   // even though a throwing monitor-exit certainly indicates a bytecode error.)
-  const Instruction* monitor_exit = Instruction::At(cu_->code_item->insns_ + monitor_exit_offset);
+  const Instruction* monitor_exit = Instruction::At(current_code_item_->insns_ + monitor_exit_offset);
   DCHECK(monitor_exit->Opcode() == Instruction::MONITOR_EXIT);
   int monitor_reg = monitor_exit->VRegA_11x();
-  const Instruction* check_insn = Instruction::At(cu_->code_item->insns_ + catch_offset);
+  const Instruction* check_insn = Instruction::At(current_code_item_->insns_ + catch_offset);
   DCHECK(check_insn->Opcode() == Instruction::MOVE_EXCEPTION);
   if (check_insn->VRegA_11x() == monitor_reg) {
     // Unexpected move-exception to the same register. Probably not the pattern we're looking for.
@@ -431,7 +456,7 @@
   BasicBlock* taken_block = FindBlock(target, /* split */ true, /* create */ true,
                                       /* immed_pred_block_p */ &cur_block);
   cur_block->taken = taken_block->id;
-  taken_block->predecessors->Insert(cur_block->id);
+  taken_block->predecessors.push_back(cur_block->id);
 
   /* Always terminate the current block for conditional branches */
   if (flags & Instruction::kContinue) {
@@ -454,7 +479,7 @@
                                              /* immed_pred_block_p */
                                              &cur_block);
     cur_block->fall_through = fallthrough_block->id;
-    fallthrough_block->predecessors->Insert(cur_block->id);
+    fallthrough_block->predecessors.push_back(cur_block->id);
   } else if (code_ptr < code_end) {
     FindBlock(cur_offset + width, /* split */ false, /* create */ true,
                 /* immed_pred_block_p */ NULL);
@@ -513,8 +538,7 @@
   }
   cur_block->successor_block_list_type =
       (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?  kPackedSwitch : kSparseSwitch;
-  cur_block->successor_blocks =
-      new (arena_) GrowableArray<SuccessorBlockInfo*>(arena_, size, kGrowableArraySuccessorBlocks);
+  cur_block->successor_blocks.reserve(size);
 
   for (i = 0; i < size; i++) {
     BasicBlock* case_block = FindBlock(cur_offset + target_table[i], /* split */ true,
@@ -526,15 +550,15 @@
     successor_block_info->key =
         (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
         first_key + i : keyTable[i];
-    cur_block->successor_blocks->Insert(successor_block_info);
-    case_block->predecessors->Insert(cur_block->id);
+    cur_block->successor_blocks.push_back(successor_block_info);
+    case_block->predecessors.push_back(cur_block->id);
   }
 
   /* Fall-through case */
   BasicBlock* fallthrough_block = FindBlock(cur_offset +  width, /* split */ false,
                                             /* create */ true, /* immed_pred_block_p */ NULL);
   cur_block->fall_through = fallthrough_block->id;
-  fallthrough_block->predecessors->Insert(cur_block->id);
+  fallthrough_block->predecessors.push_back(cur_block->id);
   return cur_block;
 }
 
@@ -567,8 +591,6 @@
       }
       if (cur_block->successor_block_list_type == kNotUsed) {
         cur_block->successor_block_list_type = kCatch;
-        cur_block->successor_blocks = new (arena_) GrowableArray<SuccessorBlockInfo*>(
-            arena_, 2, kGrowableArraySuccessorBlocks);
       }
       catch_block->catch_entry = true;
       if (kIsDebugBuild) {
@@ -578,17 +600,16 @@
           (arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
       successor_block_info->block = catch_block->id;
       successor_block_info->key = iterator.GetHandlerTypeIndex();
-      cur_block->successor_blocks->Insert(successor_block_info);
-      catch_block->predecessors->Insert(cur_block->id);
+      cur_block->successor_blocks.push_back(successor_block_info);
+      catch_block->predecessors.push_back(cur_block->id);
     }
     in_try_block = (cur_block->successor_block_list_type != kNotUsed);
   }
   if (!in_try_block && build_all_edges) {
-    BasicBlock* eh_block = NewMemBB(kExceptionHandling, num_blocks_++);
+    BasicBlock* eh_block = CreateNewBB(kExceptionHandling);
     cur_block->taken = eh_block->id;
-    block_list_.Insert(eh_block);
     eh_block->start_offset = cur_offset;
-    eh_block->predecessors->Insert(cur_block->id);
+    eh_block->predecessors.push_back(cur_block->id);
   }
 
   if (is_throw) {
@@ -631,11 +652,10 @@
    * Note also that the dex_pc_to_block_map_ entry for the potentially
    * throwing instruction will refer to the original basic block.
    */
-  BasicBlock* new_block = NewMemBB(kDalvikByteCode, num_blocks_++);
-  block_list_.Insert(new_block);
+  BasicBlock* new_block = CreateNewBB(kDalvikByteCode);
   new_block->start_offset = insn->offset;
   cur_block->fall_through = new_block->id;
-  new_block->predecessors->Insert(cur_block->id);
+  new_block->predecessors.push_back(cur_block->id);
   MIR* new_insn = NewMIR();
   *new_insn = *insn;
   insn->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpCheck);
@@ -663,8 +683,8 @@
 
   // TODO: need to rework expansion of block list & try_block_addr when inlining activated.
   // TUNING: use better estimate of basic blocks for following resize.
-  block_list_.Resize(block_list_.Size() + current_code_item_->insns_size_in_code_units_);
-  dex_pc_to_block_map_.SetSize(dex_pc_to_block_map_.Size() + current_code_item_->insns_size_in_code_units_);
+  block_list_.reserve(block_list_.size() + current_code_item_->insns_size_in_code_units_);
+  dex_pc_to_block_map_.resize(dex_pc_to_block_map_.size() + current_code_item_->insns_size_in_code_units_);
 
   // TODO: replace with explicit resize routine.  Using automatic extension side effect for now.
   try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_);
@@ -676,14 +696,11 @@
     DCHECK(exit_block_ == NULL);
     DCHECK_EQ(num_blocks_, 0U);
     // Use id 0 to represent a null block.
-    BasicBlock* null_block = NewMemBB(kNullBlock, num_blocks_++);
+    BasicBlock* null_block = CreateNewBB(kNullBlock);
     DCHECK_EQ(null_block->id, NullBasicBlockId);
     null_block->hidden = true;
-    block_list_.Insert(null_block);
-    entry_block_ = NewMemBB(kEntryBlock, num_blocks_++);
-    block_list_.Insert(entry_block_);
-    exit_block_ = NewMemBB(kExitBlock, num_blocks_++);
-    block_list_.Insert(exit_block_);
+    entry_block_ = CreateNewBB(kEntryBlock);
+    exit_block_ = CreateNewBB(kExitBlock);
     // TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated.
     cu_->dex_file = &dex_file;
     cu_->class_def_idx = class_def_idx;
@@ -691,12 +708,6 @@
     cu_->access_flags = access_flags;
     cu_->invoke_type = invoke_type;
     cu_->shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
-    cu_->num_ins = current_code_item_->ins_size_;
-    cu_->num_regs = current_code_item_->registers_size_ - cu_->num_ins;
-    cu_->num_outs = current_code_item_->outs_size_;
-    cu_->num_dalvik_registers = current_code_item_->registers_size_;
-    cu_->insns = current_code_item_->insns_;
-    cu_->code_item = current_code_item_;
   } else {
     UNIMPLEMENTED(FATAL) << "Nested inlining not implemented.";
     /*
@@ -706,13 +717,12 @@
   }
 
   /* Current block to record parsed instructions */
-  BasicBlock* cur_block = NewMemBB(kDalvikByteCode, num_blocks_++);
+  BasicBlock* cur_block = CreateNewBB(kDalvikByteCode);
   DCHECK_EQ(current_offset_, 0U);
   cur_block->start_offset = current_offset_;
-  block_list_.Insert(cur_block);
   // TODO: for inlining support, insert at the insert point rather than entry block.
   entry_block_->fall_through = cur_block->id;
-  cur_block->predecessors->Insert(entry_block_->id);
+  cur_block->predecessors.push_back(entry_block_->id);
 
   /* Identify code range in try blocks and set up the empty catch blocks */
   ProcessTryCatchBlocks();
@@ -730,7 +740,7 @@
       opcode_count_[static_cast<int>(opcode)]++;
     }
 
-    int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode);
+    int flags = insn->dalvikInsn.FlagsOf();
     int verify_flags = Instruction::VerifyFlagsOf(insn->dalvikInsn.opcode);
 
     uint64_t df_flags = GetDataFlowAttributes(insn);
@@ -770,7 +780,7 @@
     }
 
     // Associate the starting dex_pc for this opcode with its containing basic block.
-    dex_pc_to_block_map_.Put(insn->offset, cur_block->id);
+    dex_pc_to_block_map_[insn->offset] = cur_block->id;
 
     code_ptr += width;
 
@@ -780,7 +790,7 @@
     } else if (flags & Instruction::kReturn) {
       cur_block->terminated_by_return = true;
       cur_block->fall_through = exit_block_->id;
-      exit_block_->predecessors->Insert(cur_block->id);
+      exit_block_->predecessors.push_back(cur_block->id);
       /*
        * Terminate the current block if there are instructions
        * afterwards.
@@ -829,7 +839,7 @@
 
       if ((cur_block->fall_through == NullBasicBlockId) && (flags & Instruction::kContinue)) {
         cur_block->fall_through = next_block->id;
-        next_block->predecessors->Insert(cur_block->id);
+        next_block->predecessors.push_back(cur_block->id);
       }
       cur_block = next_block;
     }
@@ -894,7 +904,7 @@
   int idx;
 
   for (idx = 0; idx < num_blocks; idx++) {
-    int block_idx = all_blocks ? idx : dfs_order_->Get(idx);
+    int block_idx = all_blocks ? idx : dfs_order_[idx];
     BasicBlock* bb = GetBasicBlock(block_idx);
     if (bb == NULL) continue;
     if (bb->block_type == kDead) continue;
@@ -911,27 +921,7 @@
                 bb->first_mir_insn ? " | " : " ");
         for (mir = bb->first_mir_insn; mir; mir = mir->next) {
             int opcode = mir->dalvikInsn.opcode;
-            if (opcode > kMirOpSelect && opcode < kMirOpLast) {
-              if (opcode == kMirOpConstVector) {
-                fprintf(file, "    {%04x %s %d %d %d %d %d %d\\l}%s\\\n", mir->offset,
-                        extended_mir_op_names_[kMirOpConstVector - kMirOpFirst],
-                        mir->dalvikInsn.vA,
-                        mir->dalvikInsn.vB,
-                        mir->dalvikInsn.arg[0],
-                        mir->dalvikInsn.arg[1],
-                        mir->dalvikInsn.arg[2],
-                        mir->dalvikInsn.arg[3],
-                        mir->next ? " | " : " ");
-              } else {
-                fprintf(file, "    {%04x %s %d %d %d\\l}%s\\\n", mir->offset,
-                        extended_mir_op_names_[opcode - kMirOpFirst],
-                        mir->dalvikInsn.vA,
-                        mir->dalvikInsn.vB,
-                        mir->dalvikInsn.vC,
-                        mir->next ? " | " : " ");
-              }
-            } else {
-              fprintf(file, "    {%04x %s %s %s %s\\l}%s\\\n", mir->offset,
+            fprintf(file, "    {%04x %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset,
                       mir->ssa_rep ? GetDalvikDisassembly(mir) :
                       !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ?
                         Instruction::Name(mir->dalvikInsn.opcode) :
@@ -939,8 +929,10 @@
                       (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ",
                       (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ",
                       (mir->optimization_flags & MIR_IGNORE_SUSPEND_CHECK) != 0 ? " no_suspendcheck" : " ",
+                      (mir->optimization_flags & MIR_STORE_NON_TEMPORAL) != 0 ? " non_temporal" : " ",
+                      (mir->optimization_flags & MIR_CALLEE) != 0 ? " inlined" : " ",
+                      (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0 ? " no_clinit" : " ",
                       mir->next ? " | " : " ");
-            }
         }
         fprintf(file, "  }\"];\n\n");
     } else if (bb->block_type == kExceptionHandling) {
@@ -968,23 +960,17 @@
       fprintf(file, "  succ%04x_%d [shape=%s,label = \"{ \\\n",
               bb->start_offset, bb->id,
               (bb->successor_block_list_type == kCatch) ?  "Mrecord" : "record");
-      GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
-      SuccessorBlockInfo* successor_block_info = iterator.Next();
 
+      int last_succ_id = static_cast<int>(bb->successor_blocks.size() - 1u);
       int succ_id = 0;
-      while (true) {
-        if (successor_block_info == NULL) break;
-
+      for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
         BasicBlock* dest_block = GetBasicBlock(successor_block_info->block);
-        SuccessorBlockInfo *next_successor_block_info = iterator.Next();
-
         fprintf(file, "    {<f%d> %04x: %04x\\l}%s\\\n",
-                succ_id++,
+                succ_id,
                 successor_block_info->key,
                 dest_block->start_offset,
-                (next_successor_block_info != NULL) ? " | " : " ");
-
-        successor_block_info = next_successor_block_info;
+                (succ_id != last_succ_id) ? " | " : " ");
+        ++succ_id;
       }
       fprintf(file, "  }\"];\n\n");
 
@@ -993,13 +979,8 @@
               block_name1, bb->start_offset, bb->id);
 
       // Link the successor pseudo-block with all of its potential targets.
-      GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_blocks);
-
       succ_id = 0;
-      while (true) {
-        SuccessorBlockInfo* successor_block_info = iter.Next();
-        if (successor_block_info == NULL) break;
-
+      for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
         BasicBlock* dest_block = GetBasicBlock(successor_block_info->block);
 
         GetBlockName(dest_block, block_name2);
@@ -1172,7 +1153,7 @@
   }
 
   // Remove the BB information and also find the after_list.
-  for (MIR* mir = first_list_mir; mir != last_list_mir; mir = mir->next) {
+  for (MIR* mir = first_list_mir; mir != last_list_mir->next; mir = mir->next) {
     mir->bb = NullBasicBlockId;
   }
 
@@ -1210,6 +1191,198 @@
   return next_mir;
 }
 
+static void FillTypeSizeString(uint32_t type_size, std::string* decoded_mir) {
+  DCHECK(decoded_mir != nullptr);
+  OpSize type = static_cast<OpSize>(type_size >> 16);
+  uint16_t vect_size = (type_size & 0xFFFF);
+
+  // Now print the type and vector size.
+  std::stringstream ss;
+  ss << " (type:";
+  ss << type;
+  ss << " vectsize:";
+  ss << vect_size;
+  ss << ")";
+
+  decoded_mir->append(ss.str());
+}
+
+void MIRGraph::DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir) {
+  DCHECK(decoded_mir != nullptr);
+  int opcode = mir->dalvikInsn.opcode;
+  SSARepresentation* ssa_rep = mir->ssa_rep;
+  int defs = (ssa_rep != nullptr) ? ssa_rep->num_defs : 0;
+  int uses = (ssa_rep != nullptr) ? ssa_rep->num_uses : 0;
+
+  decoded_mir->append(extended_mir_op_names_[opcode - kMirOpFirst]);
+
+  switch (opcode) {
+    case kMirOpPhi: {
+      if (defs > 0 && uses > 0) {
+        BasicBlockId* incoming = mir->meta.phi_incoming;
+        decoded_mir->append(StringPrintf(" %s = (%s",
+                           GetSSANameWithConst(ssa_rep->defs[0], true).c_str(),
+                           GetSSANameWithConst(ssa_rep->uses[0], true).c_str()));
+        decoded_mir->append(StringPrintf(":%d", incoming[0]));
+        for (int i = 1; i < uses; i++) {
+          decoded_mir->append(StringPrintf(", %s:%d", GetSSANameWithConst(ssa_rep->uses[i], true).c_str(), incoming[i]));
+        }
+        decoded_mir->append(")");
+      }
+      break;
+    }
+    case kMirOpCopy:
+      if (ssa_rep != nullptr) {
+        decoded_mir->append(" ");
+        decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+        if (defs > 1) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+        }
+        decoded_mir->append(" = ");
+        decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false));
+        if (uses > 1) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false));
+        }
+      } else {
+        decoded_mir->append(StringPrintf(" v%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      }
+      break;
+    case kMirOpFusedCmplFloat:
+    case kMirOpFusedCmpgFloat:
+    case kMirOpFusedCmplDouble:
+    case kMirOpFusedCmpgDouble:
+    case kMirOpFusedCmpLong:
+      if (ssa_rep != nullptr) {
+        decoded_mir->append(" ");
+        decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false));
+        for (int i = 1; i < uses; i++) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false));
+        }
+      } else {
+        decoded_mir->append(StringPrintf(" v%d, v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      }
+      break;
+    case kMirOpMoveVector:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedAddition:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d + vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedMultiply:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d * vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedSubtract:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedAnd:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d & vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedOr:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d \\| vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedXor:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d ^ vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedShiftLeft:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d \\<\\< %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedUnsignedShiftRight:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedSignedShiftRight:
+      decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpConstVector:
+      decoded_mir->append(StringPrintf(" vect%d = %x, %x, %x, %x", mir->dalvikInsn.vA, mir->dalvikInsn.arg[0],
+                                      mir->dalvikInsn.arg[1], mir->dalvikInsn.arg[2], mir->dalvikInsn.arg[3]));
+      break;
+    case kMirOpPackedSet:
+      if (ssa_rep != nullptr) {
+        decoded_mir->append(StringPrintf(" vect%d = %s", mir->dalvikInsn.vA,
+              GetSSANameWithConst(ssa_rep->uses[0], false).c_str()));
+        if (uses > 1) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false));
+        }
+      } else {
+        decoded_mir->append(StringPrintf(" vect%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      }
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedAddReduce:
+      if (ssa_rep != nullptr) {
+        decoded_mir->append(" ");
+        decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+        if (defs > 1) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+        }
+        decoded_mir->append(StringPrintf(" = vect%d + %s", mir->dalvikInsn.vB,
+            GetSSANameWithConst(ssa_rep->uses[0], false).c_str()));
+        if (uses > 1) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false));
+        }
+      } else {
+        decoded_mir->append(StringPrintf("v%d = vect%d + v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB, mir->dalvikInsn.vA));
+      }
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpPackedReduce:
+      if (ssa_rep != nullptr) {
+        decoded_mir->append(" ");
+        decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+        if (defs > 1) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+        }
+        decoded_mir->append(StringPrintf(" = vect%d", mir->dalvikInsn.vB));
+      } else {
+        decoded_mir->append(StringPrintf(" v%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      }
+      FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+      break;
+    case kMirOpReserveVectorRegisters:
+    case kMirOpReturnVectorRegisters:
+      decoded_mir->append(StringPrintf(" vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+      break;
+    case kMirOpMemBarrier: {
+      decoded_mir->append(" type:");
+      std::stringstream ss;
+      ss << static_cast<MemBarrierKind>(mir->dalvikInsn.vA);
+      decoded_mir->append(ss.str());
+      break;
+    }
+    case kMirOpPackedArrayGet:
+    case kMirOpPackedArrayPut:
+      decoded_mir->append(StringPrintf(" vect%d", mir->dalvikInsn.vA));
+      if (ssa_rep != nullptr) {
+        decoded_mir->append(StringPrintf(", %s[%s]",
+                                        GetSSANameWithConst(ssa_rep->uses[0], false).c_str(),
+                                        GetSSANameWithConst(ssa_rep->uses[1], false).c_str()));
+      } else {
+        decoded_mir->append(StringPrintf(", v%d[v%d]", mir->dalvikInsn.vB, mir->dalvikInsn.vC));
+      }
+      FillTypeSizeString(mir->dalvikInsn.arg[0], decoded_mir);
+      break;
+    default:
+      break;
+  }
+}
+
 char* MIRGraph::GetDalvikDisassembly(const MIR* mir) {
   MIR::DecodedInstruction insn = mir->dalvikInsn;
   std::string str;
@@ -1219,120 +1392,114 @@
   bool nop = false;
   SSARepresentation* ssa_rep = mir->ssa_rep;
   Instruction::Format dalvik_format = Instruction::k10x;  // Default to no-operand format.
-  int defs = (ssa_rep != NULL) ? ssa_rep->num_defs : 0;
-  int uses = (ssa_rep != NULL) ? ssa_rep->num_uses : 0;
 
-  // Handle special cases.
+  // Handle special cases that recover the original dalvik instruction.
   if ((opcode == kMirOpCheck) || (opcode == kMirOpCheckPart2)) {
     str.append(extended_mir_op_names_[opcode - kMirOpFirst]);
     str.append(": ");
     // Recover the original Dex instruction.
     insn = mir->meta.throw_insn->dalvikInsn;
     ssa_rep = mir->meta.throw_insn->ssa_rep;
-    defs = ssa_rep->num_defs;
-    uses = ssa_rep->num_uses;
     opcode = insn.opcode;
   } else if (opcode == kMirOpNop) {
     str.append("[");
-    // Recover original opcode.
-    insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode();
-    opcode = insn.opcode;
+    if (mir->offset < current_code_item_->insns_size_in_code_units_) {
+      // Recover original opcode.
+      insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode();
+      opcode = insn.opcode;
+    }
     nop = true;
   }
+  int defs = (ssa_rep != NULL) ? ssa_rep->num_defs : 0;
+  int uses = (ssa_rep != NULL) ? ssa_rep->num_uses : 0;
 
   if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
-    str.append(extended_mir_op_names_[opcode - kMirOpFirst]);
+    // Note that this does not check the MIR's opcode in all cases. In cases where it
+    // recovered dalvik instruction, it uses opcode of that instead of the extended one.
+    DisassembleExtendedInstr(mir, &str);
   } else {
     dalvik_format = Instruction::FormatOf(insn.opcode);
-    flags = Instruction::FlagsOf(insn.opcode);
+    flags = insn.FlagsOf();
     str.append(Instruction::Name(insn.opcode));
-  }
 
-  if (opcode == kMirOpPhi) {
-    BasicBlockId* incoming = mir->meta.phi_incoming;
-    str.append(StringPrintf(" %s = (%s",
-               GetSSANameWithConst(ssa_rep->defs[0], true).c_str(),
-               GetSSANameWithConst(ssa_rep->uses[0], true).c_str()));
-    str.append(StringPrintf(":%d", incoming[0]));
-    int i;
-    for (i = 1; i < uses; i++) {
-      str.append(StringPrintf(", %s:%d",
-                              GetSSANameWithConst(ssa_rep->uses[i], true).c_str(),
-                              incoming[i]));
-    }
-    str.append(")");
-  } else if ((flags & Instruction::kBranch) != 0) {
-    // For branches, decode the instructions to print out the branch targets.
-    int offset = 0;
-    switch (dalvik_format) {
-      case Instruction::k21t:
-        str.append(StringPrintf(" %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str()));
-        offset = insn.vB;
-        break;
-      case Instruction::k22t:
-        str.append(StringPrintf(" %s, %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str(),
-                   GetSSANameWithConst(ssa_rep->uses[1], false).c_str()));
-        offset = insn.vC;
-        break;
-      case Instruction::k10t:
-      case Instruction::k20t:
-      case Instruction::k30t:
-        offset = insn.vA;
-        break;
-      default:
-        LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode;
-    }
-    str.append(StringPrintf(" 0x%x (%c%x)", mir->offset + offset,
-                            offset > 0 ? '+' : '-', offset > 0 ? offset : -offset));
-  } else {
     // For invokes-style formats, treat wide regs as a pair of singles.
     bool show_singles = ((dalvik_format == Instruction::k35c) ||
                          (dalvik_format == Instruction::k3rc));
     if (defs != 0) {
-      str.append(StringPrintf(" %s", GetSSANameWithConst(ssa_rep->defs[0], false).c_str()));
+      str.append(" ");
+      str.append(GetSSANameWithConst(ssa_rep->defs[0], false));
+      if (defs > 1) {
+        str.append(", ");
+        str.append(GetSSANameWithConst(ssa_rep->defs[1], false));
+      }
       if (uses != 0) {
         str.append(", ");
       }
     }
     for (int i = 0; i < uses; i++) {
-      str.append(
-          StringPrintf(" %s", GetSSANameWithConst(ssa_rep->uses[i], show_singles).c_str()));
+      str.append(" ");
+      str.append(GetSSANameWithConst(ssa_rep->uses[i], show_singles));
       if (!show_singles && (reg_location_ != NULL) && reg_location_[i].wide) {
         // For the listing, skip the high sreg.
         i++;
       }
-      if (i != (uses -1)) {
+      if (i != (uses - 1)) {
         str.append(",");
       }
     }
+
     switch (dalvik_format) {
       case Instruction::k11n:  // Add one immediate from vB.
       case Instruction::k21s:
       case Instruction::k31i:
       case Instruction::k21h:
-        str.append(StringPrintf(", #%d", insn.vB));
+        str.append(StringPrintf(", #0x%x", insn.vB));
         break;
       case Instruction::k51l:  // Add one wide immediate.
         str.append(StringPrintf(", #%" PRId64, insn.vB_wide));
         break;
       case Instruction::k21c:  // One register, one string/type/method index.
       case Instruction::k31c:
-        str.append(StringPrintf(", index #%d", insn.vB));
+        str.append(StringPrintf(", index #0x%x", insn.vB));
         break;
       case Instruction::k22c:  // Two registers, one string/type/method index.
-        str.append(StringPrintf(", index #%d", insn.vC));
+        str.append(StringPrintf(", index #0x%x", insn.vC));
         break;
       case Instruction::k22s:  // Add one immediate from vC.
       case Instruction::k22b:
-        str.append(StringPrintf(", #%d", insn.vC));
+        str.append(StringPrintf(", #0x%x", insn.vC));
         break;
-      default: {
+      default:
         // Nothing left to print.
-      }
+        break;
     }
-  }
-  if (nop) {
-    str.append("]--optimized away");
+
+    if ((flags & Instruction::kBranch) != 0) {
+      // For branches, decode the instructions to print out the branch targets.
+      int offset = 0;
+      switch (dalvik_format) {
+        case Instruction::k21t:
+          offset = insn.vB;
+          break;
+        case Instruction::k22t:
+          offset = insn.vC;
+          break;
+        case Instruction::k10t:
+        case Instruction::k20t:
+        case Instruction::k30t:
+          offset = insn.vA;
+          break;
+        default:
+          LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode;
+          break;
+      }
+      str.append(StringPrintf(", 0x%x (%c%x)", mir->offset + offset,
+                              offset > 0 ? '+' : '-', offset > 0 ? offset : -offset));
+    }
+
+    if (nop) {
+      str.append("]--optimized away");
+    }
   }
   int length = str.length() + 1;
   ret = static_cast<char*>(arena_->Alloc(length, kArenaAllocDFInfo));
@@ -1355,7 +1522,12 @@
   // TODO: This value is needed for LLVM and debugging. Currently, we compute this and then copy to
   //       the arena. We should be smarter and just place straight into the arena, or compute the
   //       value more lazily.
-  return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+  int vreg = SRegToVReg(ssa_reg);
+  if (vreg >= static_cast<int>(GetFirstTempVR())) {
+    return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+  } else {
+    return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+  }
 }
 
 // Similar to GetSSAName, but if ssa name represents an immediate show that as well.
@@ -1373,7 +1545,12 @@
                           ConstantValue(reg_location_[ssa_reg]));
     }
   } else {
-    return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+    int vreg = SRegToVReg(ssa_reg);
+    if (vreg >= static_cast<int>(GetFirstTempVR())) {
+      return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+    } else {
+      return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+    }
   }
 }
 
@@ -1406,7 +1583,6 @@
 
 /* Debug Utility - dump a compilation unit */
 void MIRGraph::DumpMIRGraph() {
-  BasicBlock* bb;
   const char* block_type_names[] = {
     "Null Block",
     "Entry Block",
@@ -1417,13 +1593,10 @@
   };
 
   LOG(INFO) << "Compiling " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
-  LOG(INFO) << cu_->insns << " insns";
+  LOG(INFO) << GetInsns(0) << " insns";
   LOG(INFO) << GetNumBlocks() << " blocks in total";
-  GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
 
-  while (true) {
-    bb = iterator.Next();
-    if (bb == NULL) break;
+  for (BasicBlock* bb : block_list_) {
     LOG(INFO) << StringPrintf("Block %d (%s) (insn %04x - %04x%s)",
         bb->id,
         block_type_names[bb->block_type],
@@ -1481,15 +1654,10 @@
 
 // Allocate a new basic block.
 BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) {
-  BasicBlock* bb = new (arena_) BasicBlock();
+  BasicBlock* bb = new (arena_) BasicBlock(block_id, block_type, arena_);
 
-  bb->block_type = block_type;
-  bb->id = block_id;
   // TUNING: better estimate of the exit block predecessors?
-  bb->predecessors = new (arena_) GrowableArray<BasicBlockId>(arena_,
-                                                             (block_type == kExitBlock) ? 2048 : 2,
-                                                             kGrowableArrayPredecessors);
-  bb->successor_block_list_type = kNotUsed;
+  bb->predecessors.reserve((block_type == kExitBlock) ? 2048 : 2);
   block_id_map_.Put(block_id, block_id);
   return bb;
 }
@@ -1502,24 +1670,20 @@
 void MIRGraph::InitializeMethodUses() {
   // The gate starts by initializing the use counts.
   int num_ssa_regs = GetNumSSARegs();
-  use_counts_.Resize(num_ssa_regs + 32);
-  raw_use_counts_.Resize(num_ssa_regs + 32);
-  // Initialize list.
-  for (int i = 0; i < num_ssa_regs; i++) {
-    use_counts_.Insert(0);
-    raw_use_counts_.Insert(0);
-  }
+  use_counts_.clear();
+  use_counts_.reserve(num_ssa_regs + 32);
+  use_counts_.resize(num_ssa_regs, 0u);
+  raw_use_counts_.clear();
+  raw_use_counts_.reserve(num_ssa_regs + 32);
+  raw_use_counts_.resize(num_ssa_regs, 0u);
 }
 
 void MIRGraph::SSATransformationStart() {
   DCHECK(temp_scoped_alloc_.get() == nullptr);
   temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
-  temp_bit_vector_size_ = cu_->num_dalvik_registers;
+  temp_bit_vector_size_ = GetNumOfCodeAndTempVRs();
   temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
       temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapRegisterV);
-
-  // Update the maximum number of reachable blocks.
-  max_num_reachable_blocks_ = num_reachable_blocks_;
 }
 
 void MIRGraph::SSATransformationEnd() {
@@ -1532,6 +1696,36 @@
   temp_bit_vector_ = nullptr;
   DCHECK(temp_scoped_alloc_.get() != nullptr);
   temp_scoped_alloc_.reset();
+
+  // Update the maximum number of reachable blocks.
+  max_num_reachable_blocks_ = num_reachable_blocks_;
+}
+
+size_t MIRGraph::GetNumDalvikInsns() const {
+  size_t cumulative_size = 0u;
+  bool counted_current_item = false;
+  const uint8_t size_for_null_code_item = 2u;
+
+  for (auto it : m_units_) {
+    const DexFile::CodeItem* code_item = it->GetCodeItem();
+    // Even if the code item is null, we still count non-zero value so that
+    // each m_unit is counted as having impact.
+    cumulative_size += (code_item == nullptr ?
+        size_for_null_code_item : code_item->insns_size_in_code_units_);
+    if (code_item == current_code_item_) {
+      counted_current_item = true;
+    }
+  }
+
+  // If the current code item was not counted yet, count it now.
+  // This can happen for example in unit tests where some fields like m_units_
+  // are not initialized.
+  if (counted_current_item == false) {
+    cumulative_size += (current_code_item_ == nullptr ?
+        size_for_null_code_item : current_code_item_->insns_size_in_code_units_);
+  }
+
+  return cumulative_size;
 }
 
 static BasicBlock* SelectTopologicalSortOrderFallBack(
@@ -1600,9 +1794,9 @@
     tmp_stack->pop_back();
     BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id);
     DCHECK(current_bb != nullptr);
-    GrowableArray<BasicBlockId>::Iterator iter(current_bb->predecessors);
-    BasicBlock* pred_bb = mir_graph->GetBasicBlock(iter.Next());
-    for ( ; pred_bb != nullptr; pred_bb = mir_graph->GetBasicBlock(iter.Next())) {
+    for (BasicBlockId pred_id : current_bb->predecessors) {
+      BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id);
+      DCHECK(pred_bb != nullptr);
       if (!pred_bb->visited && !reachable->IsBitSet(pred_bb->id)) {
         reachable->SetBit(pred_bb->id);
         tmp_stack->push_back(pred_bb->id);
@@ -1623,36 +1817,27 @@
   loop_exit_blocks.ClearAllBits();
 
   // Count the number of blocks to process and add the entry block(s).
-  GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
   unsigned int num_blocks_to_process = 0u;
-  for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) {
+  for (BasicBlock* bb : block_list_) {
     if (bb->hidden == true) {
       continue;
     }
 
     num_blocks_to_process += 1u;
 
-    if (bb->predecessors->Size() == 0u) {
+    if (bb->predecessors.size() == 0u) {
       // Add entry block to the queue.
       q.push(bb);
     }
   }
 
-  // Create the topological order if need be.
-  if (topological_order_ == nullptr) {
-    topological_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, num_blocks);
-    topological_order_loop_ends_ = new (arena_) GrowableArray<uint16_t>(arena_, num_blocks);
-    topological_order_indexes_ = new (arena_) GrowableArray<uint16_t>(arena_, num_blocks);
-  }
-  topological_order_->Reset();
-  topological_order_loop_ends_->Reset();
-  topological_order_indexes_->Reset();
-  topological_order_loop_ends_->Resize(num_blocks);
-  topological_order_indexes_->Resize(num_blocks);
-  for (BasicBlockId i = 0; i != num_blocks; ++i) {
-    topological_order_loop_ends_->Insert(0u);
-    topological_order_indexes_->Insert(static_cast<uint16_t>(-1));
-  }
+  // Clear the topological order arrays.
+  topological_order_.clear();
+  topological_order_.reserve(num_blocks);
+  topological_order_loop_ends_.clear();
+  topological_order_loop_ends_.resize(num_blocks, 0u);
+  topological_order_indexes_.clear();
+  topological_order_indexes_.resize(num_blocks, static_cast<uint16_t>(-1));
 
   // Mark all blocks as unvisited.
   ClearAllVisitedFlags();
@@ -1675,8 +1860,8 @@
       if (bb->visited) {
         // Loop head: it was already processed, mark end and copy exit blocks to the queue.
         DCHECK(q.empty()) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
-        uint16_t idx = static_cast<uint16_t>(topological_order_->Size());
-        topological_order_loop_ends_->Put(topological_order_indexes_->Get(bb->id), idx);
+        uint16_t idx = static_cast<uint16_t>(topological_order_.size());
+        topological_order_loop_ends_[topological_order_indexes_[bb->id]] = idx;
         DCHECK_EQ(loop_head_stack.back(), bb->id);
         loop_head_stack.pop_back();
         ArenaBitVector* reachable =
@@ -1719,15 +1904,16 @@
           continue;
         }
 
-        GrowableArray<BasicBlockId>::Iterator pred_iter(candidate->predecessors);
-        BasicBlock* pred_bb = GetBasicBlock(pred_iter.Next());
-        for ( ; pred_bb != nullptr; pred_bb = GetBasicBlock(pred_iter.Next())) {
+        for (BasicBlockId pred_id : candidate->predecessors) {
+          BasicBlock* pred_bb = GetBasicBlock(pred_id);
+          DCHECK(pred_bb != nullptr);
           if (pred_bb != candidate && !pred_bb->visited &&
               !pred_bb->dominators->IsBitSet(candidate->id)) {
-            break;  // Keep non-null pred_bb to indicate failure.
+            candidate = nullptr;  // Set candidate to null to indicate failure.
+            break;
           }
         }
-        if (pred_bb == nullptr) {
+        if (candidate != nullptr) {
           bb = candidate;
           break;
         }
@@ -1747,9 +1933,9 @@
     bb->visited = true;
 
     // Now add the basic block.
-    uint16_t idx = static_cast<uint16_t>(topological_order_->Size());
-    topological_order_indexes_->Put(bb->id, idx);
-    topological_order_->Insert(bb->id);
+    uint16_t idx = static_cast<uint16_t>(topological_order_.size());
+    topological_order_indexes_[bb->id] = idx;
+    topological_order_.push_back(bb->id);
 
     // Update visited_cnt_values for children.
     ChildBlockIterator succIter(bb, this);
@@ -1761,7 +1947,7 @@
 
       // One more predecessor was visited.
       visited_cnt_values[successor->id] += 1u;
-      if (visited_cnt_values[successor->id] == successor->predecessors->Size()) {
+      if (visited_cnt_values[successor->id] == successor->predecessors.size()) {
         if (loop_head_stack.empty() ||
             loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(successor->id)) {
           q.push(successor);
@@ -1774,8 +1960,8 @@
   }
 
   // Prepare the loop head stack for iteration.
-  topological_order_loop_head_stack_ =
-      new (arena_) GrowableArray<std::pair<uint16_t, bool>>(arena_, max_nested_loops);
+  topological_order_loop_head_stack_.clear();
+  topological_order_loop_head_stack_.reserve(max_nested_loops);
 }
 
 bool BasicBlock::IsExceptionBlock() const {
@@ -1792,8 +1978,8 @@
     return false;
 
   int idx;
-  for (idx = gen_suspend_test_list_.Size() - 1; idx >= 0; idx--) {
-    BasicBlock* bb = gen_suspend_test_list_.Get(idx);
+  for (idx = gen_suspend_test_list_.size() - 1; idx >= 0; idx--) {
+    BasicBlock* bb = gen_suspend_test_list_[idx];
     if (bb == source)
       return true;  // The block has been inserted by a suspend check before.
     if (source->dominators->IsBitSet(bb->id) && bb->dominators->IsBitSet(target_id))
@@ -1809,7 +1995,7 @@
   // Check if we actually do have successors.
   if (basic_block_ != 0 && basic_block_->successor_block_list_type != kNotUsed) {
     have_successors_ = true;
-    successor_iter_.Reset(basic_block_->successor_blocks);
+    successor_iter_ = basic_block_->successor_blocks.cbegin();
   }
 }
 
@@ -1842,9 +2028,10 @@
   // We visited both taken and fallthrough. Now check if we have successors we need to visit.
   if (have_successors_ == true) {
     // Get information about next successor block.
-    for (SuccessorBlockInfo* successor_block_info = successor_iter_.Next();
-      successor_block_info != nullptr;
-      successor_block_info = successor_iter_.Next()) {
+    auto end = basic_block_->successor_blocks.cend();
+    while (successor_iter_ != end) {
+      SuccessorBlockInfo* successor_block_info = *successor_iter_;
+      ++successor_iter_;
       // If block was replaced by zero block, take next one.
       if (successor_block_info->block != NullBasicBlockId) {
         return mir_graph_->GetBasicBlock(successor_block_info->block);
@@ -1875,17 +2062,12 @@
 
   result_bb->successor_block_list_type = successor_block_list_type;
   if (result_bb->successor_block_list_type != kNotUsed) {
-    size_t size = successor_blocks->Size();
-    result_bb->successor_blocks = new (arena) GrowableArray<SuccessorBlockInfo*>(arena, size, kGrowableArraySuccessorBlocks);
-    GrowableArray<SuccessorBlockInfo*>::Iterator iterator(successor_blocks);
-    while (true) {
-      SuccessorBlockInfo* sbi_old = iterator.Next();
-      if (sbi_old == nullptr) {
-        break;
-      }
-      SuccessorBlockInfo* sbi_new = static_cast<SuccessorBlockInfo*>(arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+    result_bb->successor_blocks.reserve(successor_blocks.size());
+    for (SuccessorBlockInfo* sbi_old : successor_blocks) {
+      SuccessorBlockInfo* sbi_new = static_cast<SuccessorBlockInfo*>(
+          arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
       memcpy(sbi_new, sbi_old, sizeof(SuccessorBlockInfo));
-      result_bb->successor_blocks->Insert(sbi_new);
+      result_bb->successor_blocks.push_back(sbi_new);
     }
   }
 
@@ -1934,6 +2116,10 @@
     case Instruction::IPUT_SHORT:
     case Instruction::IPUT_QUICK:
     case Instruction::IPUT_OBJECT_QUICK:
+    case Instruction::IPUT_BOOLEAN_QUICK:
+    case Instruction::IPUT_BYTE_QUICK:
+    case Instruction::IPUT_CHAR_QUICK:
+    case Instruction::IPUT_SHORT_QUICK:
     case Instruction::APUT:
     case Instruction::APUT_OBJECT:
     case Instruction::APUT_BOOLEAN:
@@ -2040,14 +2226,10 @@
   first_mir_insn = nullptr;
   last_mir_insn = nullptr;
 
-  GrowableArray<BasicBlockId>::Iterator iterator(predecessors);
-
   MIRGraph* mir_graph = c_unit->mir_graph.get();
-  while (true) {
-    BasicBlock* pred_bb = mir_graph->GetBasicBlock(iterator.Next());
-    if (pred_bb == nullptr) {
-      break;
-    }
+  for (BasicBlockId pred_id : predecessors) {
+    BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id);
+    DCHECK(pred_bb != nullptr);
 
     // Sadly we have to go through the children by hand here.
     pred_bb->ReplaceChild(id, NullBasicBlockId);
@@ -2057,9 +2239,14 @@
   ChildBlockIterator successorChildIter(this, mir_graph);
 
   for (BasicBlock* childPtr = successorChildIter.Next(); childPtr != 0; childPtr = successorChildIter.Next()) {
-    // Replace child with null child.
-    childPtr->predecessors->Delete(id);
+    // Erase this predecessor from child.
+    childPtr->ErasePredecessor(id);
   }
+
+  // Remove link to children.
+  taken = NullBasicBlockId;
+  fall_through = NullBasicBlockId;
+  successor_block_list_type = kNotUsed;
 }
 
 bool BasicBlock::IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg) {
@@ -2119,12 +2306,7 @@
   }
 
   if (successor_block_list_type != kNotUsed) {
-    GrowableArray<SuccessorBlockInfo*>::Iterator iterator(successor_blocks);
-    while (true) {
-      SuccessorBlockInfo* successor_block_info = iterator.Next();
-      if (successor_block_info == nullptr) {
-        break;
-      }
+    for (SuccessorBlockInfo* successor_block_info : successor_blocks) {
       if (successor_block_info->block == old_bb) {
         successor_block_info->block = new_bb;
         found = true;
@@ -2135,28 +2317,20 @@
   return found;
 }
 
-void BasicBlock::UpdatePredecessor(BasicBlockId old_parent, BasicBlockId new_parent) {
-  GrowableArray<BasicBlockId>::Iterator iterator(predecessors);
-  bool found = false;
+void BasicBlock::ErasePredecessor(BasicBlockId old_pred) {
+  auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred);
+  DCHECK(pos != predecessors.end());
+  predecessors.erase(pos);
+}
 
-  while (true) {
-    BasicBlockId pred_bb_id = iterator.Next();
-
-    if (pred_bb_id == NullBasicBlockId) {
-      break;
-    }
-
-    if (pred_bb_id == old_parent) {
-      size_t idx = iterator.GetIndex() - 1;
-      predecessors->Put(idx, new_parent);
-      found = true;
-      break;
-    }
-  }
-
-  // If not found, add it.
-  if (found == false) {
-    predecessors->Insert(new_parent);
+void BasicBlock::UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred) {
+  DCHECK_NE(new_pred, NullBasicBlockId);
+  auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred);
+  if (pos != predecessors.end()) {
+    *pos = new_pred;
+  } else {
+    // If not found, add it.
+    predecessors.push_back(new_pred);
   }
 }
 
@@ -2164,7 +2338,7 @@
 // post-incremented.
 BasicBlock* MIRGraph::CreateNewBB(BBType block_type) {
   BasicBlock* res = NewMemBB(block_type, num_blocks_++);
-  block_list_.Insert(res);
+  block_list_.push_back(res);
   return res;
 }
 
@@ -2174,7 +2348,89 @@
 }
 
 void MIRGraph::InitializeBasicBlockData() {
-  num_blocks_ = block_list_.Size();
+  num_blocks_ = block_list_.size();
 }
 
+int MIR::DecodedInstruction::FlagsOf() const {
+  // Calculate new index.
+  int idx = static_cast<int>(opcode) - kNumPackedOpcodes;
+
+  // Check if it is an extended or not.
+  if (idx < 0) {
+    return Instruction::FlagsOf(opcode);
+  }
+
+  // For extended, we use a switch.
+  switch (static_cast<int>(opcode)) {
+    case kMirOpPhi:
+      return Instruction::kContinue;
+    case kMirOpCopy:
+      return Instruction::kContinue;
+    case kMirOpFusedCmplFloat:
+      return Instruction::kContinue | Instruction::kBranch;
+    case kMirOpFusedCmpgFloat:
+      return Instruction::kContinue | Instruction::kBranch;
+    case kMirOpFusedCmplDouble:
+      return Instruction::kContinue | Instruction::kBranch;
+    case kMirOpFusedCmpgDouble:
+      return Instruction::kContinue | Instruction::kBranch;
+    case kMirOpFusedCmpLong:
+      return Instruction::kContinue | Instruction::kBranch;
+    case kMirOpNop:
+      return Instruction::kContinue;
+    case kMirOpNullCheck:
+      return Instruction::kContinue | Instruction::kThrow;
+    case kMirOpRangeCheck:
+      return Instruction::kContinue | Instruction::kThrow;
+    case kMirOpDivZeroCheck:
+      return Instruction::kContinue | Instruction::kThrow;
+    case kMirOpCheck:
+      return Instruction::kContinue | Instruction::kThrow;
+    case kMirOpCheckPart2:
+      return Instruction::kContinue;
+    case kMirOpSelect:
+      return Instruction::kContinue;
+    case kMirOpConstVector:
+      return Instruction::kContinue;
+    case kMirOpMoveVector:
+      return Instruction::kContinue;
+    case kMirOpPackedMultiply:
+      return Instruction::kContinue;
+    case kMirOpPackedAddition:
+      return Instruction::kContinue;
+    case kMirOpPackedSubtract:
+      return Instruction::kContinue;
+    case kMirOpPackedShiftLeft:
+      return Instruction::kContinue;
+    case kMirOpPackedSignedShiftRight:
+      return Instruction::kContinue;
+    case kMirOpPackedUnsignedShiftRight:
+      return Instruction::kContinue;
+    case kMirOpPackedAnd:
+      return Instruction::kContinue;
+    case kMirOpPackedOr:
+      return Instruction::kContinue;
+    case kMirOpPackedXor:
+      return Instruction::kContinue;
+    case kMirOpPackedAddReduce:
+      return Instruction::kContinue;
+    case kMirOpPackedReduce:
+      return Instruction::kContinue;
+    case kMirOpPackedSet:
+      return Instruction::kContinue;
+    case kMirOpReserveVectorRegisters:
+      return Instruction::kContinue;
+    case kMirOpReturnVectorRegisters:
+      return Instruction::kContinue;
+    case kMirOpMemBarrier:
+      return Instruction::kContinue;
+    case kMirOpPackedArrayGet:
+      return Instruction::kContinue | Instruction::kThrow;
+    case kMirOpPackedArrayPut:
+      return Instruction::kContinue | Instruction::kThrow;
+    default:
+      LOG(WARNING) << "ExtendedFlagsOf: Unhandled case: " << static_cast<int> (opcode);
+      return 0;
+  }
+}
 }  // namespace art
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 5817f92..f14b187 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -19,14 +19,14 @@
 
 #include <stdint.h>
 
+#include "compiler_ir.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
-#include "compiler_ir.h"
+#include "driver/dex_compilation_unit.h"
 #include "invoke_type.h"
 #include "mir_field_info.h"
 #include "mir_method_info.h"
 #include "utils/arena_bit_vector.h"
-#include "utils/growable_array.h"
 #include "utils/arena_containers.h"
 #include "utils/scoped_arena_containers.h"
 #include "reg_location.h"
@@ -36,40 +36,6 @@
 
 class GlobalValueNumbering;
 
-enum InstructionAnalysisAttributePos {
-  kUninterestingOp = 0,
-  kArithmeticOp,
-  kFPOp,
-  kSingleOp,
-  kDoubleOp,
-  kIntOp,
-  kLongOp,
-  kBranchOp,
-  kInvokeOp,
-  kArrayOp,
-  kHeavyweightOp,
-  kSimpleConstOp,
-  kMoveOp,
-  kSwitch
-};
-
-#define AN_NONE (1 << kUninterestingOp)
-#define AN_MATH (1 << kArithmeticOp)
-#define AN_FP (1 << kFPOp)
-#define AN_LONG (1 << kLongOp)
-#define AN_INT (1 << kIntOp)
-#define AN_SINGLE (1 << kSingleOp)
-#define AN_DOUBLE (1 << kDoubleOp)
-#define AN_FLOATMATH (1 << kFPOp)
-#define AN_BRANCH (1 << kBranchOp)
-#define AN_INVOKE (1 << kInvokeOp)
-#define AN_ARRAYOP (1 << kArrayOp)
-#define AN_HEAVYWEIGHT (1 << kHeavyweightOp)
-#define AN_SIMPLECONST (1 << kSimpleConstOp)
-#define AN_MOVE (1 << kMoveOp)
-#define AN_SWITCH (1 << kSwitch)
-#define AN_COMPUTATIONAL (AN_MATH | AN_ARRAYOP | AN_MOVE | AN_SIMPLECONST)
-
 enum DataFlowAttributePos {
   kUA = 0,
   kUB,
@@ -194,6 +160,7 @@
 #define MIR_CALLEE                      (1 << kMIRCallee)
 #define MIR_IGNORE_SUSPEND_CHECK        (1 << kMIRIgnoreSuspendCheck)
 #define MIR_DUP                         (1 << kMIRDup)
+#define MIR_STORE_NON_TEMPORAL          (1 << kMIRStoreNonTemporal)
 
 #define BLOCK_NAME_LEN 80
 
@@ -215,6 +182,7 @@
 enum CompilerTempType {
   kCompilerTempVR,                // A virtual register temporary.
   kCompilerTempSpecialMethodPtr,  // Temporary that keeps track of current method pointer.
+  kCompilerTempBackend,           // Temporary that is used by backend.
 };
 
 // When debug option enabled, records effectiveness of null and range check elimination.
@@ -297,37 +265,37 @@
     }
 
     bool IsInvoke() const {
-      return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kInvoke) == Instruction::kInvoke);
+      return ((FlagsOf() & Instruction::kInvoke) == Instruction::kInvoke);
     }
 
     bool IsStore() const {
-      return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kStore) == Instruction::kStore);
+      return ((FlagsOf() & Instruction::kStore) == Instruction::kStore);
     }
 
     bool IsLoad() const {
-      return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kLoad) == Instruction::kLoad);
+      return ((FlagsOf() & Instruction::kLoad) == Instruction::kLoad);
     }
 
     bool IsConditionalBranch() const {
-      return !IsPseudoMirOp(opcode) && (Instruction::FlagsOf(opcode) == (Instruction::kContinue | Instruction::kBranch));
+      return (FlagsOf() == (Instruction::kContinue | Instruction::kBranch));
     }
 
     /**
      * @brief Is the register C component of the decoded instruction a constant?
      */
     bool IsCFieldOrConstant() const {
-      return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant);
+      return ((FlagsOf() & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant);
     }
 
     /**
      * @brief Is the register C component of the decoded instruction a constant?
      */
     bool IsBFieldOrConstant() const {
-      return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant);
+      return ((FlagsOf() & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant);
     }
 
     bool IsCast() const {
-      return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kCast) == Instruction::kCast);
+      return ((FlagsOf() & Instruction::kCast) == Instruction::kCast);
     }
 
     /**
@@ -337,12 +305,14 @@
      *            when crossing such an instruction.
      */
     bool Clobbers() const {
-      return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kClobber) == Instruction::kClobber);
+      return ((FlagsOf() & Instruction::kClobber) == Instruction::kClobber);
     }
 
     bool IsLinear() const {
-      return !IsPseudoMirOp(opcode) && (Instruction::FlagsOf(opcode) & (Instruction::kAdd | Instruction::kSubtract)) != 0;
+      return (FlagsOf() & (Instruction::kAdd | Instruction::kSubtract)) != 0;
     }
+
+    int FlagsOf() const;
   } dalvikInsn;
 
   NarrowDexOffset offset;         // Offset of the instruction in code units.
@@ -389,6 +359,17 @@
 struct SuccessorBlockInfo;
 
 struct BasicBlock {
+  BasicBlock(BasicBlockId block_id, BBType type, ArenaAllocator* allocator)
+      : id(block_id),
+        dfs_id(), start_offset(), fall_through(), taken(), i_dom(), nesting_depth(),
+        block_type(type),
+        successor_block_list_type(kNotUsed),
+        visited(), hidden(), catch_entry(), explicit_throw(), conditional_branch(),
+        terminated_by_return(), dominates_return(), use_lvn(), first_mir_insn(),
+        last_mir_insn(), data_flow_info(), dominators(), i_dominated(), dom_frontier(),
+        predecessors(allocator->Adapter(kArenaAllocBBPredecessors)),
+        successor_blocks(allocator->Adapter(kArenaAllocSuccessor)) {
+  }
   BasicBlockId id;
   BasicBlockId dfs_id;
   NarrowDexOffset start_offset;     // Offset in code units.
@@ -412,8 +393,8 @@
   ArenaBitVector* dominators;
   ArenaBitVector* i_dominated;      // Set nodes being immediately dominated.
   ArenaBitVector* dom_frontier;     // Dominance frontier.
-  GrowableArray<BasicBlockId>* predecessors;
-  GrowableArray<SuccessorBlockInfo*>* successor_blocks;
+  ArenaVector<BasicBlockId> predecessors;
+  ArenaVector<SuccessorBlockInfo*> successor_blocks;
 
   void AppendMIR(MIR* mir);
   void AppendMIRList(MIR* first_list_mir, MIR* last_list_mir);
@@ -441,7 +422,7 @@
    * @brief Hide the BasicBlock.
    * @details Set it to kDalvikByteCode, set hidden to true, remove all MIRs,
    *          remove itself from any predecessor edges, remove itself from any
-   *          child's predecessor growable array.
+   *          child's predecessor array.
    */
   void Hide(CompilationUnit* c_unit);
 
@@ -456,7 +437,12 @@
   bool ReplaceChild(BasicBlockId old_bb, BasicBlockId new_bb);
 
   /**
-   * @brief Update the predecessor growable array from old_pred to new_pred.
+   * @brief Erase the predecessor old_pred.
+   */
+  void ErasePredecessor(BasicBlockId old_pred);
+
+  /**
+   * @brief Update the predecessor array from old_pred to new_pred.
    */
   void UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred);
 
@@ -507,7 +493,7 @@
   bool visited_fallthrough_;
   bool visited_taken_;
   bool have_successors_;
-  GrowableArray<SuccessorBlockInfo*>::Iterator successor_iter_;
+  ArenaVector<SuccessorBlockInfo*>::const_iterator successor_iter_;
 };
 
 /*
@@ -539,7 +525,7 @@
 class MIRGraph {
  public:
   MIRGraph(CompilationUnit* cu, ArenaAllocator* arena);
-  ~MIRGraph();
+  virtual ~MIRGraph();
 
   /*
    * Examine the graph to determine whether it's worthwile to spend the time compiling
@@ -569,17 +555,36 @@
     return current_code_item_->insns_;
   }
 
+  /**
+   * @brief Used to obtain the raw dex bytecode instruction pointer.
+   * @param m_unit_index The method index in MIRGraph (caused by having multiple methods).
+   * This is guaranteed to contain index 0 which is the base method being compiled.
+   * @return Returns the raw instruction pointer.
+   */
   const uint16_t* GetInsns(int m_unit_index) const {
     return m_units_[m_unit_index]->GetCodeItem()->insns_;
   }
 
+  /**
+   * @brief Used to obtain the raw data table.
+   * @param mir sparse switch, packed switch, of fill-array-data
+   * @param table_offset The table offset from start of method.
+   * @return Returns the raw table pointer.
+   */
+  const uint16_t* GetTable(MIR* mir, uint32_t table_offset) const {
+    return GetInsns(mir->m_unit_index) + mir->offset + table_offset;
+  }
+
   unsigned int GetNumBlocks() const {
     return num_blocks_;
   }
 
-  size_t GetNumDalvikInsns() const {
-    return cu_->code_item->insns_size_in_code_units_;
-  }
+  /**
+   * @brief Provides the total size in code units of all instructions in MIRGraph.
+   * @details Includes the sizes of all methods in compilation unit.
+   * @return Returns the cumulative sum of all insn sizes (in code units).
+   */
+  size_t GetNumDalvikInsns() const;
 
   ArenaBitVector* GetTryBlockAddr() const {
     return try_block_addr_;
@@ -594,26 +599,27 @@
   }
 
   BasicBlock* GetBasicBlock(unsigned int block_id) const {
-    return (block_id == NullBasicBlockId) ? NULL : block_list_.Get(block_id);
+    DCHECK_LT(block_id, block_list_.size());  // NOTE: NullBasicBlockId is 0.
+    return (block_id == NullBasicBlockId) ? NULL : block_list_[block_id];
   }
 
   size_t GetBasicBlockListCount() const {
-    return block_list_.Size();
+    return block_list_.size();
   }
 
-  GrowableArray<BasicBlock*>* GetBlockList() {
-    return &block_list_;
+  const ArenaVector<BasicBlock*>& GetBlockList() {
+    return block_list_;
   }
 
-  GrowableArray<BasicBlockId>* GetDfsOrder() {
+  const ArenaVector<BasicBlockId>& GetDfsOrder() {
     return dfs_order_;
   }
 
-  GrowableArray<BasicBlockId>* GetDfsPostOrder() {
+  const ArenaVector<BasicBlockId>& GetDfsPostOrder() {
     return dfs_post_order_;
   }
 
-  GrowableArray<BasicBlockId>* GetDomPostOrder() {
+  const ArenaVector<BasicBlockId>& GetDomPostOrder() {
     return dom_post_order_traversal_;
   }
 
@@ -660,20 +666,20 @@
   void DoCacheFieldLoweringInfo();
 
   const MirIFieldLoweringInfo& GetIFieldLoweringInfo(MIR* mir) const {
-    DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.Size());
-    return ifield_lowering_infos_.GetRawStorage()[mir->meta.ifield_lowering_info];
+    DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.size());
+    return ifield_lowering_infos_[mir->meta.ifield_lowering_info];
   }
 
   const MirSFieldLoweringInfo& GetSFieldLoweringInfo(MIR* mir) const {
-    DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.Size());
-    return sfield_lowering_infos_.GetRawStorage()[mir->meta.sfield_lowering_info];
+    DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.size());
+    return sfield_lowering_infos_[mir->meta.sfield_lowering_info];
   }
 
   void DoCacheMethodLoweringInfo();
 
   const MirMethodLoweringInfo& GetMethodLoweringInfo(MIR* mir) {
-    DCHECK_LT(mir->meta.method_lowering_info, method_lowering_infos_.Size());
-    return method_lowering_infos_.GetRawStorage()[mir->meta.method_lowering_info];
+    DCHECK_LT(mir->meta.method_lowering_info, method_lowering_infos_.size());
+    return method_lowering_infos_[mir->meta.method_lowering_info];
   }
 
   void ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput);
@@ -686,24 +692,24 @@
 
   void BasicBlockOptimization();
 
-  GrowableArray<BasicBlockId>* GetTopologicalSortOrder() {
-    DCHECK(topological_order_ != nullptr);
+  const ArenaVector<BasicBlockId>& GetTopologicalSortOrder() {
+    DCHECK(!topological_order_.empty());
     return topological_order_;
   }
 
-  GrowableArray<BasicBlockId>* GetTopologicalSortOrderLoopEnds() {
-    DCHECK(topological_order_loop_ends_ != nullptr);
+  const ArenaVector<BasicBlockId>& GetTopologicalSortOrderLoopEnds() {
+    DCHECK(!topological_order_loop_ends_.empty());
     return topological_order_loop_ends_;
   }
 
-  GrowableArray<BasicBlockId>* GetTopologicalSortOrderIndexes() {
-    DCHECK(topological_order_indexes_ != nullptr);
+  const ArenaVector<BasicBlockId>& GetTopologicalSortOrderIndexes() {
+    DCHECK(!topological_order_indexes_.empty());
     return topological_order_indexes_;
   }
 
-  GrowableArray<std::pair<uint16_t, bool>>* GetTopologicalSortOrderLoopHeadStack() {
-    DCHECK(topological_order_loop_head_stack_ != nullptr);
-    return topological_order_loop_head_stack_;
+  ArenaVector<std::pair<uint16_t, bool>>* GetTopologicalSortOrderLoopHeadStack() {
+    DCHECK(!topological_order_.empty());  // Checking the main array, not the stack.
+    return &topological_order_loop_head_stack_;
   }
 
   bool IsConst(int32_t s_reg) const {
@@ -724,6 +730,19 @@
     return constant_values_[s_reg];
   }
 
+  /**
+   * @brief Used to obtain 64-bit value of a pair of ssa registers.
+   * @param s_reg_low The ssa register representing the low bits.
+   * @param s_reg_high The ssa register representing the high bits.
+   * @return Retusn the 64-bit constant value.
+   */
+  int64_t ConstantValueWide(int32_t s_reg_low, int32_t s_reg_high) const {
+    DCHECK(IsConst(s_reg_low));
+    DCHECK(IsConst(s_reg_high));
+    return (static_cast<int64_t>(constant_values_[s_reg_high]) << 32) |
+        Low32Bits(static_cast<int64_t>(constant_values_[s_reg_low]));
+  }
+
   int64_t ConstantValueWide(RegLocation loc) const {
     DCHECK(IsConst(loc));
     DCHECK(!loc.high_word);  // Do not allow asking for the high partner.
@@ -732,6 +751,20 @@
         Low32Bits(static_cast<int64_t>(constant_values_[loc.orig_sreg]));
   }
 
+  /**
+   * @brief Used to mark ssa register as being constant.
+   * @param ssa_reg The ssa register.
+   * @param value The constant value of ssa register.
+   */
+  void SetConstant(int32_t ssa_reg, int32_t value);
+
+  /**
+   * @brief Used to mark ssa register and its wide counter-part as being constant.
+   * @param ssa_reg The ssa register.
+   * @param value The 64-bit constant value of ssa register and its pair.
+   */
+  void SetConstantWide(int32_t ssa_reg, int64_t value);
+
   bool IsConstantNullRef(RegLocation loc) const {
     return loc.ref && loc.is_const && (ConstantValue(loc) == 0);
   }
@@ -754,16 +787,19 @@
     return num_reachable_blocks_;
   }
 
-  int GetUseCount(int vreg) const {
-    return use_counts_.Get(vreg);
+  uint32_t GetUseCount(int sreg) const {
+    DCHECK_LT(static_cast<size_t>(sreg), use_counts_.size());
+    return use_counts_[sreg];
   }
 
-  int GetRawUseCount(int vreg) const {
-    return raw_use_counts_.Get(vreg);
+  uint32_t GetRawUseCount(int sreg) const {
+    DCHECK_LT(static_cast<size_t>(sreg), raw_use_counts_.size());
+    return raw_use_counts_[sreg];
   }
 
   int GetSSASubscript(int ssa_reg) const {
-    return ssa_subscripts_->Get(ssa_reg);
+    DCHECK_LT(static_cast<size_t>(ssa_reg), ssa_subscripts_.size());
+    return ssa_subscripts_[ssa_reg];
   }
 
   RegLocation GetRawSrc(MIR* mir, int num) {
@@ -815,9 +851,26 @@
    * @return Returns the number of compiler temporaries.
    */
   size_t GetNumUsedCompilerTemps() const {
-    size_t total_num_temps = compiler_temps_.Size();
-    DCHECK_LE(num_non_special_compiler_temps_, total_num_temps);
-    return total_num_temps;
+    // Assume that the special temps will always be used.
+    return GetNumNonSpecialCompilerTemps() + max_available_special_compiler_temps_;
+  }
+
+  /**
+   * @brief Used to obtain number of bytes needed for special temps.
+   * @details This space is always needed because temps have special location on stack.
+   * @return Returns number of bytes for the special temps.
+   */
+  size_t GetNumBytesForSpecialTemps() const;
+
+  /**
+   * @brief Used by backend as a hint for maximum number of bytes for non-special temps.
+   * @details Returns 4 bytes for each temp because that is the maximum amount needed
+   * for storing each temp. The BE could be smarter though and allocate a smaller
+   * spill region.
+   * @return Returns the maximum number of bytes needed for non-special temps.
+   */
+  size_t GetMaximumBytesForNonSpecialTemps() const {
+    return GetNumNonSpecialCompilerTemps() * sizeof(uint32_t);
   }
 
   /**
@@ -835,7 +888,9 @@
    * @return Returns true if the max was set and false if failed to set.
    */
   bool SetMaxAvailableNonSpecialCompilerTemps(size_t new_max) {
-    if (new_max < GetNumNonSpecialCompilerTemps()) {
+    // Make sure that enough temps still exist for backend and also that the
+    // new max can still keep around all of the already requested temps.
+    if (new_max < (GetNumNonSpecialCompilerTemps() + reserved_temps_for_backend_)) {
       return false;
     } else {
       max_available_non_special_compiler_temps_ = new_max;
@@ -844,21 +899,12 @@
   }
 
   /**
-   * @brief Provides the number of non-special compiler temps available.
+   * @brief Provides the number of non-special compiler temps available for use by ME.
    * @details Even if this returns zero, special compiler temps are guaranteed to be available.
+   * Additionally, this makes sure to not use any temps reserved for BE only.
    * @return Returns the number of available temps.
    */
-  size_t GetNumAvailableNonSpecialCompilerTemps();
-
-  /**
-   * @brief Used to obtain an existing compiler temporary.
-   * @param index The index of the temporary which must be strictly less than the
-   * number of temporaries.
-   * @return Returns the temporary that was asked for.
-   */
-  CompilerTemp* GetCompilerTemp(size_t index) const {
-    return compiler_temps_.Get(index);
-  }
+  size_t GetNumAvailableVRTemps();
 
   /**
    * @brief Used to obtain the maximum number of compiler temporaries that can be requested.
@@ -869,7 +915,22 @@
   }
 
   /**
+   * @brief Used to signal that the compiler temps have been committed.
+   * @details This should be used once the number of temps can no longer change,
+   * such as after frame size is committed and cannot be changed.
+   */
+  void CommitCompilerTemps() {
+    compiler_temps_committed_ = true;
+  }
+
+  /**
    * @brief Used to obtain a new unique compiler temporary.
+   * @details Two things are done for convenience when allocating a new compiler
+   * temporary. The ssa register is automatically requested and the information
+   * about reg location is filled. This helps when the temp is requested post
+   * ssa initialization, such as when temps are requested by the backend.
+   * @warning If the temp requested will be used for ME and have multiple versions,
+   * the sreg provided by the temp will be invalidated on next ssa recalculation.
    * @param ct_type Type of compiler temporary requested.
    * @param wide Whether we should allocate a wide temporary.
    * @return Returns the newly created compiler temporary.
@@ -911,8 +972,49 @@
   }
 
   // Is this vreg in the in set?
-  bool IsInVReg(int vreg) {
-    return (vreg >= cu_->num_regs);
+  bool IsInVReg(uint32_t vreg) {
+    return (vreg >= GetFirstInVR()) && (vreg < GetFirstTempVR());
+  }
+
+  uint32_t GetNumOfCodeVRs() const {
+    return current_code_item_->registers_size_;
+  }
+
+  uint32_t GetNumOfCodeAndTempVRs() const {
+    // Include all of the possible temps so that no structures overflow when initialized.
+    return GetNumOfCodeVRs() + GetMaxPossibleCompilerTemps();
+  }
+
+  uint32_t GetNumOfLocalCodeVRs() const {
+    // This also refers to the first "in" VR.
+    return GetNumOfCodeVRs() - current_code_item_->ins_size_;
+  }
+
+  uint32_t GetNumOfInVRs() const {
+    return current_code_item_->ins_size_;
+  }
+
+  uint32_t GetNumOfOutVRs() const {
+    return current_code_item_->outs_size_;
+  }
+
+  uint32_t GetFirstInVR() const {
+    return GetNumOfLocalCodeVRs();
+  }
+
+  uint32_t GetFirstTempVR() const {
+    // Temp VRs immediately follow code VRs.
+    return GetNumOfCodeVRs();
+  }
+
+  uint32_t GetFirstSpecialTempVR() const {
+    // Special temps appear first in the ordering before non special temps.
+    return GetFirstTempVR();
+  }
+
+  uint32_t GetFirstNonSpecialTempVR() const {
+    // We always leave space for all the special temps before the non-special ones.
+    return GetFirstSpecialTempVR() + max_available_special_compiler_temps_;
   }
 
   void DumpCheckStats();
@@ -965,6 +1067,7 @@
     punt_to_interpreter_ = val;
   }
 
+  void DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir);
   char* GetDalvikDisassembly(const MIR* mir);
   void ReplaceSpecialChars(std::string& str);
   std::string GetSSAName(int ssa_reg);
@@ -1044,6 +1147,7 @@
   void ComputeDefBlockMatrix();
   void ComputeDominators();
   void CompilerInitializeSSAConversion();
+  virtual void InitializeBasicBlockDataFlow();
   void InsertPhiNodes();
   void DoDFSPreOrderSSARename(BasicBlock* block);
 
@@ -1058,16 +1162,15 @@
   ArenaSafeMap<unsigned int, unsigned int> block_id_map_;   // Block collapse lookup cache.
 
   static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst];
-  static const uint32_t analysis_attributes_[kMirOpLast];
 
   void HandleSSADef(int* defs, int dalvik_reg, int reg_index);
   bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed);
 
   // Used for removing redudant suspend tests
   void AppendGenSuspendTestList(BasicBlock* bb) {
-    if (gen_suspend_test_list_.Size() == 0 ||
-        gen_suspend_test_list_.Get(gen_suspend_test_list_.Size() - 1) != bb) {
-      gen_suspend_test_list_.Insert(bb);
+    if (gen_suspend_test_list_.size() == 0 ||
+        gen_suspend_test_list_.back() != bb) {
+      gen_suspend_test_list_.push_back(bb);
     }
   }
 
@@ -1088,7 +1191,6 @@
                       ArenaBitVector* live_in_v,
                       const MIR::DecodedInstruction& d_insn);
   bool DoSSAConversion(BasicBlock* bb);
-  bool InvokeUsesMethodStar(MIR* mir);
   int ParseInsn(const uint16_t* code_ptr, MIR::DecodedInstruction* decoded_instruction);
   bool ContentIsInsn(const uint16_t* code_ptr);
   BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block,
@@ -1116,8 +1218,6 @@
   void MarkPreOrder(BasicBlock* bb);
   void RecordDFSOrders(BasicBlock* bb);
   void ComputeDomPostOrderTraversal(BasicBlock* bb);
-  void SetConstant(int32_t ssa_reg, int value);
-  void SetConstantWide(int ssa_reg, int64_t value);
   int GetSSAUseCount(int s_reg);
   bool BasicBlockOpt(BasicBlock* bb);
   bool BuildExtendedBBList(struct BasicBlock* bb);
@@ -1135,45 +1235,45 @@
                               std::string* skip_message);
 
   CompilationUnit* const cu_;
-  GrowableArray<int>* ssa_base_vregs_;
-  GrowableArray<int>* ssa_subscripts_;
+  ArenaVector<int> ssa_base_vregs_;
+  ArenaVector<int> ssa_subscripts_;
   // Map original Dalvik virtual reg i to the current SSA name.
   int* vreg_to_ssa_map_;            // length == method->registers_size
   int* ssa_last_defs_;              // length == method->registers_size
   ArenaBitVector* is_constant_v_;   // length == num_ssa_reg
   int* constant_values_;            // length == num_ssa_reg
   // Use counts of ssa names.
-  GrowableArray<uint32_t> use_counts_;      // Weighted by nesting depth
-  GrowableArray<uint32_t> raw_use_counts_;  // Not weighted
+  ArenaVector<uint32_t> use_counts_;      // Weighted by nesting depth
+  ArenaVector<uint32_t> raw_use_counts_;  // Not weighted
   unsigned int num_reachable_blocks_;
   unsigned int max_num_reachable_blocks_;
-  GrowableArray<BasicBlockId>* dfs_order_;
-  GrowableArray<BasicBlockId>* dfs_post_order_;
-  GrowableArray<BasicBlockId>* dom_post_order_traversal_;
-  GrowableArray<BasicBlockId>* topological_order_;
+  ArenaVector<BasicBlockId> dfs_order_;
+  ArenaVector<BasicBlockId> dfs_post_order_;
+  ArenaVector<BasicBlockId> dom_post_order_traversal_;
+  ArenaVector<BasicBlockId> topological_order_;
   // Indexes in topological_order_ need to be only as big as the BasicBlockId.
   COMPILE_ASSERT(sizeof(BasicBlockId) == sizeof(uint16_t), assuming_16_bit_BasicBlockId);
   // For each loop head, remember the past-the-end index of the end of the loop. 0 if not loop head.
-  GrowableArray<uint16_t>* topological_order_loop_ends_;
+  ArenaVector<uint16_t> topological_order_loop_ends_;
   // Map BB ids to topological_order_ indexes. 0xffff if not included (hidden or null block).
-  GrowableArray<uint16_t>* topological_order_indexes_;
+  ArenaVector<uint16_t> topological_order_indexes_;
   // Stack of the loop head indexes and recalculation flags for RepeatingTopologicalSortIterator.
-  GrowableArray<std::pair<uint16_t, bool>>* topological_order_loop_head_stack_;
+  ArenaVector<std::pair<uint16_t, bool>> topological_order_loop_head_stack_;
   int* i_dom_list_;
-  ArenaBitVector** def_block_matrix_;    // num_dalvik_register x num_blocks.
+  ArenaBitVector** def_block_matrix_;    // original num registers x num_blocks.
   std::unique_ptr<ScopedArenaAllocator> temp_scoped_alloc_;
   uint16_t* temp_insn_data_;
   uint32_t temp_bit_vector_size_;
   ArenaBitVector* temp_bit_vector_;
   std::unique_ptr<GlobalValueNumbering> temp_gvn_;
   static const int kInvalidEntry = -1;
-  GrowableArray<BasicBlock*> block_list_;
+  ArenaVector<BasicBlock*> block_list_;
   ArenaBitVector* try_block_addr_;
   BasicBlock* entry_block_;
   BasicBlock* exit_block_;
   unsigned int num_blocks_;
   const DexFile::CodeItem* current_code_item_;
-  GrowableArray<uint16_t> dex_pc_to_block_map_;  // FindBlock lookup cache.
+  ArenaVector<uint16_t> dex_pc_to_block_map_;    // FindBlock lookup cache.
   ArenaVector<DexCompilationUnit*> m_units_;     // List of methods included in this graph
   typedef std::pair<int, int> MIRLocation;       // Insert point, (m_unit_ index, offset)
   ArenaVector<MIRLocation> method_stack_;        // Include stack
@@ -1189,17 +1289,19 @@
   ArenaAllocator* arena_;
   int backward_branches_;
   int forward_branches_;
-  GrowableArray<CompilerTemp*> compiler_temps_;
-  size_t num_non_special_compiler_temps_;
-  size_t max_available_non_special_compiler_temps_;
-  size_t max_available_special_compiler_temps_;
-  bool punt_to_interpreter_;                    // Difficult or not worthwhile - just interpret.
+  size_t num_non_special_compiler_temps_;  // Keeps track of allocated non-special compiler temps. These are VRs that are in compiler temp region on stack.
+  size_t max_available_non_special_compiler_temps_;  // Keeps track of maximum available non-special temps.
+  size_t max_available_special_compiler_temps_;      // Keeps track of maximum available special temps.
+  bool requested_backend_temp_;            // Keeps track whether BE temps have been requested.
+  size_t reserved_temps_for_backend_;      // Keeps track of the remaining temps that are reserved for BE.
+  bool compiler_temps_committed_;          // Keeps track whether number of temps has been frozen (for example post frame size calculation).
+  bool punt_to_interpreter_;               // Difficult or not worthwhile - just interpret.
   uint64_t merged_df_flags_;
-  GrowableArray<MirIFieldLoweringInfo> ifield_lowering_infos_;
-  GrowableArray<MirSFieldLoweringInfo> sfield_lowering_infos_;
-  GrowableArray<MirMethodLoweringInfo> method_lowering_infos_;
+  ArenaVector<MirIFieldLoweringInfo> ifield_lowering_infos_;
+  ArenaVector<MirSFieldLoweringInfo> sfield_lowering_infos_;
+  ArenaVector<MirMethodLoweringInfo> method_lowering_infos_;
   static const uint64_t oat_data_flow_attributes_[kMirOpLast];
-  GrowableArray<BasicBlock*> gen_suspend_test_list_;  // List of blocks containing suspend tests
+  ArenaVector<BasicBlock*> gen_suspend_test_list_;  // List of blocks containing suspend tests
 
   friend class ClassInitCheckEliminationTest;
   friend class GlobalValueNumberingTest;
diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc
index 932f453..a96cd84 100644
--- a/compiler/dex/mir_graph_test.cc
+++ b/compiler/dex/mir_graph_test.cc
@@ -57,52 +57,48 @@
 
   void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
     cu_.mir_graph->block_id_map_.clear();
-    cu_.mir_graph->block_list_.Reset();
+    cu_.mir_graph->block_list_.clear();
     ASSERT_LT(3u, count);  // null, entry, exit and at least one bytecode block.
     ASSERT_EQ(kNullBlock, defs[0].type);
     ASSERT_EQ(kEntryBlock, defs[1].type);
     ASSERT_EQ(kExitBlock, defs[2].type);
     for (size_t i = 0u; i != count; ++i) {
       const BBDef* def = &defs[i];
-      BasicBlock* bb = cu_.mir_graph->NewMemBB(def->type, i);
-      cu_.mir_graph->block_list_.Insert(bb);
+      BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
       if (def->num_successors <= 2) {
         bb->successor_block_list_type = kNotUsed;
-        bb->successor_blocks = nullptr;
         bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
         bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
       } else {
         bb->successor_block_list_type = kPackedSwitch;
         bb->fall_through = 0u;
         bb->taken = 0u;
-        bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
-            &cu_.arena, def->num_successors, kGrowableArraySuccessorBlocks);
+        bb->successor_blocks.reserve(def->num_successors);
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
                                                                kArenaAllocSuccessor));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
-          bb->successor_blocks->Insert(successor_block_info);
+          bb->successor_blocks.push_back(successor_block_info);
         }
       }
-      bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
-          &cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
-      for (size_t j = 0u; j != def->num_predecessors; ++j) {
-        ASSERT_NE(0u, def->predecessors[j]);
-        bb->predecessors->Insert(def->predecessors[j]);
-      }
+      bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
       if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
         bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
             cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
       }
     }
     cu_.mir_graph->num_blocks_ = count;
-    ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
-    cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
+    ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
+    cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
-    cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
+    cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
     ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
+
+    DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(cu_.arena.Alloc(sizeof(DexFile::CodeItem),
+                                                                                   kArenaAllocMisc));
+    cu_.mir_graph->current_code_item_ = code_item;
   }
 
   template <size_t count>
@@ -116,21 +112,21 @@
     cu_.mir_graph->ComputeDominators();
     cu_.mir_graph->ComputeTopologicalSortOrder();
     cu_.mir_graph->SSATransformationEnd();
-    ASSERT_NE(cu_.mir_graph->topological_order_, nullptr);
-    ASSERT_NE(cu_.mir_graph->topological_order_loop_ends_, nullptr);
-    ASSERT_NE(cu_.mir_graph->topological_order_indexes_, nullptr);
-    ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_->Size());
-    for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder()->Size(); i != size; ++i) {
-      ASSERT_LT(cu_.mir_graph->topological_order_->Get(i), cu_.mir_graph->GetNumBlocks());
-      BasicBlockId id = cu_.mir_graph->topological_order_->Get(i);
-      EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_->Get(id));
+    ASSERT_FALSE(cu_.mir_graph->topological_order_.empty());
+    ASSERT_FALSE(cu_.mir_graph->topological_order_loop_ends_.empty());
+    ASSERT_FALSE(cu_.mir_graph->topological_order_indexes_.empty());
+    ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_.size());
+    for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder().size(); i != size; ++i) {
+      ASSERT_LT(cu_.mir_graph->topological_order_[i], cu_.mir_graph->GetNumBlocks());
+      BasicBlockId id = cu_.mir_graph->topological_order_[i];
+      EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_[id]);
     }
   }
 
   void DoCheckOrder(const BasicBlockId* ids, size_t count) {
-    ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder()->Size());
+    ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder().size());
     for (size_t i = 0; i != count; ++i) {
-      EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()->Get(i)) << i;
+      EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()[i]) << i;
     }
   }
 
@@ -141,8 +137,8 @@
 
   void DoCheckLoopEnds(const uint16_t* ends, size_t count) {
     for (size_t i = 0; i != count; ++i) {
-      ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds()->Size());
-      EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()->Get(i)) << i;
+      ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds().size());
+      EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()[i]) << i;
     }
   }
 
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 23ceb56..dac71f6 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "base/bit_vector-inl.h"
 #include "compiler_internals.h"
 #include "global_value_numbering.h"
 #include "local_value_numbering.h"
@@ -21,21 +22,22 @@
 #include "dex/global_value_numbering.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "stack.h"
 #include "utils/scoped_arena_containers.h"
 
 namespace art {
 
 static unsigned int Predecessors(BasicBlock* bb) {
-  return bb->predecessors->Size();
+  return bb->predecessors.size();
 }
 
 /* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */
-void MIRGraph::SetConstant(int32_t ssa_reg, int value) {
+void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) {
   is_constant_v_->SetBit(ssa_reg);
   constant_values_[ssa_reg] = value;
 }
 
-void MIRGraph::SetConstantWide(int ssa_reg, int64_t value) {
+void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) {
   is_constant_v_->SetBit(ssa_reg);
   is_constant_v_->SetBit(ssa_reg + 1);
   constant_values_[ssa_reg] = Low32Bits(value);
@@ -228,30 +230,63 @@
 COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_LEZ) == kCondLe, check_if_lez_ccode);
 
 int MIRGraph::GetSSAUseCount(int s_reg) {
-  return raw_use_counts_.Get(s_reg);
+  DCHECK_LT(static_cast<size_t>(s_reg), ssa_subscripts_.size());
+  return raw_use_counts_[s_reg];
 }
 
-size_t MIRGraph::GetNumAvailableNonSpecialCompilerTemps() {
-  if (num_non_special_compiler_temps_ >= max_available_non_special_compiler_temps_) {
+size_t MIRGraph::GetNumBytesForSpecialTemps() const {
+  // This logic is written with assumption that Method* is only special temp.
+  DCHECK_EQ(max_available_special_compiler_temps_, 1u);
+  return sizeof(StackReference<mirror::ArtMethod>);
+}
+
+size_t MIRGraph::GetNumAvailableVRTemps() {
+  // First take into account all temps reserved for backend.
+  if (max_available_non_special_compiler_temps_ < reserved_temps_for_backend_) {
+    return 0;
+  }
+
+  // Calculate remaining ME temps available.
+  size_t remaining_me_temps = max_available_non_special_compiler_temps_ - reserved_temps_for_backend_;
+
+  if (num_non_special_compiler_temps_ >= remaining_me_temps) {
     return 0;
   } else {
-    return max_available_non_special_compiler_temps_ - num_non_special_compiler_temps_;
+    return remaining_me_temps - num_non_special_compiler_temps_;
   }
 }
 
-
 // FIXME - will probably need to revisit all uses of this, as type not defined.
 static const RegLocation temp_loc = {kLocCompilerTemp,
                                      0, 1 /*defined*/, 0, 0, 0, 0, 0, 1 /*home*/,
                                      RegStorage(), INVALID_SREG, INVALID_SREG};
 
 CompilerTemp* MIRGraph::GetNewCompilerTemp(CompilerTempType ct_type, bool wide) {
-  // There is a limit to the number of non-special temps so check to make sure it wasn't exceeded.
-  if (ct_type == kCompilerTempVR) {
-    size_t available_temps = GetNumAvailableNonSpecialCompilerTemps();
-    if (available_temps <= 0 || (available_temps <= 1 && wide)) {
-      return 0;
+  // Once the compiler temps have been committed, new ones cannot be requested anymore.
+  DCHECK_EQ(compiler_temps_committed_, false);
+  // Make sure that reserved for BE set is sane.
+  DCHECK_LE(reserved_temps_for_backend_, max_available_non_special_compiler_temps_);
+
+  bool verbose = cu_->verbose;
+  const char* ct_type_str = nullptr;
+
+  if (verbose) {
+    switch (ct_type) {
+      case kCompilerTempBackend:
+        ct_type_str = "backend";
+        break;
+      case kCompilerTempSpecialMethodPtr:
+        ct_type_str = "method*";
+        break;
+      case kCompilerTempVR:
+        ct_type_str = "VR";
+        break;
+      default:
+        ct_type_str = "unknown";
+        break;
     }
+    LOG(INFO) << "CompilerTemps: A compiler temp of type " << ct_type_str << " that is "
+        << (wide ? "wide is being requested." : "not wide is being requested.");
   }
 
   CompilerTemp *compiler_temp = static_cast<CompilerTemp *>(arena_->Alloc(sizeof(CompilerTemp),
@@ -260,51 +295,100 @@
   // Create the type of temp requested. Special temps need special handling because
   // they have a specific virtual register assignment.
   if (ct_type == kCompilerTempSpecialMethodPtr) {
+    // This has a special location on stack which is 32-bit or 64-bit depending
+    // on mode. However, we don't want to overlap with non-special section
+    // and thus even for 64-bit, we allow only a non-wide temp to be requested.
     DCHECK_EQ(wide, false);
-    compiler_temp->v_reg = static_cast<int>(kVRegMethodPtrBaseReg);
-    compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
 
-    // The MIR graph keeps track of the sreg for method pointer specially, so record that now.
-    method_sreg_ = compiler_temp->s_reg_low;
+    // The vreg is always the first special temp for method ptr.
+    compiler_temp->v_reg = GetFirstSpecialTempVR();
+
+  } else if (ct_type == kCompilerTempBackend) {
+    requested_backend_temp_ = true;
+
+    // Make sure that we are not exceeding temps reserved for BE.
+    // Since VR temps cannot be requested once the BE temps are requested, we
+    // allow reservation of VR temps as well for BE. We
+    size_t available_temps = reserved_temps_for_backend_ + GetNumAvailableVRTemps();
+    if (available_temps <= 0 || (available_temps <= 1 && wide)) {
+      if (verbose) {
+        LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str << " are available.";
+      }
+      return nullptr;
+    }
+
+    // Update the remaining reserved temps since we have now used them.
+    // Note that the code below is actually subtracting to remove them from reserve
+    // once they have been claimed. It is careful to not go below zero.
+    if (reserved_temps_for_backend_ >= 1) {
+      reserved_temps_for_backend_--;
+    }
+    if (wide && reserved_temps_for_backend_ >= 1) {
+      reserved_temps_for_backend_--;
+    }
+
+    // The new non-special compiler temp must receive a unique v_reg.
+    compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_;
+    num_non_special_compiler_temps_++;
+  } else if (ct_type == kCompilerTempVR) {
+    // Once we start giving out BE temps, we don't allow anymore ME temps to be requested.
+    // This is done in order to prevent problems with ssa since these structures are allocated
+    // and managed by the ME.
+    DCHECK_EQ(requested_backend_temp_, false);
+
+    // There is a limit to the number of non-special temps so check to make sure it wasn't exceeded.
+    size_t available_temps = GetNumAvailableVRTemps();
+    if (available_temps <= 0 || (available_temps <= 1 && wide)) {
+      if (verbose) {
+        LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str << " are available.";
+      }
+      return nullptr;
+    }
+
+    // The new non-special compiler temp must receive a unique v_reg.
+    compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_;
+    num_non_special_compiler_temps_++;
   } else {
-    DCHECK_EQ(ct_type, kCompilerTempVR);
+    UNIMPLEMENTED(FATAL) << "No handling for compiler temp type " << ct_type_str << ".";
+  }
 
-    // The new non-special compiler temp must receive a unique v_reg with a negative value.
-    compiler_temp->v_reg = static_cast<int>(kVRegNonSpecialTempBaseReg) -
-        num_non_special_compiler_temps_;
-    compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
+  // We allocate an sreg as well to make developer life easier.
+  // However, if this is requested from an ME pass that will recalculate ssa afterwards,
+  // this sreg is no longer valid. The caller should be aware of this.
+  compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
+
+  if (verbose) {
+    LOG(INFO) << "CompilerTemps: New temp of type " << ct_type_str << " with v" << compiler_temp->v_reg
+        << " and s" << compiler_temp->s_reg_low << " has been created.";
+  }
+
+  if (wide) {
+    // Only non-special temps are handled as wide for now.
+    // Note that the number of non special temps is incremented below.
+    DCHECK(ct_type == kCompilerTempBackend || ct_type == kCompilerTempVR);
+
+    // Ensure that the two registers are consecutive.
+    int ssa_reg_low = compiler_temp->s_reg_low;
+    int ssa_reg_high = AddNewSReg(compiler_temp->v_reg + 1);
     num_non_special_compiler_temps_++;
 
-    if (wide) {
-      // Create a new CompilerTemp for the high part.
-      CompilerTemp *compiler_temp_high =
-          static_cast<CompilerTemp *>(arena_->Alloc(sizeof(CompilerTemp), kArenaAllocRegAlloc));
-      compiler_temp_high->v_reg = compiler_temp->v_reg;
-      compiler_temp_high->s_reg_low = compiler_temp->s_reg_low;
-      compiler_temps_.Insert(compiler_temp_high);
+    if (verbose) {
+      LOG(INFO) << "CompilerTemps: The wide part of temp of type " << ct_type_str << " is v"
+          << compiler_temp->v_reg + 1 << " and s" << ssa_reg_high << ".";
+    }
 
-      // Ensure that the two registers are consecutive. Since the virtual registers used for temps
-      // grow in a negative fashion, we need the smaller to refer to the low part. Thus, we
-      // redefine the v_reg and s_reg_low.
-      compiler_temp->v_reg--;
-      int ssa_reg_high = compiler_temp->s_reg_low;
-      compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
-      int ssa_reg_low = compiler_temp->s_reg_low;
-
-      // If needed initialize the register location for the high part.
-      // The low part is handled later in this method on a common path.
-      if (reg_location_ != nullptr) {
-        reg_location_[ssa_reg_high] = temp_loc;
-        reg_location_[ssa_reg_high].high_word = 1;
-        reg_location_[ssa_reg_high].s_reg_low = ssa_reg_low;
-        reg_location_[ssa_reg_high].wide = true;
-      }
-
-      num_non_special_compiler_temps_++;
+    if (reg_location_ != nullptr) {
+      reg_location_[ssa_reg_high] = temp_loc;
+      reg_location_[ssa_reg_high].high_word = true;
+      reg_location_[ssa_reg_high].s_reg_low = ssa_reg_low;
+      reg_location_[ssa_reg_high].wide = true;
     }
   }
 
-  // Have we already allocated the register locations?
+  // If the register locations have already been allocated, add the information
+  // about the temp. We will not overflow because they have been initialized
+  // to support the maximum number of temps. For ME temps that have multiple
+  // ssa versions, the structures below will be expanded on the post pass cleanup.
   if (reg_location_ != nullptr) {
     int ssa_reg_low = compiler_temp->s_reg_low;
     reg_location_[ssa_reg_low] = temp_loc;
@@ -312,7 +396,6 @@
     reg_location_[ssa_reg_low].wide = wide;
   }
 
-  compiler_temps_.Insert(compiler_temp);
   return compiler_temp;
 }
 
@@ -596,26 +679,41 @@
   }
 }
 
-/* Try to make common case the fallthrough path */
+/* Try to make common case the fallthrough path. */
 bool MIRGraph::LayoutBlocks(BasicBlock* bb) {
-  // TODO: For now, just looking for direct throws.  Consider generalizing for profile feedback
+  // TODO: For now, just looking for direct throws.  Consider generalizing for profile feedback.
   if (!bb->explicit_throw) {
     return false;
   }
+
+  // If we visited it, we are done.
+  if (bb->visited) {
+    return false;
+  }
+  bb->visited = true;
+
   BasicBlock* walker = bb;
   while (true) {
-    // Check termination conditions
+    // Check termination conditions.
     if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) {
       break;
     }
-    BasicBlock* prev = GetBasicBlock(walker->predecessors->Get(0));
+    DCHECK(!walker->predecessors.empty());
+    BasicBlock* prev = GetBasicBlock(walker->predecessors[0]);
+
+    // If we visited the predecessor, we are done.
+    if (prev->visited) {
+      return false;
+    }
+    prev->visited = true;
+
     if (prev->conditional_branch) {
       if (GetBasicBlock(prev->fall_through) == walker) {
-        // Already done - return
+        // Already done - return.
         break;
       }
       DCHECK_EQ(walker, GetBasicBlock(prev->taken));
-      // Got one.  Flip it and exit
+      // Got one.  Flip it and exit.
       Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode;
       switch (opcode) {
         case Instruction::IF_EQ: opcode = Instruction::IF_NE; break;
@@ -639,6 +737,10 @@
       break;
     }
     walker = prev;
+
+    if (walker->visited) {
+      break;
+    }
   }
   return false;
 }
@@ -684,7 +786,7 @@
     *bb->last_mir_insn = *throw_insn;
     // Use the successor info from the next block
     bb->successor_block_list_type = bb_next->successor_block_list_type;
-    bb->successor_blocks = bb_next->successor_blocks;
+    bb->successor_blocks.swap(bb_next->successor_blocks);  // Swap instead of copying.
     // Use the ending block linkage from the next block
     bb->fall_through = bb_next->fall_through;
     GetBasicBlock(bb->taken)->block_type = kDead;  // Kill the unused exception block
@@ -692,10 +794,13 @@
     // Include the rest of the instructions
     bb->last_mir_insn = bb_next->last_mir_insn;
     /*
-     * If lower-half of pair of blocks to combine contained a return, move the flag
-     * to the newly combined block.
+     * If lower-half of pair of blocks to combine contained
+     * a return or a conditional branch or an explicit throw,
+     * move the flag to the newly combined block.
      */
     bb->terminated_by_return = bb_next->terminated_by_return;
+    bb->conditional_branch = bb_next->conditional_branch;
+    bb->explicit_throw = bb_next->explicit_throw;
 
     /*
      * NOTE: we aren't updating all dataflow info here.  Should either make sure this pass
@@ -744,17 +849,17 @@
     if (bb->block_type == kEntryBlock) {
       ssa_regs_to_check->ClearAllBits();
       // Assume all ins are objects.
-      for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins;
-           in_reg < cu_->num_dalvik_registers; in_reg++) {
+      for (uint16_t in_reg = GetFirstInVR();
+           in_reg < GetNumOfCodeVRs(); in_reg++) {
         ssa_regs_to_check->SetBit(in_reg);
       }
       if ((cu_->access_flags & kAccStatic) == 0) {
         // If non-static method, mark "this" as non-null
-        int this_reg = cu_->num_dalvik_registers - cu_->num_ins;
+        int this_reg = GetFirstInVR();
         ssa_regs_to_check->ClearBit(this_reg);
       }
-    } else if (bb->predecessors->Size() == 1) {
-      BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0));
+    } else if (bb->predecessors.size() == 1) {
+      BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
       // pred_bb must have already been processed at least once.
       DCHECK(pred_bb->data_flow_info->ending_check_v != nullptr);
       ssa_regs_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
@@ -780,25 +885,22 @@
       }
     } else {
       // Starting state is union of all incoming arcs
-      GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
-      BasicBlock* pred_bb = GetBasicBlock(iter.Next());
-      CHECK(pred_bb != NULL);
-      while (pred_bb->data_flow_info->ending_check_v == nullptr) {
-        pred_bb = GetBasicBlock(iter.Next());
-        // At least one predecessor must have been processed before this bb.
+      bool copied_first = false;
+      for (BasicBlockId pred_id : bb->predecessors) {
+        BasicBlock* pred_bb = GetBasicBlock(pred_id);
         DCHECK(pred_bb != nullptr);
         DCHECK(pred_bb->data_flow_info != nullptr);
-      }
-      ssa_regs_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
-      while (true) {
-        pred_bb = GetBasicBlock(iter.Next());
-        if (!pred_bb) break;
-        DCHECK(pred_bb->data_flow_info != nullptr);
         if (pred_bb->data_flow_info->ending_check_v == nullptr) {
           continue;
         }
-        ssa_regs_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+        if (!copied_first) {
+          copied_first = true;
+          ssa_regs_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
+        } else {
+          ssa_regs_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+        }
       }
+      DCHECK(copied_first);  // At least one predecessor must have been processed before this bb.
     }
     // At this point, ssa_regs_to_check shows which sregs have an object definition with
     // no intervening uses.
@@ -964,7 +1066,7 @@
   temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
 
   // Each insn we use here has at least 2 code units, offset/2 will be a unique index.
-  const size_t end = (cu_->code_item->insns_size_in_code_units_ + 1u) / 2u;
+  const size_t end = (GetNumDalvikInsns() + 1u) / 2u;
   temp_insn_data_ = static_cast<uint16_t*>(
       temp_scoped_alloc_->Alloc(end * sizeof(*temp_insn_data_), kArenaAllocGrowableArray));
 
@@ -1053,35 +1155,31 @@
   DCHECK(classes_to_check != nullptr);
   if (bb->block_type == kEntryBlock) {
     classes_to_check->SetInitialBits(temp_bit_vector_size_);
-  } else if (bb->predecessors->Size() == 1) {
-    BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0));
+  } else if (bb->predecessors.size() == 1) {
+    BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
     // pred_bb must have already been processed at least once.
     DCHECK(pred_bb != nullptr);
     DCHECK(pred_bb->data_flow_info != nullptr);
     DCHECK(pred_bb->data_flow_info->ending_check_v != nullptr);
     classes_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
   } else {
-    // Starting state is union of all incoming arcs
-    GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
-    BasicBlock* pred_bb = GetBasicBlock(iter.Next());
-    DCHECK(pred_bb != NULL);
-    DCHECK(pred_bb->data_flow_info != NULL);
-    while (pred_bb->data_flow_info->ending_check_v == nullptr) {
-      pred_bb = GetBasicBlock(iter.Next());
-      // At least one predecessor must have been processed before this bb.
+    // Starting state is union of all incoming arcs.
+    bool copied_first = false;
+    for (BasicBlockId pred_id : bb->predecessors) {
+      BasicBlock* pred_bb = GetBasicBlock(pred_id);
       DCHECK(pred_bb != nullptr);
       DCHECK(pred_bb->data_flow_info != nullptr);
-    }
-    classes_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
-    while (true) {
-      pred_bb = GetBasicBlock(iter.Next());
-      if (!pred_bb) break;
-      DCHECK(pred_bb->data_flow_info != nullptr);
       if (pred_bb->data_flow_info->ending_check_v == nullptr) {
         continue;
       }
-      classes_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+      if (!copied_first) {
+        copied_first = true;
+        classes_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
+      } else {
+        classes_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+      }
     }
+    DCHECK(copied_first);  // At least one predecessor must have been processed before this bb.
   }
   // At this point, classes_to_check shows which classes need clinit checks.
 
@@ -1209,8 +1307,8 @@
   MirIFieldLoweringInfo::Resolve(cu_->compiler_driver, &inlined_unit, &inlined_field_info, 1u);
   DCHECK(inlined_field_info.IsResolved());
 
-  uint32_t field_info_index = ifield_lowering_infos_.Size();
-  ifield_lowering_infos_.Insert(inlined_field_info);
+  uint32_t field_info_index = ifield_lowering_infos_.size();
+  ifield_lowering_infos_.push_back(inlined_field_info);
   temp_bit_vector_->SetBit(method_index);
   temp_insn_data_[method_index] = field_info_index;
   iget_or_iput->meta.ifield_lowering_info = field_info_index;
@@ -1218,7 +1316,7 @@
 
 bool MIRGraph::InlineSpecialMethodsGate() {
   if ((cu_->disable_opt & (1 << kSuppressMethodInlining)) != 0 ||
-      method_lowering_infos_.Size() == 0u) {
+      method_lowering_infos_.size() == 0u) {
     return false;
   }
   if (cu_->compiler_driver->GetMethodInlinerMap() == nullptr) {
@@ -1234,7 +1332,7 @@
 
   DCHECK(temp_scoped_alloc_.get() == nullptr);
   temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
-  temp_bit_vector_size_ = method_lowering_infos_.Size();
+  temp_bit_vector_size_ = method_lowering_infos_.size();
   temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
       temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapMisc);
   temp_bit_vector_->ClearAllBits();
@@ -1250,18 +1348,27 @@
     if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) {
       continue;
     }
-    if (!(Instruction::FlagsOf(mir->dalvikInsn.opcode) & Instruction::kInvoke)) {
+    if (!(mir->dalvikInsn.FlagsOf() & Instruction::kInvoke)) {
       continue;
     }
     const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir);
     if (!method_info.FastPath()) {
       continue;
     }
+
     InvokeType sharp_type = method_info.GetSharpType();
-    if ((sharp_type != kDirect) &&
-        (sharp_type != kStatic || method_info.NeedsClassInitialization())) {
+    if ((sharp_type != kDirect) && (sharp_type != kStatic)) {
       continue;
     }
+
+    if (sharp_type == kStatic) {
+      bool needs_clinit = method_info.NeedsClassInitialization() &&
+          ((mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0);
+      if (needs_clinit) {
+        continue;
+      }
+    }
+
     DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
     MethodReference target = method_info.GetTargetMethod();
     if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(target.dex_file)
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index c510b52..55e547e 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -76,8 +76,8 @@
     { opcode, bb, field_info }
 
   void DoPrepareSFields(const SFieldDef* defs, size_t count) {
-    cu_.mir_graph->sfield_lowering_infos_.Reset();
-    cu_.mir_graph->sfield_lowering_infos_.Resize(count);
+    cu_.mir_graph->sfield_lowering_infos_.clear();
+    cu_.mir_graph->sfield_lowering_infos_.reserve(count);
     for (size_t i = 0u; i != count; ++i) {
       const SFieldDef* def = &defs[i];
       MirSFieldLoweringInfo field_info(def->field_idx);
@@ -89,7 +89,7 @@
       }
       ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
       ASSERT_FALSE(field_info.IsInitialized());
-      cu_.mir_graph->sfield_lowering_infos_.Insert(field_info);
+      cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
     }
   }
 
@@ -100,51 +100,43 @@
 
   void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
     cu_.mir_graph->block_id_map_.clear();
-    cu_.mir_graph->block_list_.Reset();
+    cu_.mir_graph->block_list_.clear();
     ASSERT_LT(3u, count);  // null, entry, exit and at least one bytecode block.
     ASSERT_EQ(kNullBlock, defs[0].type);
     ASSERT_EQ(kEntryBlock, defs[1].type);
     ASSERT_EQ(kExitBlock, defs[2].type);
     for (size_t i = 0u; i != count; ++i) {
       const BBDef* def = &defs[i];
-      BasicBlock* bb = cu_.mir_graph->NewMemBB(def->type, i);
-      cu_.mir_graph->block_list_.Insert(bb);
+      BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
       if (def->num_successors <= 2) {
         bb->successor_block_list_type = kNotUsed;
-        bb->successor_blocks = nullptr;
         bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
         bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
       } else {
         bb->successor_block_list_type = kPackedSwitch;
         bb->fall_through = 0u;
         bb->taken = 0u;
-        bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
-            &cu_.arena, def->num_successors, kGrowableArraySuccessorBlocks);
+        bb->successor_blocks.reserve(def->num_successors);
         for (size_t j = 0u; j != def->num_successors; ++j) {
           SuccessorBlockInfo* successor_block_info =
               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
                                                                kArenaAllocSuccessor));
           successor_block_info->block = j;
           successor_block_info->key = 0u;  // Not used by class init check elimination.
-          bb->successor_blocks->Insert(successor_block_info);
+          bb->successor_blocks.push_back(successor_block_info);
         }
       }
-      bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
-          &cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
-      for (size_t j = 0u; j != def->num_predecessors; ++j) {
-        ASSERT_NE(0u, def->predecessors[j]);
-        bb->predecessors->Insert(def->predecessors[j]);
-      }
+      bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
       if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
         bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
             cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
       }
     }
     cu_.mir_graph->num_blocks_ = count;
-    ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
-    cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
+    ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
+    cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
-    cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
+    cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
     ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
   }
 
@@ -161,11 +153,11 @@
       const MIRDef* def = &defs[i];
       MIR* mir = &mirs_[i];
       mir->dalvikInsn.opcode = def->opcode;
-      ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.Size());
-      BasicBlock* bb = cu_.mir_graph->block_list_.Get(def->bbid);
+      ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size());
+      BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
       bb->AppendMIR(mir);
       if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
-        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.Size());
+        ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size());
         mir->meta.sfield_lowering_info = def->field_or_method_info;
       }
       mir->ssa_rep = nullptr;
@@ -179,7 +171,7 @@
         cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
     memset(code_item_, 0, sizeof(DexFile::CodeItem));
     code_item_->insns_size_in_code_units_ = 2u * count;
-    cu_.mir_graph->current_code_item_ = cu_.code_item = code_item_;
+    cu_.mir_graph->current_code_item_ = code_item_;
   }
 
   template <size_t count>
@@ -408,12 +400,10 @@
   // Add successor block info to the check block.
   BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
   check_bb->successor_block_list_type = kCatch;
-  check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
-      &cu_.arena, 2, kGrowableArraySuccessorBlocks);
   SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
       (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
   successor_block_info->block = catch_handler->id;
-  check_bb->successor_blocks->Insert(successor_block_info);
+  check_bb->successor_blocks.push_back(successor_block_info);
   PrepareMIRs(mirs);
   PerformClassInitCheckElimination();
   ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h
index dbb5366..c377426 100644
--- a/compiler/dex/pass.h
+++ b/compiler/dex/pass.h
@@ -81,7 +81,7 @@
    * @param data the object containing data necessary for the pass.
    * @return whether or not there is a change when walking the BasicBlock
    */
-  virtual bool Worker(const PassDataHolder* data) const {
+  virtual bool Worker(PassDataHolder* data) const {
     // Unused parameter.
     UNUSED(data);
 
diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h
index bd8f53c..8a3eae1 100644
--- a/compiler/dex/pass_driver.h
+++ b/compiler/dex/pass_driver.h
@@ -161,6 +161,17 @@
     print_pass_list_ = list;
   }
 
+  /**
+   * @brief Used to set a string that contains the overridden pass options.
+   * @details An overridden pass option means that the pass uses this option
+   * instead of using its default option.
+   * @param s The string passed by user with overridden options. The string is in format
+   * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting
+   */
+  static void SetOverriddenPassOptions(const std::string& s) {
+    overridden_pass_options_list_ = s;
+  }
+
   void SetDefaultPasses() {
     pass_list_ = PassDriver<PassDriverType>::g_default_pass_list;
   }
@@ -206,6 +217,9 @@
 
   /** @brief What are the passes we want to be dumping the CFG? */
   static std::string dump_pass_list_;
+
+  /** @brief String of all options that should be overridden for selected passes */
+  static std::string overridden_pass_options_list_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h
index 133593c..537ceb6 100644
--- a/compiler/dex/pass_driver_me.h
+++ b/compiler/dex/pass_driver_me.h
@@ -17,6 +17,8 @@
 #ifndef ART_COMPILER_DEX_PASS_DRIVER_ME_H_
 #define ART_COMPILER_DEX_PASS_DRIVER_ME_H_
 
+#include <cstdlib>
+#include <cstring>
 #include "bb_optimizations.h"
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
@@ -94,19 +96,27 @@
       c_unit->NewTimingSplit(pass->GetName());
     }
 
+    // First, work on determining pass verbosity.
+    bool old_print_pass = c_unit->print_pass;
+    c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
+    const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
+    if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
+      c_unit->print_pass = true;
+    }
+
+    // Next, check if there are any overridden settings for the pass that change default configuration.
+    c_unit->overridden_pass_options.clear();
+    FillOverriddenPassSettings(pass->GetName(), c_unit->overridden_pass_options);
+    if (c_unit->print_pass) {
+      for (auto setting_it : c_unit->overridden_pass_options) {
+        LOG(INFO) << "Overridden option \"" << setting_it.first << ":"
+          << setting_it.second << "\" for pass \"" << pass->GetName() << "\"";
+      }
+    }
+
     // Check the pass gate first.
     bool should_apply_pass = pass->Gate(&pass_me_data_holder_);
     if (should_apply_pass) {
-      bool old_print_pass = c_unit->print_pass;
-
-      c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
-
-      const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
-
-      if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
-        c_unit->print_pass = true;
-      }
-
       // Applying the pass: first start, doWork, and end calls.
       this->ApplyPass(&pass_me_data_holder_, pass);
 
@@ -137,10 +147,11 @@
           }
         }
       }
-
-      c_unit->print_pass = old_print_pass;
     }
 
+    // Before wrapping up with this pass, restore old pass verbosity flag.
+    c_unit->print_pass = old_print_pass;
+
     // If the pass gate passed, we can declare success.
     return should_apply_pass;
   }
@@ -149,6 +160,18 @@
     return dump_cfg_folder_;
   }
 
+  static void PrintPassOptions() {
+    for (auto pass : PassDriver<PassDriverType>::g_default_pass_list) {
+      const PassME* me_pass = down_cast<const PassME*>(pass);
+      if (me_pass->HasOptions()) {
+        LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:";
+        SafeMap<const std::string, int> overridden_settings;
+        FillOverriddenPassSettings(me_pass->GetName(), overridden_settings);
+        me_pass->PrintPassOptions(overridden_settings);
+      }
+    }
+  }
+
  protected:
   /** @brief The data holder that contains data needed for the PassDriverME. */
   PassMEDataHolder pass_me_data_holder_;
@@ -175,6 +198,97 @@
       Iterator iterator(c_unit->mir_graph.get());
       DoWalkBasicBlocks(data, pass, &iterator);
     }
+
+  /**
+   * @brief Fills the settings_to_fill by finding all of the applicable options in the overridden_pass_options_list_.
+   * @param pass_name The pass name for which to fill settings.
+   * @param settings_to_fill Fills the options to contain the mapping of name of option to the new configuration.
+   */
+  static void FillOverriddenPassSettings(const char* pass_name, SafeMap<const std::string, int>& settings_to_fill) {
+    const std::string& settings = PassDriver<PassDriverType>::overridden_pass_options_list_;
+    const size_t settings_len = settings.size();
+
+    // Before anything, check if we care about anything right now.
+    if (settings_len == 0) {
+      return;
+    }
+
+    const size_t pass_name_len = strlen(pass_name);
+    const size_t min_setting_size = 4;  // 2 delimiters, 1 setting name, 1 setting
+    size_t search_pos = 0;
+
+    // If there is no room for pass options, exit early.
+    if (settings_len < pass_name_len + min_setting_size) {
+      return;
+    }
+
+    do {
+      search_pos = settings.find(pass_name, search_pos);
+
+      // Check if we found this pass name in rest of string.
+      if (search_pos == std::string::npos) {
+        // No more settings for this pass.
+        break;
+      }
+
+      // The string contains the pass name. Now check that there is
+      // room for the settings: at least one char for setting name,
+      // two chars for two delimiter, and at least one char for setting.
+      if (search_pos + pass_name_len + min_setting_size >= settings_len) {
+        // No more settings for this pass.
+        break;
+      }
+
+      // Update the current search position to not include the pass name.
+      search_pos += pass_name_len;
+
+      // The format must be "PassName:SettingName:#" where # is the setting.
+      // Thus look for the first ":" which must exist.
+      if (settings[search_pos] != ':') {
+        // Missing delimiter right after pass name.
+        continue;
+      } else {
+        search_pos += 1;
+      }
+
+      // Now look for the actual setting by finding the next ":" delimiter.
+      const size_t setting_name_pos = search_pos;
+      size_t setting_pos = settings.find(':', setting_name_pos);
+
+      if (setting_pos == std::string::npos) {
+        // Missing a delimiter that would capture where setting starts.
+        continue;
+      } else if (setting_pos == setting_name_pos) {
+        // Missing setting thus did not move from setting name
+        continue;
+      } else {
+        // Skip the delimiter.
+        setting_pos += 1;
+      }
+
+      // Look for the terminating delimiter which must be a comma.
+      size_t next_configuration_separator = settings.find(',', setting_pos);
+      if (next_configuration_separator == std::string::npos) {
+        next_configuration_separator = settings_len;
+      }
+
+      // Prevent end of string errors.
+      if (next_configuration_separator == setting_pos) {
+          continue;
+      }
+
+      // Get the actual setting itself. Strtol is being used to convert because it is
+      // exception safe. If the input is not sane, it will set a setting of 0.
+      std::string setting_string = settings.substr(setting_pos, next_configuration_separator - setting_pos);
+      int setting = std::strtol(setting_string.c_str(), 0, 0);
+
+      std::string setting_name = settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
+
+      settings_to_fill.Put(setting_name, setting);
+
+      search_pos = next_configuration_separator;
+    } while (true);
+  }
 };
 }  // namespace art
 #endif  // ART_COMPILER_DEX_PASS_DRIVER_ME_H_
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index c72a4a6..6281062 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -69,20 +69,30 @@
 template<>
 bool PassDriver<PassDriverMEOpts>::default_print_passes_ = false;
 
-void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) {
-  // First call the base class' version.
-  PassDriver::ApplyPass(data, pass);
+// By default, there are no overridden pass settings.
+template<>
+std::string PassDriver<PassDriverMEOpts>::overridden_pass_options_list_ = std::string();
 
+void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) {
   const PassME* pass_me = down_cast<const PassME*> (pass);
   DCHECK(pass_me != nullptr);
 
   PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
 
+  // Set to dirty.
+  pass_me_data_holder->dirty = true;
+
+  // First call the base class' version.
+  PassDriver::ApplyPass(data, pass);
+
   // Now we care about flags.
   if ((pass_me->GetFlag(kOptimizationBasicBlockChange) == true) ||
       (pass_me->GetFlag(kOptimizationDefUsesChange) == true)) {
-    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
-    c_unit->mir_graph.get()->CalculateBasicBlockInformation();
+    // Is it dirty at least?
+    if (pass_me_data_holder->dirty == true) {
+      CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+      c_unit->mir_graph.get()->CalculateBasicBlockInformation();
+    }
   }
 }
 
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index 14108af..4acab6c 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -73,4 +73,8 @@
 template<>
 bool PassDriver<PassDriverMEPostOpt>::default_print_passes_ = false;
 
+// By default, there are no overridden pass settings.
+template<>
+std::string PassDriver<PassDriverMEPostOpt>::overridden_pass_options_list_ = std::string();
+
 }  // namespace art
diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h
index c7276eb..8242cb8 100644
--- a/compiler/dex/pass_me.h
+++ b/compiler/dex/pass_me.h
@@ -42,7 +42,8 @@
   public:
     CompilationUnit* c_unit;
     BasicBlock* bb;
-    void* data;
+    void* data;               /**< @brief Any data the pass wants to use */
+    bool dirty;               /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */
 };
 
 enum DataFlowAnalysisMode {
@@ -80,12 +81,53 @@
   }
 
   ~PassME() {
+    default_options_.clear();
   }
 
   virtual DataFlowAnalysisMode GetTraversal() const {
     return traversal_type_;
   }
 
+  /**
+   * @return Returns whether the pass has any configurable options.
+   */
+  bool HasOptions() const {
+    return default_options_.size() != 0;
+  }
+
+  /**
+   * @brief Prints the pass options along with default settings if there are any.
+   * @details The printing is done using LOG(INFO).
+   */
+  void PrintPassDefaultOptions() const {
+    for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
+      LOG(INFO) << "\t" << option_it->first << ":" << std::dec << option_it->second;
+    }
+  }
+
+  /**
+   * @brief Prints the pass options along with either default or overridden setting.
+   * @param overridden_options The overridden settings for this pass.
+   */
+  void PrintPassOptions(SafeMap<const std::string, int>& overridden_options) const {
+    // We walk through the default options only to get the pass names. We use GetPassOption to
+    // also consider the overridden ones.
+    for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
+      LOG(INFO) << "\t" << option_it->first << ":" << std::dec << GetPassOption(option_it->first, overridden_options);
+    }
+  }
+
+  /**
+   * @brief Used to obtain the option for a pass.
+   * @details Will return the overridden option if it exists or default one.
+   * @param option_name The name of option whose setting to look for.
+   * @param c_unit The compilation unit currently being handled.
+   * @return Returns the setting for the pass option.
+   */
+  int GetPassOption(const char* option_name, CompilationUnit* c_unit) const {
+    return GetPassOption(option_name, c_unit->overridden_pass_options);
+  }
+
   const char* GetDumpCFGFolder() const {
     return dump_cfg_folder_;
   }
@@ -95,6 +137,25 @@
   }
 
  protected:
+  int GetPassOption(const char* option_name, const SafeMap<const std::string, int>& overridden_options) const {
+    // First check if there are any overridden settings.
+    auto overridden_it = overridden_options.find(std::string(option_name));
+    if (overridden_it != overridden_options.end()) {
+      return overridden_it->second;
+    }
+
+    // Next check the default options.
+    auto default_it = default_options_.find(option_name);
+
+    if (default_it == default_options_.end()) {
+      // An invalid option is being requested.
+      DCHECK(false);
+      return 0;
+    }
+
+    return default_it->second;
+  }
+
   /** @brief Type of traversal: determines the order to execute the pass on the BasicBlocks. */
   const DataFlowAnalysisMode traversal_type_;
 
@@ -103,6 +164,13 @@
 
   /** @brief CFG Dump Folder: what sub-folder to use for dumping the CFGs post pass. */
   const char* const dump_cfg_folder_;
+
+  /**
+   * @brief Contains a map of options with the default settings.
+   * @details The constructor of the specific pass instance should fill this
+   * with default options.
+   * */
+  SafeMap<const char*, int> default_options_;
 };
 }  // namespace art
 #endif  // ART_COMPILER_DEX_PASS_ME_H_
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
index fd67608..ba255e0 100644
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ b/compiler/dex/portable/mir_to_gbc.cc
@@ -64,7 +64,7 @@
 }
 
 ::llvm::Value* MirConverter::GetLLVMValue(int s_reg) {
-  return llvm_values_.Get(s_reg);
+  return llvm_values_[s_reg];
 }
 
 void MirConverter::SetVregOnValue(::llvm::Value* val, int s_reg) {
@@ -87,7 +87,7 @@
   }
   placeholder->replaceAllUsesWith(val);
   val->takeName(placeholder);
-  llvm_values_.Put(s_reg, val);
+  llvm_values_[s_reg] = val;
   ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(placeholder);
   DCHECK(inst != NULL);
   inst->eraseFromParent();
@@ -140,11 +140,11 @@
   return GetLLVMBlock(bb->id);
 }
 
-void MirConverter::ConvertPackedSwitch(BasicBlock* bb,
+void MirConverter::ConvertPackedSwitch(BasicBlock* bb, MIR* mir,
                                 int32_t table_offset, RegLocation rl_src) {
   const Instruction::PackedSwitchPayload* payload =
       reinterpret_cast<const Instruction::PackedSwitchPayload*>(
-      cu_->insns + current_dalvik_offset_ + table_offset);
+      mir_graph_->GetTable(mir, table_offset));
 
   ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
 
@@ -164,11 +164,11 @@
   bb->fall_through = NullBasicBlockId;
 }
 
-void MirConverter::ConvertSparseSwitch(BasicBlock* bb,
+void MirConverter::ConvertSparseSwitch(BasicBlock* bb, MIR* mir,
                                 int32_t table_offset, RegLocation rl_src) {
   const Instruction::SparseSwitchPayload* payload =
       reinterpret_cast<const Instruction::SparseSwitchPayload*>(
-      cu_->insns + current_dalvik_offset_ + table_offset);
+      mir_graph_->GetTable(mir, table_offset));
 
   const int32_t* keys = payload->GetKeys();
   const int32_t* targets = payload->GetTargets();
@@ -1536,9 +1536,9 @@
   ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
   ::llvm::Instruction* inst = irb_->CreateCall(intr);
   ::llvm::SmallVector< ::llvm::Value*, 2> reg_info;
-  reg_info.push_back(irb_->getInt32(cu_->num_ins));
-  reg_info.push_back(irb_->getInt32(cu_->num_regs));
-  reg_info.push_back(irb_->getInt32(cu_->num_outs));
+  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfInVRs()));
+  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfLocalCodeVRs()));
+  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfOutVRs()));
   reg_info.push_back(irb_->getInt32(mir_graph_->GetNumUsedCompilerTemps()));
   reg_info.push_back(irb_->getInt32(mir_graph_->GetNumSSARegs()));
   ::llvm::MDNode* reg_info_node = ::llvm::MDNode::get(*context_, reg_info);
@@ -1669,12 +1669,12 @@
       art::llvm::IntrinsicHelper::IntrinsicId id =
               art::llvm::IntrinsicHelper::AllocaShadowFrame;
       ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
-      ::llvm::Value* entries = irb_->getInt32(cu_->num_dalvik_registers);
+      ::llvm::Value* entries = irb_->getInt32(mir_graph_->GetNumOfCodeVRs());
       irb_->CreateCall(func, entries);
     }
 
     {  // Store arguments to vregs.
-      uint16_t arg_reg = cu_->num_regs;
+      uint16_t arg_reg = mir_graph_->GetFirstInVR();
 
       ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
 
@@ -1740,15 +1740,12 @@
             art::llvm::IntrinsicHelper::CatchTargets);
         ::llvm::Value* switch_key =
             irb_->CreateCall(intr, irb_->getInt32(mir->offset));
-        GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_blocks);
         // New basic block to use for work half
         ::llvm::BasicBlock* work_bb =
             ::llvm::BasicBlock::Create(*context_, "", func_);
         ::llvm::SwitchInst* sw =
-            irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks->Size());
-        while (true) {
-          SuccessorBlockInfo *successor_block_info = iter.Next();
-          if (successor_block_info == NULL) break;
+            irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks.size());
+        for (SuccessorBlockInfo *successor_block_info : bb->successor_blocks) {
           ::llvm::BasicBlock *target =
               GetLLVMBlock(successor_block_info->block);
           int type_index = successor_block_info->key;
@@ -1843,7 +1840,7 @@
   arg_iter->setName("method");
   ++arg_iter;
 
-  int start_sreg = cu_->num_regs;
+  int start_sreg = mir_graph_->GetFirstInVR();
 
   for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) {
     arg_iter->setName(StringPrintf("v%i_0", start_sreg));
@@ -1908,18 +1905,18 @@
     ::llvm::Value* val;
     RegLocation rl_temp = mir_graph_->reg_location_[i];
     if ((mir_graph_->SRegToVReg(i) < 0) || rl_temp.high_word) {
-      llvm_values_.Insert(0);
-    } else if ((i < cu_->num_regs) ||
-               (i >= (cu_->num_regs + cu_->num_ins))) {
+      llvm_values_.push_back(0);
+    } else if ((i < mir_graph_->GetFirstInVR()) ||
+               (i >= (mir_graph_->GetFirstTempVR()))) {
       ::llvm::Constant* imm_value = mir_graph_->reg_location_[i].wide ?
          irb_->getJLong(0) : irb_->getJInt(0);
       val = EmitConst(imm_value, mir_graph_->reg_location_[i]);
       val->setName(mir_graph_->GetSSAName(i));
-      llvm_values_.Insert(val);
+      llvm_values_.push_back(val);
     } else {
       // Recover previously-created argument values
       ::llvm::Value* arg_val = arg_iter++;
-      llvm_values_.Insert(arg_val);
+      llvm_values_.push_back(arg_val);
     }
   }
 
@@ -1966,7 +1963,7 @@
      if (::llvm::verifyFunction(*func_, ::llvm::PrintMessageAction)) {
        LOG(INFO) << "Bitcode verification FAILED for "
                  << PrettyMethod(cu_->method_idx, *cu_->dex_file)
-                 << " of size " << cu_->code_item->insns_size_in_code_units_;
+                 << " of size " << mir_graph_->GetNumDalvikInsns();
        cu_->enable_debug |= (1 << kDebugDumpBitcodeFile);
      }
   }
diff --git a/compiler/dex/portable/mir_to_gbc.h b/compiler/dex/portable/mir_to_gbc.h
index e97634c..94ae3f7 100644
--- a/compiler/dex/portable/mir_to_gbc.h
+++ b/compiler/dex/portable/mir_to_gbc.h
@@ -31,9 +31,48 @@
 #include "llvm/intrinsic_helper.h"
 #include "llvm/llvm_compilation_unit.h"
 #include "safe_map.h"
+#include "utils/arena_containers.h"
+
+namespace llvm {
+  class Module;
+  class LLVMContext;
+}
 
 namespace art {
 
+namespace llvm {
+  class IntrinsicHelper;
+  class IRBuilder;
+}
+
+class LLVMInfo {
+  public:
+    LLVMInfo();
+    ~LLVMInfo();
+
+    ::llvm::LLVMContext* GetLLVMContext() {
+      return llvm_context_.get();
+    }
+
+    ::llvm::Module* GetLLVMModule() {
+      return llvm_module_;
+    }
+
+    art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
+      return intrinsic_helper_.get();
+    }
+
+    art::llvm::IRBuilder* GetIRBuilder() {
+      return ir_builder_.get();
+    }
+
+  private:
+    std::unique_ptr< ::llvm::LLVMContext> llvm_context_;
+    ::llvm::Module* llvm_module_;  // Managed by context_.
+    std::unique_ptr<art::llvm::IntrinsicHelper> intrinsic_helper_;
+    std::unique_ptr<art::llvm::IRBuilder> ir_builder_;
+};
+
 struct BasicBlock;
 struct CallInfo;
 struct CompilationUnit;
@@ -66,9 +105,10 @@
         placeholder_bb_(NULL),
         entry_bb_(NULL),
         entry_target_bb_(NULL),
-        llvm_values_(arena, mir_graph->GetNumSSARegs()),
+        llvm_values_(arena->Adapter()),
         temp_name_(0),
         current_dalvik_offset_(0) {
+      llvm_values_.reserve(mir_graph->GetNumSSARegs());
       if (kIsDebugBuild) {
         cu->enable_debug |= (1 << kDebugVerifyBitcode);
       }
@@ -91,9 +131,9 @@
     ::llvm::Type* LlvmTypeFromLocRec(RegLocation loc);
     void InitIR();
     ::llvm::BasicBlock* FindCaseTarget(uint32_t vaddr);
-    void ConvertPackedSwitch(BasicBlock* bb, int32_t table_offset,
+    void ConvertPackedSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
                              RegLocation rl_src);
-    void ConvertSparseSwitch(BasicBlock* bb, int32_t table_offset,
+    void ConvertSparseSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
                              RegLocation rl_src);
     void ConvertSget(int32_t field_index,
                      art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
@@ -190,7 +230,7 @@
     ::llvm::BasicBlock* entry_bb_;
     ::llvm::BasicBlock* entry_target_bb_;
     std::string bitcode_filename_;
-    GrowableArray< ::llvm::Value*> llvm_values_;
+    ArenaVector< ::llvm::Value*> llvm_values_;
     int32_t temp_name_;
     SafeMap<int32_t, ::llvm::BasicBlock*> id_to_block_map_;  // block id -> llvm bb.
     int current_dalvik_offset_;
diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc
index 1371652..675dbcf 100644
--- a/compiler/dex/post_opt_passes.cc
+++ b/compiler/dex/post_opt_passes.cc
@@ -36,9 +36,9 @@
   return res;
 }
 
-bool MethodUseCount::Worker(const PassDataHolder* data) const {
+bool MethodUseCount::Worker(PassDataHolder* data) const {
   DCHECK(data != nullptr);
-  const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
   CompilationUnit* c_unit = pass_me_data_holder->c_unit;
   DCHECK(c_unit != nullptr);
   BasicBlock* bb = pass_me_data_holder->bb;
@@ -49,9 +49,9 @@
 }
 
 
-bool ClearPhiInstructions::Worker(const PassDataHolder* data) const {
+bool ClearPhiInstructions::Worker(PassDataHolder* data) const {
   DCHECK(data != nullptr);
-  const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
   CompilationUnit* c_unit = pass_me_data_holder->c_unit;
   DCHECK(c_unit != nullptr);
   BasicBlock* bb = pass_me_data_holder->bb;
@@ -84,7 +84,7 @@
   // First clear all predecessors.
   AllNodesIterator first(mir_graph);
   for (BasicBlock* bb = first.Next(); bb != nullptr; bb = first.Next()) {
-    bb->predecessors->Reset();
+    bb->predecessors.clear();
   }
 
   // Now calculate all predecessors.
@@ -100,7 +100,7 @@
 
     // Now iterate through the children to set the predecessor bits.
     for (BasicBlock* child = child_iter.Next(); child != nullptr; child = child_iter.Next()) {
-      child->predecessors->Insert(bb->id);
+      child->predecessors.push_back(bb->id);
     }
   }
 }
diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h
index a1b0df4..e7805ba 100644
--- a/compiler/dex/post_opt_passes.h
+++ b/compiler/dex/post_opt_passes.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_DEX_POST_OPT_PASSES_H_
 #define ART_COMPILER_DEX_POST_OPT_PASSES_H_
 
+#include "dex/quick/mir_to_lir.h"
 #include "compiler_internals.h"
 #include "pass_me.h"
 
@@ -52,7 +53,7 @@
   MethodUseCount() : PassME("UseCount") {
   }
 
-  bool Worker(const PassDataHolder* data) const;
+  bool Worker(PassDataHolder* data) const;
 
   bool Gate(const PassDataHolder* data) const;
 };
@@ -66,7 +67,7 @@
   ClearPhiInstructions() : PassME("ClearPhiInstructions") {
   }
 
-  bool Worker(const PassDataHolder* data) const;
+  bool Worker(PassDataHolder* data) const;
 };
 
 /**
@@ -222,11 +223,11 @@
   PhiNodeOperands() : PassME("PhiNodeOperands", kPreOrderDFSTraversal) {
   }
 
-  bool Worker(const PassDataHolder* data) const {
+  bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
     DCHECK(c_unit != nullptr);
-    BasicBlock* bb = down_cast<const PassMEDataHolder*>(data)->bb;
+    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
     DCHECK(bb != nullptr);
     c_unit->mir_graph->InsertPhiNodeOperands(bb);
     // No need of repeating, so just return false.
@@ -260,11 +261,11 @@
   ConstantPropagation() : PassME("ConstantPropagation") {
   }
 
-  bool Worker(const PassDataHolder* data) const {
+  bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
     DCHECK(c_unit != nullptr);
-    BasicBlock* bb = down_cast<const PassMEDataHolder*>(data)->bb;
+    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
     DCHECK(bb != nullptr);
     c_unit->mir_graph->DoConstantPropagation(bb);
     // No need of repeating, so just return false.
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 6272555..d935bc3 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -528,6 +528,7 @@
   kThumb2Vldms,      // vldms rd, <list>.
   kThumb2Vstms,      // vstms rd, <list>.
   kThumb2BUncond,    // b <label>.
+  kThumb2Bl,         // bl with linker fixup. [11110] S imm10 [11] J1 [1] J2 imm11.
   kThumb2MovImm16H,  // similar to kThumb2MovImm16, but target high hw.
   kThumb2AddPCR,     // Thumb2 2-operand add with hard-coded PC target.
   kThumb2Adr,        // Special purpose encoding of ADR for switch tables.
@@ -556,22 +557,24 @@
 
 // Instruction assembly field_loc kind.
 enum ArmEncodingKind {
-  kFmtUnused,    // Unused field and marks end of formats.
-  kFmtBitBlt,    // Bit string using end/start.
-  kFmtDfp,       // Double FP reg.
-  kFmtSfp,       // Single FP reg.
-  kFmtModImm,    // Shifted 8-bit immed using [26,14..12,7..0].
-  kFmtImm16,     // Zero-extended immed using [26,19..16,14..12,7..0].
-  kFmtImm6,      // Encoded branch target using [9,7..3]0.
-  kFmtImm12,     // Zero-extended immediate using [26,14..12,7..0].
-  kFmtShift,     // Shift descriptor, [14..12,7..4].
-  kFmtLsb,       // least significant bit using [14..12][7..6].
-  kFmtBWidth,    // bit-field width, encoded as width-1.
-  kFmtShift5,    // Shift count, [14..12,7..6].
-  kFmtBrOffset,  // Signed extended [26,11,13,21-16,10-0]:0.
-  kFmtFPImm,     // Encoded floating point immediate.
-  kFmtOff24,     // 24-bit Thumb2 unconditional branch encoding.
-  kFmtSkip,      // Unused field, but continue to next.
+  kFmtUnused,      // Unused field and marks end of formats.
+  kFmtBitBlt,      // Bit string using end/start.
+  kFmtLdmRegList,  // Load multiple register list using [15,14,12..0].
+  kFmtStmRegList,  // Store multiple register list using [14,12..0].
+  kFmtDfp,         // Double FP reg.
+  kFmtSfp,         // Single FP reg.
+  kFmtModImm,      // Shifted 8-bit immed using [26,14..12,7..0].
+  kFmtImm16,       // Zero-extended immed using [26,19..16,14..12,7..0].
+  kFmtImm6,        // Encoded branch target using [9,7..3]0.
+  kFmtImm12,       // Zero-extended immediate using [26,14..12,7..0].
+  kFmtShift,       // Shift descriptor, [14..12,7..4].
+  kFmtLsb,         // least significant bit using [14..12][7..6].
+  kFmtBWidth,      // bit-field width, encoded as width-1.
+  kFmtShift5,      // Shift count, [14..12,7..6].
+  kFmtBrOffset,    // Signed extended [26,11,13,21-16,10-0]:0.
+  kFmtFPImm,       // Encoded floating point immediate.
+  kFmtOff24,       // 24-bit Thumb2 unconditional branch encoding.
+  kFmtSkip,        // Unused field, but continue to next.
 };
 
 // Struct used to define the snippet positions for each Thumb opcode.
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 35c3597..cf34948 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -427,7 +427,7 @@
                  REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4, kFixupVLoad),
     ENCODING_MAP(kThumb2Vldrd,       0xed900b00,
                  kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
-                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF |
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF4 |
                  REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4, kFixupVLoad),
     ENCODING_MAP(kThumb2Vmuls,        0xee200a00,
                  kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
@@ -560,12 +560,12 @@
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | IS_MOVE,
                  "vmov.f64 ", " !0S, !1S", 4, kFixupNone),
     ENCODING_MAP(kThumb2Ldmia,         0xe8900000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
                  "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
     ENCODING_MAP(kThumb2Stmia,         0xe8800000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
                  "stmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -935,7 +935,7 @@
                  IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD_OFF,
                  "ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone),
     ENCODING_MAP(kThumb2Stm,          0xe9000000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
                  "stm", "!0C, <!1R>", 4, kFixupNone),
@@ -968,6 +968,10 @@
                  kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
                  "b", "!0t", 4, kFixupT2Branch),
+    ENCODING_MAP(kThumb2Bl,           0xf000d000,
+                 kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+                 "bl", "!0T", 4, kFixupLabel),
     ENCODING_MAP(kThumb2MovImm16H,       0xf2c00000,
                  kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0,
@@ -992,7 +996,7 @@
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
                  "movt", "!0C, #!1M", 4, kFixupMovImmHST),
     ENCODING_MAP(kThumb2LdmiaWB,         0xe8b00000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
                  "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -1094,6 +1098,19 @@
             bits |= value;
           } else {
             switch (encoder->field_loc[i].kind) {
+              case kFmtLdmRegList:
+                value = (operand << encoder->field_loc[i].start) &
+                    ((1 << (encoder->field_loc[i].end + 1)) - 1);
+                bits |= value;
+                DCHECK_EQ((bits & (1 << 13)), 0u);
+                break;
+              case kFmtStmRegList:
+                value = (operand << encoder->field_loc[i].start) &
+                    ((1 << (encoder->field_loc[i].end + 1)) - 1);
+                bits |= value;
+                DCHECK_EQ((bits & (1 << 13)), 0u);
+                DCHECK_EQ((bits & (1 << 15)), 0u);
+                break;
               case kFmtSkip:
                 break;  // Nothing to do, but continue to next.
               case kFmtUnused:
diff --git a/compiler/dex/quick/arm/backend_arm.h b/compiler/dex/quick/arm/backend_arm.h
new file mode 100644
index 0000000..42a9bca
--- /dev/null
+++ b/compiler/dex/quick/arm/backend_arm.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEX_QUICK_ARM_BACKEND_ARM_H_
+#define ART_COMPILER_DEX_QUICK_ARM_BACKEND_ARM_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+                          ArenaAllocator* const arena);
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_QUICK_ARM_BACKEND_ARM_H_
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 4ba3c4b..b721e02 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -20,6 +20,8 @@
 #include "codegen_arm.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "gc/accounting/card_table.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 
 namespace art {
@@ -44,7 +46,7 @@
  *   cbnz  r_idx, lp
  */
 void ArmMir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpSparseSwitchTable(table);
   }
@@ -55,7 +57,7 @@
   tab_rec->vaddr = current_dalvik_offset_;
   uint32_t size = table[1];
   tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
-  switch_tables_.Insert(tab_rec);
+  switch_tables_.push_back(tab_rec);
 
   // Get the switch value
   rl_src = LoadValue(rl_src, kCoreReg);
@@ -92,7 +94,7 @@
 
 
 void ArmMir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpPackedSwitchTable(table);
   }
@@ -104,7 +106,7 @@
   uint32_t size = table[1];
   tab_rec->targets =
       static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
-  switch_tables_.Insert(tab_rec);
+  switch_tables_.push_back(tab_rec);
 
   // Get the switch value
   rl_src = LoadValue(rl_src, kCoreReg);
@@ -147,8 +149,8 @@
  *
  * Total size is 4+(width * size + 1)/2 16-bit code units.
  */
-void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void ArmMir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   // Add the table to the list - we'll process it later
   FillArrayData *tab_rec =
       static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
@@ -158,7 +160,7 @@
   uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
   tab_rec->size = (size * width) + 8;
 
-  fill_array_data_.Insert(tab_rec);
+  fill_array_data_.push_back(tab_rec);
 
   // Making a call - use explicit registers
   FlushAllRegs();   /* Everything to home location */
@@ -499,4 +501,117 @@
   NewLIR1(kThumbBx, rs_rARM_LR.GetReg());
 }
 
+static bool ArmUseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
+  // Emit relative calls only within a dex file due to the limited range of the BL insn.
+  return cu->dex_file == target_method.dex_file;
+}
+
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+                             int state, const MethodReference& target_method,
+                             uint32_t unused,
+                             uintptr_t direct_code, uintptr_t direct_method,
+                             InvokeType type) {
+  Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+  if (direct_code != 0 && direct_method != 0) {
+    switch (state) {
+    case 0:  // Get the current Method* [sets kArg0]
+      if (direct_code != static_cast<uintptr_t>(-1)) {
+        cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+      } else if (ArmUseRelativeCall(cu, target_method)) {
+        // Defer to linker patch.
+      } else {
+        cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+      }
+      if (direct_method != static_cast<uintptr_t>(-1)) {
+        cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
+      } else {
+        cg->LoadMethodAddress(target_method, type, kArg0);
+      }
+      break;
+    default:
+      return -1;
+    }
+  } else {
+    RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+    switch (state) {
+    case 0:  // Get the current Method* [sets kArg0]
+      // TUNING: we can save a reg copy if Method* has been promoted.
+      cg->LoadCurrMethodDirect(arg0_ref);
+      break;
+    case 1:  // Get method->dex_cache_resolved_methods_
+      cg->LoadRefDisp(arg0_ref,
+                      mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+                      arg0_ref,
+                      kNotVolatile);
+      // Set up direct code if known.
+      if (direct_code != 0) {
+        if (direct_code != static_cast<uintptr_t>(-1)) {
+          cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+        } else if (ArmUseRelativeCall(cu, target_method)) {
+          // Defer to linker patch.
+        } else {
+          CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+          cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+        }
+      }
+      break;
+    case 2:  // Grab target method*
+      CHECK_EQ(cu->dex_file, target_method.dex_file);
+      cg->LoadRefDisp(arg0_ref,
+                      mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+                          target_method.dex_method_index).Int32Value(),
+                      arg0_ref,
+                      kNotVolatile);
+      break;
+    case 3:  // Grab the code from the method*
+      if (direct_code == 0) {
+        // kInvokeTgt := arg0_ref->entrypoint
+        cg->LoadWordDisp(arg0_ref,
+                         mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
+                         cg->TargetPtrReg(kInvokeTgt));
+      }
+      break;
+    default:
+      return -1;
+    }
+  }
+  return state + 1;
+}
+
+NextCallInsn ArmMir2Lir::GetNextSDCallInsn() {
+  return ArmNextSDCallInsn;
+}
+
+LIR* ArmMir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
+  // For ARM, just generate a relative BL instruction that will be filled in at 'link time'.
+  // If the target turns out to be too far, the linker will generate a thunk for dispatch.
+  int target_method_idx = target_method.dex_method_index;
+  const DexFile* target_dex_file = target_method.dex_file;
+
+  // Generate the call instruction and save index, dex_file, and type.
+  // NOTE: Method deduplication takes linker patches into account, so we can just pass 0
+  // as a placeholder for the offset.
+  LIR* call = RawLIR(current_dalvik_offset_, kThumb2Bl, 0,
+                     target_method_idx, WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
+  AppendLIR(call);
+  call_method_insns_.push_back(call);
+  return call;
+}
+
+LIR* ArmMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+  LIR* call_insn;
+  if (method_info.FastPath() && ArmUseRelativeCall(cu_, method_info.GetTargetMethod()) &&
+      (method_info.GetSharpType() == kDirect || method_info.GetSharpType() == kStatic) &&
+      method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
+    call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType());
+  } else {
+    call_insn = OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+  }
+  return call_insn;
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index cd6c9cc..932dd87 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -19,6 +19,8 @@
 
 #include "arm_lir.h"
 #include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
+#include "utils/arena_containers.h"
 
 namespace art {
 
@@ -116,7 +118,7 @@
     void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
     void GenExitSequence();
     void GenSpecialExitSequence();
-    void GenFillArrayData(DexOffset table_offset, RegLocation rl_src);
+    void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src);
     void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
     void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
     void GenSelect(BasicBlock* bb, MIR* mir);
@@ -184,6 +186,28 @@
       return false;  // Wide FPRs are formed by pairing.
     }
 
+    NextCallInsn GetNextSDCallInsn() OVERRIDE;
+
+    /*
+     * @brief Generate a relative call to the method that will be patched at link time.
+     * @param target_method The MethodReference of the method to be invoked.
+     * @param type How the method will be invoked.
+     * @returns Call instruction
+     */
+    LIR* CallWithLinkerFixup(const MethodReference& target_method, InvokeType type);
+
+    /*
+     * @brief Generate the actual call insn based on the method info.
+     * @param method_info the lowering info for the method call.
+     * @returns Call instruction
+     */
+    LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
+
+    /*
+     * @brief Handle ARM specific literals.
+     */
+    void InstallLiteralPools() OVERRIDE;
+
     LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
     size_t GetInstructionOffset(LIR* lir);
 
@@ -214,6 +238,8 @@
     static constexpr ResourceMask GetRegMaskArm(RegStorage reg);
     static constexpr ResourceMask EncodeArmRegList(int reg_list);
     static constexpr ResourceMask EncodeArmRegFpcsList(int reg_list);
+
+    ArenaVector<LIR*> call_method_insns_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 0de2a44..1a4b23e 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -377,7 +377,7 @@
    * TODO: consider interspersing slowpaths in code following unconditional branches.
    */
   bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget));
-  skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64);
+  skip &= ((mir_graph_->GetNumDalvikInsns() - current_dalvik_offset_) > 64);
   if (!skip && reg.Low8() && (check_value == 0)) {
     if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) {
       branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 0509ad3..dd8f7fe 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -20,6 +20,7 @@
 
 #include <string>
 
+#include "backend_arm.h"
 #include "dex/compiler_internals.h"
 #include "dex/quick/mir_to_lir-inl.h"
 
@@ -451,6 +452,11 @@
                  reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
                  lir->target);
              break;
+           case 'T':
+             snprintf(tbuf, arraysize(tbuf), "%s", PrettyMethod(
+                 static_cast<uint32_t>(lir->operands[1]),
+                 *reinterpret_cast<const DexFile*>(UnwrapPointer(lir->operands[2]))).c_str());
+             break;
            case 'u': {
              int offset_1 = lir->operands[0];
              int offset_2 = NEXT_LIR(lir)->operands[0];
@@ -550,7 +556,9 @@
 }
 
 ArmMir2Lir::ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
-    : Mir2Lir(cu, mir_graph, arena) {
+    : Mir2Lir(cu, mir_graph, arena),
+      call_method_insns_(arena->Adapter()) {
+  call_method_insns_.reserve(100);
   // Sanity check - make sure encoding map lines up.
   for (int i = 0; i < kArmLast; i++) {
     if (ArmMir2Lir::EncodingMap[i].opcode != i) {
@@ -567,16 +575,16 @@
 }
 
 void ArmMir2Lir::CompilerInitializeRegAlloc() {
-  reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */, sp_regs,
-                                        dp_regs, reserved_regs, empty_pool /* reserved64 */,
-                                        core_temps, empty_pool /* core64_temps */, sp_temps,
-                                        dp_temps);
+  reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */,
+                                            sp_regs, dp_regs,
+                                            reserved_regs, empty_pool /* reserved64 */,
+                                            core_temps, empty_pool /* core64_temps */,
+                                            sp_temps, dp_temps));
 
   // Target-specific adjustments.
 
   // Alias single precision floats to appropriate half of overlapping double.
-  GrowableArray<RegisterInfo*>::Iterator it(&reg_pool_->sp_regs_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : reg_pool_->sp_regs_) {
     int sp_reg_num = info->GetReg().GetRegNum();
     int dp_reg_num = sp_reg_num >> 1;
     RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num);
@@ -783,8 +791,7 @@
      * TODO: until runtime support is in, make sure we avoid promoting the same vreg to
      * different underlying physical registers.
      */
-    GrowableArray<RegisterInfo*>::Iterator it(&reg_pool_->dp_regs_);
-    for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+    for (RegisterInfo* info : reg_pool_->dp_regs_) {
       if (!info->IsTemp() && !info->InUse()) {
         res = info->GetReg();
         info->MarkInUse();
@@ -808,8 +815,7 @@
 // Reserve a callee-save sp single register.
 RegStorage ArmMir2Lir::AllocPreservedSingle(int s_reg) {
   RegStorage res;
-  GrowableArray<RegisterInfo*>::Iterator it(&reg_pool_->sp_regs_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : reg_pool_->sp_regs_) {
     if (!info->IsTemp() && !info->InUse()) {
       res = info->GetReg();
       int p_map_idx = SRegToPMap(s_reg);
@@ -824,4 +830,21 @@
   return res;
 }
 
+void ArmMir2Lir::InstallLiteralPools() {
+  // PC-relative calls to methods.
+  patches_.reserve(call_method_insns_.size());
+  for (LIR* p : call_method_insns_) {
+      DCHECK_EQ(p->opcode, kThumb2Bl);
+      uint32_t target_method_idx = p->operands[1];
+      const DexFile* target_dex_file =
+          reinterpret_cast<const DexFile*>(UnwrapPointer(p->operands[2]));
+
+      patches_.push_back(LinkerPatch::RelativeCodePatch(p->offset,
+                                                        target_dex_file, target_method_idx));
+  }
+
+  // And do the normal processing.
+  Mir2Lir::InstallLiteralPools();
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index a449cbd..ab71921 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -22,77 +22,75 @@
 namespace art {
 
 /*
- * TODO(Arm64): the comments below are outdated.
- *
  * Runtime register usage conventions.
  *
- * r0-r3: Argument registers in both Dalvik and C/C++ conventions.
- *        However, for Dalvik->Dalvik calls we'll pass the target's Method*
- *        pointer in r0 as a hidden arg0. Otherwise used as codegen scratch
- *        registers.
- * r0-r1: As in C/C++ r0 is 32-bit return register and r0/r1 is 64-bit
- * r4   : (rA64_SUSPEND) is reserved (suspend check/debugger assist)
- * r5   : Callee save (promotion target)
- * r6   : Callee save (promotion target)
- * r7   : Callee save (promotion target)
- * r8   : Callee save (promotion target)
- * r9   : (rA64_SELF) is reserved (pointer to thread-local storage)
- * r10  : Callee save (promotion target)
- * r11  : Callee save (promotion target)
- * r12  : Scratch, may be trashed by linkage stubs
- * r13  : (sp) is reserved
- * r14  : (lr) is reserved
- * r15  : (pc) is reserved
+ * r0     : As in C/C++ w0 is 32-bit return register and x0 is 64-bit.
+ * r0-r7  : Argument registers in both Dalvik and C/C++ conventions.
+ *          However, for Dalvik->Dalvik calls we'll pass the target's Method*
+ *          pointer in x0 as a hidden arg0. Otherwise used as codegen scratch
+ *          registers.
+ * r8-r15 : Caller save registers (used as temporary registers).
+ * r16-r17: Also known as ip0-ip1, respectively. Used as scratch registers by
+ *          the linker, by the trampolines and other stubs (the backend uses
+ *          these as temporary registers).
+ * r18    : (rxSELF) is reserved (pointer to thread-local storage).
+ * r19    : (rwSUSPEND) is reserved (suspend check/debugger assist).
+ * r20-r29: Callee save registers (promotion targets).
+ * r30    : (lr) is reserved (the link register).
+ * rsp    : (sp) is reserved (the stack pointer).
+ * rzr    : (zr) is reserved (the zero register).
  *
- * 5 core temps that codegen can use (r0, r1, r2, r3, r12)
- * 7 core registers that can be used for promotion
+ * 18 core temps that codegen can use (r0-r17).
+ * 10 core registers that can be used for promotion.
  *
- * Floating pointer registers
- * s0-s31
- * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31}
+ * Floating-point registers
+ * v0-v31
  *
- * s16-s31 (d8-d15) preserved across C calls
- * s0-s15 (d0-d7) trashed across C calls
+ * v0     : s0 is return register for singles (32-bit) and d0 for doubles (64-bit).
+ *          This is analogous to the C/C++ (hard-float) calling convention.
+ * v0-v7  : Floating-point argument registers in both Dalvik and C/C++ conventions.
+ *          Also used as temporary and codegen scratch registers.
  *
- * s0-s15/d0-d7 used as codegen temp/scratch
- * s16-s31/d8-d31 can be used for promotion.
+ * v0-v7 and v16-v31 : trashed across C calls.
+ * v8-v15 : bottom 64-bits preserved across C calls (d8-d15 are preserved).
  *
- * Calling convention
- *     o On a call to a Dalvik method, pass target's Method* in r0
- *     o r1-r3 will be used for up to the first 3 words of arguments
- *     o Arguments past the first 3 words will be placed in appropriate
+ * v16-v31: Used as codegen temp/scratch.
+ * v8-v15 : Can be used for promotion.
+ *
+ * Calling convention (Hard-float)
+ *     o On a call to a Dalvik method, pass target's Method* in x0
+ *     o r1-r7, v0-v7 will be used for the first 7+8 arguments
+ *     o Arguments which cannot be put in registers are placed in appropriate
  *       out slots by the caller.
- *     o If a 64-bit argument would span the register/memory argument
- *       boundary, it will instead be fully passed in the frame.
  *     o Maintain a 16-byte stack alignment
  *
  *  Stack frame diagram (stack grows down, higher addresses at top):
  *
- * +------------------------+
- * | IN[ins-1]              |  {Note: resides in caller's frame}
- * |       .                |
- * | IN[0]                  |
- * | caller's Method*       |
- * +========================+  {Note: start of callee's frame}
- * | spill region           |  {variable sized - will include lr if non-leaf.}
- * +------------------------+
- * | ...filler word...      |  {Note: used as 2nd word of V[locals-1] if long]
- * +------------------------+
- * | V[locals-1]            |
- * | V[locals-2]            |
- * |      .                 |
- * |      .                 |
- * | V[1]                   |
- * | V[0]                   |
- * +------------------------+
- * |  0 to 3 words padding  |
- * +------------------------+
- * | OUT[outs-1]            |
- * | OUT[outs-2]            |
- * |       .                |
- * | OUT[0]                 |
- * | cur_method*            | <<== sp w/ 16-byte alignment
- * +========================+
+ * +--------------------------------------------+
+ * | IN[ins-1]                                  |  {Note: resides in caller's frame}
+ * |       .                                    |
+ * | IN[0]                                      |
+ * | caller's method (StackReference<ArtMethod>)|  {This is a compressed (4-bytes) reference}
+ * +============================================+  {Note: start of callee's frame}
+ * | spill region                               |  {variable sized - will include lr if non-leaf}
+ * +--------------------------------------------+
+ * |   ...filler word...                        |  {Note: used as 2nd word of V[locals-1] if long}
+ * +--------------------------------------------+
+ * | V[locals-1]                                |
+ * | V[locals-2]                                |
+ * |      .                                     |
+ * |      .                                     |
+ * | V[1]                                       |
+ * | V[0]                                       |
+ * +--------------------------------------------+
+ * |   0 to 3 words padding                     |
+ * +--------------------------------------------+
+ * | OUT[outs-1]                                |
+ * | OUT[outs-2]                                |
+ * |       .                                    |
+ * | OUT[0]                                     |
+ * | current method (StackReference<ArtMethod>) | <<== sp w/ 16-byte alignment
+ * +============================================+
  */
 
 // First FP callee save.
@@ -103,12 +101,12 @@
 #define A64_REG_IS_ZR(reg_num) ((reg_num) == rwzr || (reg_num) == rxzr)
 #define A64_REGSTORAGE_IS_SP_OR_ZR(rs) (((rs).GetRegNum() & 0x1f) == 0x1f)
 
-enum Arm64ResourceEncodingPos {
-  kArm64GPReg0   = 0,
-  kArm64RegLR    = 30,
-  kArm64RegSP    = 31,
-  kArm64FPReg0   = 32,
-  kArm64RegEnd   = 64,
+enum A64ResourceEncodingPos {
+  kA64GPReg0   = 0,
+  kA64RegLR    = 30,
+  kA64RegSP    = 31,
+  kA64FPReg0   = 32,
+  kA64RegEnd   = 64,
 };
 
 #define IS_SIGNED_IMM(size, value) \
@@ -116,6 +114,7 @@
 #define IS_SIGNED_IMM7(value) IS_SIGNED_IMM(7, value)
 #define IS_SIGNED_IMM9(value) IS_SIGNED_IMM(9, value)
 #define IS_SIGNED_IMM12(value) IS_SIGNED_IMM(12, value)
+#define IS_SIGNED_IMM14(value) IS_SIGNED_IMM(14, value)
 #define IS_SIGNED_IMM19(value) IS_SIGNED_IMM(19, value)
 #define IS_SIGNED_IMM21(value) IS_SIGNED_IMM(21, value)
 
@@ -185,15 +184,15 @@
 constexpr RegStorage rs_wLR(RegStorage::kValid | rwLR);
 
 // RegisterLocation templates return values (following the hard-float calling convention).
-const RegLocation arm_loc_c_return =
+const RegLocation a64_loc_c_return =
     {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_w0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_ref =
+const RegLocation a64_loc_c_return_ref =
     {kLocPhysReg, 0, 0, 0, 0, 0, 1, 0, 1, rs_x0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_wide =
+const RegLocation a64_loc_c_return_wide =
     {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_x0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_float =
+const RegLocation a64_loc_c_return_float =
     {kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, rs_f0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_double =
+const RegLocation a64_loc_c_return_double =
     {kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, rs_d0, INVALID_SREG, INVALID_SREG};
 
 /**
@@ -227,7 +226,7 @@
  * assembler. Their corresponding EncodingMap positions will be defined in
  * assemble_arm64.cc.
  */
-enum ArmOpcode {
+enum A64Opcode {
   kA64First = 0,
   kA64Adc3rrr = kA64First,  // adc [00011010000] rm[20-16] [000000] rn[9-5] rd[4-0].
   kA64Add4RRdT,      // add [s001000100] imm_12[21-10] rn[9-5] rd[4-0].
@@ -355,7 +354,10 @@
   kA64Sub4rrro,      // sub [s1001011000] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
   kA64Sub4RRre,      // sub [s1001011001] rm[20-16] option[15-13] imm_3[12-10] rn[9-5] rd[4-0].
   kA64Subs3rRd,      // subs[s111000100] imm_12[21-10] rn[9-5] rd[4-0].
+  kA64Tst2rl,        // tst alias of "ands rzr, rn, #imm".
   kA64Tst3rro,       // tst alias of "ands rzr, arg1, arg2, arg3".
+  kA64Tbnz3rht,      // tbnz imm_6_b5[31] [0110111] imm_6_b40[23-19] imm_14[18-5] rt[4-0].
+  kA64Tbz3rht,       // tbz imm_6_b5[31] [0110110] imm_6_b40[23-19] imm_14[18-5] rt[4-0].
   kA64Ubfm4rrdd,     // ubfm[s10100110] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0].
   kA64Last,
   kA64NotWide = 0,   // Flag used to select the first instruction variant.
@@ -371,22 +373,13 @@
  */
 
 // Return the wide and no-wide variants of the given opcode.
-#define WIDE(op) ((ArmOpcode)((op) | kA64Wide))
-#define UNWIDE(op) ((ArmOpcode)((op) & ~kA64Wide))
+#define WIDE(op) ((A64Opcode)((op) | kA64Wide))
+#define UNWIDE(op) ((A64Opcode)((op) & ~kA64Wide))
 
 // Whether the given opcode is wide.
 #define IS_WIDE(op) (((op) & kA64Wide) != 0)
 
-/*
- * Floating point variants. These are just aliases of the macros above which we use for floating
- * point instructions, just for readibility reasons.
- * TODO(Arm64): should we remove these and use the original macros?
- */
-#define FWIDE WIDE
-#define FUNWIDE UNWIDE
-#define IS_FWIDE IS_WIDE
-
-enum ArmOpDmbOptions {
+enum A64OpDmbOptions {
   kSY = 0xf,
   kST = 0xe,
   kISH = 0xb,
@@ -397,38 +390,39 @@
 };
 
 // Instruction assembly field_loc kind.
-enum ArmEncodingKind {
+enum A64EncodingKind {
   // All the formats below are encoded in the same way (as a kFmtBitBlt).
   // These are grouped together, for fast handling (e.g. "if (LIKELY(fmt <= kFmtBitBlt)) ...").
-  kFmtRegW = 0,  // Word register (w) or wzr.
-  kFmtRegX,      // Extended word register (x) or xzr.
-  kFmtRegR,      // Register with same width as the instruction or zr.
-  kFmtRegWOrSp,  // Word register (w) or wsp.
-  kFmtRegXOrSp,  // Extended word register (x) or sp.
-  kFmtRegROrSp,  // Register with same width as the instruction or sp.
-  kFmtRegS,      // Single FP reg.
-  kFmtRegD,      // Double FP reg.
-  kFmtRegF,      // Single/double FP reg depending on the instruction width.
-  kFmtBitBlt,    // Bit string using end/start.
+  kFmtRegW = 0,   // Word register (w) or wzr.
+  kFmtRegX,       // Extended word register (x) or xzr.
+  kFmtRegR,       // Register with same width as the instruction or zr.
+  kFmtRegWOrSp,   // Word register (w) or wsp.
+  kFmtRegXOrSp,   // Extended word register (x) or sp.
+  kFmtRegROrSp,   // Register with same width as the instruction or sp.
+  kFmtRegS,       // Single FP reg.
+  kFmtRegD,       // Double FP reg.
+  kFmtRegF,       // Single/double FP reg depending on the instruction width.
+  kFmtBitBlt,     // Bit string using end/start.
 
   // Less likely formats.
-  kFmtUnused,    // Unused field and marks end of formats.
-  kFmtImm21,     // Sign-extended immediate using [23..5,30..29].
-  kFmtShift,     // Register shift, 9-bit at [23..21, 15..10]..
-  kFmtExtend,    // Register extend, 9-bit at [23..21, 15..10].
-  kFmtSkip,      // Unused field, but continue to next.
+  kFmtUnused,     // Unused field and marks end of formats.
+  kFmtImm6Shift,  // Shift immediate, 6-bit at [31, 23..19].
+  kFmtImm21,      // Sign-extended immediate using [23..5,30..29].
+  kFmtShift,      // Register shift, 9-bit at [23..21, 15..10]..
+  kFmtExtend,     // Register extend, 9-bit at [23..21, 15..10].
+  kFmtSkip,       // Unused field, but continue to next.
 };
 
 // Struct used to define the snippet positions for each A64 opcode.
-struct ArmEncodingMap {
+struct A64EncodingMap {
   uint32_t wskeleton;
   uint32_t xskeleton;
   struct {
-    ArmEncodingKind kind;
+    A64EncodingKind kind;
     int end;         // end for kFmtBitBlt, 1-bit slice end for FP regs.
     int start;       // start for kFmtBitBlt, 4-bit slice end for FP regs.
   } field_loc[4];
-  ArmOpcode opcode;  // can be WIDE()-ned to indicate it has a wide variant.
+  A64Opcode opcode;  // can be WIDE()-ned to indicate it has a wide variant.
   uint64_t flags;
   const char* name;
   const char* fmt;
@@ -436,25 +430,6 @@
   FixupKind fixup;
 };
 
-#if 0
-// TODO(Arm64): try the following alternative, which fits exactly in one cache line (64 bytes).
-struct ArmEncodingMap {
-  uint32_t wskeleton;
-  uint32_t xskeleton;
-  uint64_t flags;
-  const char* name;
-  const char* fmt;
-  struct {
-    uint8_t kind;
-    int8_t end;         // end for kFmtBitBlt, 1-bit slice end for FP regs.
-    int8_t start;       // start for kFmtBitBlt, 4-bit slice end for FP regs.
-  } field_loc[4];
-  uint32_t fixup;
-  uint32_t opcode;         // can be WIDE()-ned to indicate it has a wide variant.
-  uint32_t padding[3];
-};
-#endif
-
 }  // namespace art
 
 #endif  // ART_COMPILER_DEX_QUICK_ARM64_ARM64_LIR_H_
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index 15c89f2..b1cf279 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -47,7 +47,7 @@
   CUSTOM_VARIANTS(type00_skeleton, (type00_skeleton | 0x00400000))
 
 /*
- * opcode: ArmOpcode enum
+ * opcode: A64Opcode enum
  * variants: instruction skeletons supplied via CUSTOM_VARIANTS or derived macros.
  * a{n}k: key to applying argument {n}    \
  * a{n}s: argument {n} start bit position | n = 0, 1, 2, 3
@@ -89,6 +89,7 @@
  *     M -> 16-bit shift expression ("" or ", lsl #16" or ", lsl #32"...)
  *     B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
  *     H -> operand shift
+ *     h -> 6-bit shift immediate
  *     T -> register shift (either ", lsl #0" or ", lsl #12")
  *     e -> register extend (e.g. uxtb #1)
  *     o -> register shift (e.g. lsl #1) for Word registers
@@ -101,8 +102,8 @@
  *
  *  [!] escape.  To insert "!", use "!!"
  */
-/* NOTE: must be kept in sync with enum ArmOpcode from arm64_lir.h */
-const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = {
+/* NOTE: must be kept in sync with enum A64Opcode from arm64_lir.h */
+const A64EncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = {
     ENCODING_MAP(WIDE(kA64Adc3rrr), SF_VARIANTS(0x1a000000),
                  kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
@@ -228,27 +229,27 @@
                  kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
                  kFmtBitBlt, 15, 10, IS_QUAD_OP | REG_DEF0_USE12,
                  "extr", "!0r, !1r, !2r, #!3d", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fabs2ff), FLOAT_VARIANTS(0x1e20c000),
+    ENCODING_MAP(WIDE(kA64Fabs2ff), FLOAT_VARIANTS(0x1e20c000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP| REG_DEF0_USE1,
                  "fabs", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fadd3fff), FLOAT_VARIANTS(0x1e202800),
+    ENCODING_MAP(WIDE(kA64Fadd3fff), FLOAT_VARIANTS(0x1e202800),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
                  "fadd", "!0f, !1f, !2f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fcmp1f), FLOAT_VARIANTS(0x1e202008),
+    ENCODING_MAP(WIDE(kA64Fcmp1f), FLOAT_VARIANTS(0x1e202008),
                  kFmtRegF, 9, 5, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE0 | SETS_CCODES,
                  "fcmp", "!0f, #0", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fcmp2ff), FLOAT_VARIANTS(0x1e202000),
+    ENCODING_MAP(WIDE(kA64Fcmp2ff), FLOAT_VARIANTS(0x1e202000),
                  kFmtRegF, 9, 5, kFmtRegF, 20, 16, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
                  "fcmp", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fcvtzs2wf), FLOAT_VARIANTS(0x1e380000),
+    ENCODING_MAP(WIDE(kA64Fcvtzs2wf), FLOAT_VARIANTS(0x1e380000),
                  kFmtRegW, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "fcvtzs", "!0w, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fcvtzs2xf), FLOAT_VARIANTS(0x9e380000),
+    ENCODING_MAP(WIDE(kA64Fcvtzs2xf), FLOAT_VARIANTS(0x9e380000),
                  kFmtRegX, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "fcvtzs", "!0x, !1f", kFixupNone),
@@ -268,23 +269,23 @@
                  kFmtRegX, 4, 0, kFmtRegD, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "fcvtms", "!0x, !1S", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fdiv3fff), FLOAT_VARIANTS(0x1e201800),
+    ENCODING_MAP(WIDE(kA64Fdiv3fff), FLOAT_VARIANTS(0x1e201800),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
                  "fdiv", "!0f, !1f, !2f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fmax3fff), FLOAT_VARIANTS(0x1e204800),
+    ENCODING_MAP(WIDE(kA64Fmax3fff), FLOAT_VARIANTS(0x1e204800),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
                  "fmax", "!0f, !1f, !2f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fmin3fff), FLOAT_VARIANTS(0x1e205800),
+    ENCODING_MAP(WIDE(kA64Fmin3fff), FLOAT_VARIANTS(0x1e205800),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
                  "fmin", "!0f, !1f, !2f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fmov2ff), FLOAT_VARIANTS(0x1e204000),
+    ENCODING_MAP(WIDE(kA64Fmov2ff), FLOAT_VARIANTS(0x1e204000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | IS_MOVE,
                  "fmov", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fmov2fI), FLOAT_VARIANTS(0x1e201000),
+    ENCODING_MAP(WIDE(kA64Fmov2fI), FLOAT_VARIANTS(0x1e201000),
                  kFmtRegF, 4, 0, kFmtBitBlt, 20, 13, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
                  "fmov", "!0f, #!1I", kFixupNone),
@@ -304,35 +305,35 @@
                  kFmtRegX, 4, 0, kFmtRegD, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "fmov", "!0x, !1S", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fmul3fff), FLOAT_VARIANTS(0x1e200800),
+    ENCODING_MAP(WIDE(kA64Fmul3fff), FLOAT_VARIANTS(0x1e200800),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
                  "fmul", "!0f, !1f, !2f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fneg2ff), FLOAT_VARIANTS(0x1e214000),
+    ENCODING_MAP(WIDE(kA64Fneg2ff), FLOAT_VARIANTS(0x1e214000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "fneg", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Frintp2ff), FLOAT_VARIANTS(0x1e24c000),
+    ENCODING_MAP(WIDE(kA64Frintp2ff), FLOAT_VARIANTS(0x1e24c000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "frintp", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Frintm2ff), FLOAT_VARIANTS(0x1e254000),
+    ENCODING_MAP(WIDE(kA64Frintm2ff), FLOAT_VARIANTS(0x1e254000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "frintm", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Frintn2ff), FLOAT_VARIANTS(0x1e244000),
+    ENCODING_MAP(WIDE(kA64Frintn2ff), FLOAT_VARIANTS(0x1e244000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "frintn", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Frintz2ff), FLOAT_VARIANTS(0x1e25c000),
+    ENCODING_MAP(WIDE(kA64Frintz2ff), FLOAT_VARIANTS(0x1e25c000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "frintz", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fsqrt2ff), FLOAT_VARIANTS(0x1e61c000),
+    ENCODING_MAP(WIDE(kA64Fsqrt2ff), FLOAT_VARIANTS(0x1e61c000),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "fsqrt", "!0f, !1f", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Fsub3fff), FLOAT_VARIANTS(0x1e203800),
+    ENCODING_MAP(WIDE(kA64Fsub3fff), FLOAT_VARIANTS(0x1e203800),
                  kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
                  "fsub", "!0f, !1f, !2f", kFixupNone),
@@ -368,7 +369,7 @@
                  kFmtRegR, 4, 0, kFmtRegXOrSp, 9, 5, kFmtRegX, 20, 16,
                  kFmtBitBlt, 12, 12, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD_OFF,
                  "ldrsh", "!0r, [!1X, !2x, lsl #!3d]", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Ldr2fp), SIZE_VARIANTS(0x1c000000),
+    ENCODING_MAP(WIDE(kA64Ldr2fp), SIZE_VARIANTS(0x1c000000),
                  kFmtRegF, 4, 0, kFmtBitBlt, 23, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
@@ -378,7 +379,7 @@
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
                  "ldr", "!0r, !1p", kFixupLoad),
-    ENCODING_MAP(FWIDE(kA64Ldr3fXD), SIZE_VARIANTS(0xbd400000),
+    ENCODING_MAP(WIDE(kA64Ldr3fXD), SIZE_VARIANTS(0xbd400000),
                  kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 21, 10,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF,
                  "ldr", "!0f, [!1X, #!2D]", kFixupNone),
@@ -386,7 +387,7 @@
                  kFmtRegR, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 21, 10,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF,
                  "ldr", "!0r, [!1X, #!2D]", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Ldr4fXxG), SIZE_VARIANTS(0xbc606800),
+    ENCODING_MAP(WIDE(kA64Ldr4fXxG), SIZE_VARIANTS(0xbc606800),
                  kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtRegX, 20, 16,
                  kFmtBitBlt, 12, 12, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
                  "ldr", "!0f, [!1X, !2x!3G]", kFixupNone),
@@ -410,7 +411,7 @@
                  kFmtRegR, 4, 0, kFmtRegR, 14, 10, kFmtRegXOrSp, 9, 5,
                  kFmtBitBlt, 21, 15, IS_QUAD_OP | REG_USE2 | REG_DEF012 | IS_LOAD,
                  "ldp", "!0r, !1r, [!2X], #!3D", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Ldur3fXd), CUSTOM_VARIANTS(0xbc400000, 0xfc400000),
+    ENCODING_MAP(WIDE(kA64Ldur3fXd), CUSTOM_VARIANTS(0xbc400000, 0xfc400000),
                  kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 20, 12,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
                  "ldur", "!0f, [!1X, #!2d]", kFixupNone),
@@ -506,11 +507,11 @@
                  kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 21, 16,
                  kFmtBitBlt, 15, 10, IS_QUAD_OP | REG_DEF0_USE1,
                  "sbfm", "!0r, !1r, #!2d, #!3d", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Scvtf2fw), FLOAT_VARIANTS(0x1e220000),
+    ENCODING_MAP(WIDE(kA64Scvtf2fw), FLOAT_VARIANTS(0x1e220000),
                  kFmtRegF, 4, 0, kFmtRegW, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "scvtf", "!0f, !1w", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Scvtf2fx), FLOAT_VARIANTS(0x9e220000),
+    ENCODING_MAP(WIDE(kA64Scvtf2fx), FLOAT_VARIANTS(0x9e220000),
                  kFmtRegF, 4, 0, kFmtRegX, 9, 5, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
                  "scvtf", "!0f, !1x", kFixupNone),
@@ -546,11 +547,11 @@
                  kFmtRegR, 4, 0, kFmtRegR, 14, 10, kFmtRegXOrSp, 9, 5,
                  kFmtBitBlt, 21, 15, IS_QUAD_OP | REG_DEF2 | REG_USE012 | IS_STORE,
                  "stp", "!0r, !1r, [!2X, #!3D]!!", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Str3fXD), CUSTOM_VARIANTS(0xbd000000, 0xfd000000),
+    ENCODING_MAP(WIDE(kA64Str3fXD), CUSTOM_VARIANTS(0xbd000000, 0xfd000000),
                  kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 21, 10,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE_OFF,
                  "str", "!0f, [!1X, #!2D]", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Str4fXxG), CUSTOM_VARIANTS(0xbc206800, 0xfc206800),
+    ENCODING_MAP(WIDE(kA64Str4fXxG), CUSTOM_VARIANTS(0xbc206800, 0xfc206800),
                  kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtRegX, 20, 16,
                  kFmtBitBlt, 12, 12, IS_QUAD_OP | REG_USE012 | IS_STORE,
                  "str", "!0f, [!1X, !2x!3G]", kFixupNone),
@@ -582,7 +583,7 @@
                  kFmtRegR, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 20, 12,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | REG_DEF1 | IS_STORE,
                  "str", "!0r, [!1X], #!2d", kFixupNone),
-    ENCODING_MAP(FWIDE(kA64Stur3fXd), CUSTOM_VARIANTS(0xbc000000, 0xfc000000),
+    ENCODING_MAP(WIDE(kA64Stur3fXd), CUSTOM_VARIANTS(0xbc000000, 0xfc000000),
                  kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 20, 12,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
                  "stur", "!0f, [!1X, #!2d]", kFixupNone),
@@ -614,10 +615,24 @@
                  kFmtRegR, 4, 0, kFmtRegROrSp, 9, 5, kFmtBitBlt, 21, 10,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
                  "subs", "!0r, !1R, #!2d", kFixupNone),
-    ENCODING_MAP(WIDE(kA64Tst3rro), SF_VARIANTS(0x6a000000),
+    ENCODING_MAP(WIDE(kA64Tst2rl), SF_VARIANTS(0x7200001f),
+                 kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | SETS_CCODES,
+                 "tst", "!0r, !1l", kFixupNone),
+    ENCODING_MAP(WIDE(kA64Tst3rro), SF_VARIANTS(0x6a00001f),
                  kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtShift, -1, -1,
-                 kFmtUnused, -1, -1, IS_QUAD_OP | REG_USE01 | SETS_CCODES,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
                  "tst", "!0r, !1r!2o", kFixupNone),
+    // NOTE: Tbz/Tbnz does not require SETS_CCODES, but it may be replaced by some other LIRs
+    // which require SETS_CCODES in the fix-up stage.
+    ENCODING_MAP(WIDE(kA64Tbnz3rht), CUSTOM_VARIANTS(0x37000000, 0x37000000),
+                 kFmtRegR, 4, 0, kFmtImm6Shift, -1, -1, kFmtBitBlt, 18, 5, kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP | SETS_CCODES,
+                 "tbnz", "!0r, #!1h, !2t", kFixupTBxZ),
+    ENCODING_MAP(WIDE(kA64Tbz3rht), CUSTOM_VARIANTS(0x36000000, 0x36000000),
+                 kFmtRegR, 4, 0, kFmtImm6Shift, -1, -1, kFmtBitBlt, 18, 5, kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP | SETS_CCODES,
+                 "tbz", "!0r, #!1h, !2t", kFixupTBxZ),
     ENCODING_MAP(WIDE(kA64Ubfm4rrdd), SF_N_VARIANTS(0x53000000),
                  kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 21, 16,
                  kFmtBitBlt, 15, 10, IS_QUAD_OP | REG_DEF0_USE1,
@@ -652,21 +667,21 @@
 uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) {
   for (; lir != nullptr; lir = NEXT_LIR(lir)) {
     bool opcode_is_wide = IS_WIDE(lir->opcode);
-    ArmOpcode opcode = UNWIDE(lir->opcode);
+    A64Opcode opcode = UNWIDE(lir->opcode);
 
     if (UNLIKELY(IsPseudoLirOp(opcode))) {
       continue;
     }
 
     if (LIKELY(!lir->flags.is_nop)) {
-      const ArmEncodingMap *encoder = &EncodingMap[opcode];
+      const A64EncodingMap *encoder = &EncodingMap[opcode];
 
       // Select the right variant of the skeleton.
       uint32_t bits = opcode_is_wide ? encoder->xskeleton : encoder->wskeleton;
       DCHECK(!opcode_is_wide || IS_WIDE(encoder->opcode));
 
       for (int i = 0; i < 4; i++) {
-        ArmEncodingKind kind = encoder->field_loc[i].kind;
+        A64EncodingKind kind = encoder->field_loc[i].kind;
         uint32_t operand = lir->operands[i];
         uint32_t value;
 
@@ -787,6 +802,11 @@
               value |= ((operand & 0x1ffffc) >> 2) << 5;
               bits |= value;
               break;
+            case kFmtImm6Shift:
+              value = (operand & 0x1f) << 19;
+              value |= ((operand & 0x20) >> 5) << 31;
+              bits |= value;
+              break;
             default:
               LOG(FATAL) << "Bad fmt for arg. " << i << " in " << encoder->name
                          << " (" << kind << ")";
@@ -827,11 +847,6 @@
    */
   int generation = 0;
   while (true) {
-    // TODO(Arm64): check whether passes and offset adjustments are really necessary.
-    //   Currently they aren't, as - in the fixups below - LIR are never inserted.
-    //   Things can be different if jump ranges above 1 MB need to be supported.
-    //   If they are not, then we can get rid of the assembler retry logic.
-
     offset_adjustment = 0;
     AssemblerStatus res = kSuccess;  // Assume success
     generation ^= 1;
@@ -839,13 +854,9 @@
     lir = first_fixup_;
     prev_lir = NULL;
     while (lir != NULL) {
-      /*
-       * NOTE: the lir being considered here will be encoded following the switch (so long as
-       * we're not in a retry situation).  However, any new non-pc_rel instructions inserted
-       * due to retry must be explicitly encoded at the time of insertion.  Note that
-       * inserted instructions don't need use/def flags, but do need size and pc-rel status
-       * properly updated.
-       */
+      // NOTE: Any new non-pc_rel instructions inserted due to retry must be explicitly encoded at
+      // the time of insertion.  Note that inserted instructions don't need use/def flags, but do
+      // need size and pc-rel status properly updated.
       lir->offset += offset_adjustment;
       // During pass, allows us to tell whether a node has been updated with offset_adjustment yet.
       lir->flags.generation = generation;
@@ -861,7 +872,8 @@
           CodeOffset target = target_lir->offset +
               ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
           int32_t delta = target - pc;
-          if (!((delta & 0x3) == 0 && IS_SIGNED_IMM19(delta >> 2))) {
+          DCHECK_EQ(delta & 0x3, 0);
+          if (!IS_SIGNED_IMM19(delta >> 2)) {
             LOG(FATAL) << "Invalid jump range in kFixupT1Branch";
           }
           lir->operands[0] = delta >> 2;
@@ -876,12 +888,75 @@
           CodeOffset target = target_lir->offset +
             ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
           int32_t delta = target - pc;
-          if (!((delta & 0x3) == 0 && IS_SIGNED_IMM19(delta >> 2))) {
+          DCHECK_EQ(delta & 0x3, 0);
+          if (!IS_SIGNED_IMM19(delta >> 2)) {
             LOG(FATAL) << "Invalid jump range in kFixupLoad";
           }
           lir->operands[1] = delta >> 2;
           break;
         }
+        case kFixupTBxZ: {
+          int16_t opcode = lir->opcode;
+          RegStorage reg(lir->operands[0] | RegStorage::kValid);
+          int32_t imm = lir->operands[1];
+          DCHECK_EQ(IS_WIDE(opcode), reg.Is64Bit());
+          DCHECK_LT(imm, 64);
+          if (imm >= 32) {
+            DCHECK(IS_WIDE(opcode));
+          } else if (kIsDebugBuild && IS_WIDE(opcode)) {
+            // "tbz/tbnz x0, #imm(<32)" is the same with "tbz/tbnz w0, #imm(<32)", but GCC/oatdump
+            // will disassemble it as "tbz/tbnz w0, #imm(<32)". So unwide the LIR to make the
+            // compiler log behave the same with those disassembler in debug build.
+            // This will also affect tst instruction if it need to be replaced, but there is no
+            // performance difference between "tst Xt" and "tst Wt".
+            lir->opcode = UNWIDE(opcode);
+            lir->operands[0] = As32BitReg(reg).GetReg();
+          }
+
+          // Fix-up branch offset.
+          LIR *target_lir = lir->target;
+          DCHECK(target_lir);
+          CodeOffset pc = lir->offset;
+          CodeOffset target = target_lir->offset +
+              ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+          int32_t delta = target - pc;
+          DCHECK_EQ(delta & 0x3, 0);
+          // Check if branch offset can be encoded in tbz/tbnz.
+          if (!IS_SIGNED_IMM14(delta >> 2)) {
+            DexOffset dalvik_offset = lir->dalvik_offset;
+            int16_t opcode = lir->opcode;
+            LIR* target = lir->target;
+            // "tbz/tbnz Rt, #imm, label" -> "tst Rt, #(1<<imm)".
+            offset_adjustment -= lir->flags.size;
+            int32_t imm = EncodeLogicalImmediate(IS_WIDE(opcode), 1 << lir->operands[1]);
+            DCHECK_NE(imm, -1);
+            lir->opcode = IS_WIDE(opcode) ? WIDE(kA64Tst2rl) : kA64Tst2rl;
+            lir->operands[1] = imm;
+            lir->target = nullptr;
+            lir->flags.fixup = EncodingMap[kA64Tst2rl].fixup;
+            lir->flags.size = EncodingMap[kA64Tst2rl].size;
+            offset_adjustment += lir->flags.size;
+            // Insert "beq/bneq label".
+            opcode = UNWIDE(opcode);
+            DCHECK(opcode == kA64Tbz3rht || opcode == kA64Tbnz3rht);
+            LIR* new_lir = RawLIR(dalvik_offset, kA64B2ct,
+                opcode == kA64Tbz3rht ? kArmCondEq : kArmCondNe, 0, 0, 0, 0, target);
+            InsertLIRAfter(lir, new_lir);
+            new_lir->offset = lir->offset + lir->flags.size;
+            new_lir->flags.generation = generation;
+            new_lir->flags.fixup = EncodingMap[kA64B2ct].fixup;
+            new_lir->flags.size = EncodingMap[kA64B2ct].size;
+            offset_adjustment += new_lir->flags.size;
+            // lir no longer pcrel, unlink and link in new_lir.
+            ReplaceFixup(prev_lir, lir, new_lir);
+            prev_lir = new_lir;  // Continue with the new instruction.
+            lir = new_lir->u.a.pcrel_next;
+            res = kRetryAll;
+            continue;
+          }
+          lir->operands[2] = delta >> 2;
+          break;
+        }
         case kFixupAdr: {
           LIR* target_lir = lir->target;
           int32_t delta;
@@ -910,6 +985,7 @@
     }
 
     if (res == kSuccess) {
+      DCHECK_EQ(offset_adjustment, 0);
       break;
     } else {
       assembler_retries++;
@@ -951,7 +1027,7 @@
 }
 
 size_t Arm64Mir2Lir::GetInsnSize(LIR* lir) {
-  ArmOpcode opcode = UNWIDE(lir->opcode);
+  A64Opcode opcode = UNWIDE(lir->opcode);
   DCHECK(!IsPseudoLirOp(opcode));
   return EncodingMap[opcode].size;
 }
@@ -962,7 +1038,7 @@
 
   LIR* last_fixup = NULL;
   for (LIR* lir = head_lir; lir != end_lir; lir = NEXT_LIR(lir)) {
-    ArmOpcode opcode = UNWIDE(lir->opcode);
+    A64Opcode opcode = UNWIDE(lir->opcode);
     if (!lir->flags.is_nop) {
       if (lir->flags.fixup != kFixupNone) {
         if (!IsPseudoLirOp(opcode)) {
diff --git a/compiler/dex/quick/arm64/backend_arm64.h b/compiler/dex/quick/arm64/backend_arm64.h
new file mode 100644
index 0000000..53650c4
--- /dev/null
+++ b/compiler/dex/quick/arm64/backend_arm64.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEX_QUICK_ARM64_BACKEND_ARM64_H_
+#define ART_COMPILER_DEX_QUICK_ARM64_BACKEND_ARM64_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* Arm64CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+                            ArenaAllocator* const arena);
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_QUICK_ARM64_BACKEND_ARM64_H_
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index eddc3a3..6081f28 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -44,7 +44,7 @@
  * quit:
  */
 void Arm64Mir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpSparseSwitchTable(table);
   }
@@ -55,7 +55,7 @@
   tab_rec->vaddr = current_dalvik_offset_;
   uint32_t size = table[1];
   tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
-  switch_tables_.Insert(tab_rec);
+  switch_tables_.push_back(tab_rec);
 
   // Get the switch value
   rl_src = LoadValue(rl_src, kCoreReg);
@@ -96,7 +96,7 @@
 
 
 void Arm64Mir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpPackedSwitchTable(table);
   }
@@ -108,7 +108,7 @@
   uint32_t size = table[1];
   tab_rec->targets =
       static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
-  switch_tables_.Insert(tab_rec);
+  switch_tables_.push_back(tab_rec);
 
   // Get the switch value
   rl_src = LoadValue(rl_src, kCoreReg);
@@ -156,8 +156,8 @@
  *
  * Total size is 4+(width * size + 1)/2 16-bit code units.
  */
-void Arm64Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void Arm64Mir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   // Add the table to the list - we'll process it later
   FillArrayData *tab_rec =
       static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
@@ -167,7 +167,7 @@
   uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
   tab_rec->size = (size * width) + 8;
 
-  fill_array_data_.Insert(tab_rec);
+  fill_array_data_.push_back(tab_rec);
 
   // Making a call - use explicit registers
   FlushAllRegs();   /* Everything to home location */
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 3e1c18b..55cc938 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -19,6 +19,7 @@
 
 #include "arm64_lir.h"
 #include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
 
 #include <map>
 
@@ -167,6 +168,7 @@
   bool GenInlinedRound(CallInfo* info, bool is_double) OVERRIDE;
   bool GenInlinedPeek(CallInfo* info, OpSize size) OVERRIDE;
   bool GenInlinedPoke(CallInfo* info, OpSize size) OVERRIDE;
+  bool GenInlinedAbsInt(CallInfo* info) OVERRIDE;
   bool GenInlinedAbsLong(CallInfo* info) OVERRIDE;
   bool GenInlinedArrayCopyCharArray(CallInfo* info) OVERRIDE;
   void GenIntToLong(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
@@ -181,7 +183,7 @@
   void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
   void GenExitSequence() OVERRIDE;
   void GenSpecialExitSequence() OVERRIDE;
-  void GenFillArrayData(DexOffset table_offset, RegLocation rl_src) OVERRIDE;
+  void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
   void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) OVERRIDE;
   void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) OVERRIDE;
   void GenSelect(BasicBlock* bb, MIR* mir) OVERRIDE;
@@ -393,7 +395,7 @@
                      RegLocation rl_src2, bool is_div);
 
   InToRegStorageMapping in_to_reg_storage_mapping_;
-  static const ArmEncodingMap EncodingMap[kA64Last];
+  static const A64EncodingMap EncodingMap[kA64Last];
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index 7268d70..db24d12 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -112,7 +112,7 @@
   rl_result = EvalLoc(rl_dest, kFPReg, true);
   DCHECK(rl_dest.wide);
   DCHECK(rl_result.wide);
-  NewLIR3(FWIDE(op), rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
+  NewLIR3(WIDE(op), rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
   StoreValueWide(rl_dest, rl_result);
 }
 
@@ -145,17 +145,17 @@
       dst_reg_class = kFPReg;
       break;
     case Instruction::INT_TO_DOUBLE:
-      op = FWIDE(kA64Scvtf2fw);
+      op = WIDE(kA64Scvtf2fw);
       src_reg_class = kCoreReg;
       dst_reg_class = kFPReg;
       break;
     case Instruction::DOUBLE_TO_INT:
-      op = FWIDE(kA64Fcvtzs2wf);
+      op = WIDE(kA64Fcvtzs2wf);
       src_reg_class = kFPReg;
       dst_reg_class = kCoreReg;
       break;
     case Instruction::LONG_TO_DOUBLE:
-      op = FWIDE(kA64Scvtf2fx);
+      op = WIDE(kA64Scvtf2fx);
       src_reg_class = kCoreReg;
       dst_reg_class = kFPReg;
       break;
@@ -170,7 +170,7 @@
       dst_reg_class = kFPReg;
       break;
     case Instruction::DOUBLE_TO_LONG:
-      op = FWIDE(kA64Fcvtzs2xf);
+      op = WIDE(kA64Fcvtzs2xf);
       src_reg_class = kFPReg;
       dst_reg_class = kCoreReg;
       break;
@@ -208,7 +208,7 @@
     rl_src2 = mir_graph_->GetSrcWide(mir, 2);
     rl_src1 = LoadValueWide(rl_src1, kFPReg);
     rl_src2 = LoadValueWide(rl_src2, kFPReg);
-    NewLIR2(FWIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
+    NewLIR2(WIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
   } else {
     rl_src1 = mir_graph_->GetSrc(mir, 0);
     rl_src2 = mir_graph_->GetSrc(mir, 1);
@@ -281,7 +281,7 @@
     ClobberSReg(rl_dest.s_reg_low);
     rl_result = EvalLoc(rl_dest, kCoreReg, true);
     LoadConstant(rl_result.reg, default_result);
-    NewLIR2(FWIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
+    NewLIR2(WIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
   } else {
     rl_src1 = LoadValue(rl_src1, kFPReg);
     rl_src2 = LoadValue(rl_src2, kFPReg);
@@ -318,7 +318,7 @@
   RegLocation rl_result;
   rl_src = LoadValueWide(rl_src, kFPReg);
   rl_result = EvalLoc(rl_dest, kFPReg, true);
-  NewLIR2(FWIDE(kA64Fneg2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+  NewLIR2(WIDE(kA64Fneg2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
   StoreValueWide(rl_dest, rl_result);
 }
 
@@ -353,7 +353,8 @@
   if (reg_class == kFPReg) {
     NewLIR2(kA64Fabs2ff, rl_result.reg.GetReg(), rl_src.reg.GetReg());
   } else {
-    NewLIR4(kA64Ubfm4rrdd, rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 30);
+    // Clear the sign bit in an integer register.
+    OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
   }
   StoreValue(rl_dest, rl_result);
   return true;
@@ -369,9 +370,10 @@
   rl_src = LoadValueWide(rl_src, reg_class);
   RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
   if (reg_class == kFPReg) {
-    NewLIR2(FWIDE(kA64Fabs2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+    NewLIR2(WIDE(kA64Fabs2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
   } else {
-    NewLIR4(WIDE(kA64Ubfm4rrdd), rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 62);
+    // Clear the sign bit in an integer register.
+    OpRegRegImm64(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffffffffffff);
   }
   StoreValueWide(rl_dest, rl_result);
   return true;
@@ -382,7 +384,7 @@
   RegLocation rl_dest = InlineTargetWide(info);  // double place for result
   rl_src = LoadValueWide(rl_src, kFPReg);
   RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
-  NewLIR2(FWIDE(kA64Fsqrt2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+  NewLIR2(WIDE(kA64Fsqrt2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
   StoreValueWide(rl_dest, rl_result);
   return true;
 }
@@ -392,7 +394,7 @@
   RegLocation rl_dest = InlineTargetWide(info);
   rl_src = LoadValueWide(rl_src, kFPReg);
   RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
-  NewLIR2(FWIDE(kA64Frintp2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+  NewLIR2(WIDE(kA64Frintp2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
   StoreValueWide(rl_dest, rl_result);
   return true;
 }
@@ -402,7 +404,7 @@
   RegLocation rl_dest = InlineTargetWide(info);
   rl_src = LoadValueWide(rl_src, kFPReg);
   RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
-  NewLIR2(FWIDE(kA64Frintm2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+  NewLIR2(WIDE(kA64Frintm2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
   StoreValueWide(rl_dest, rl_result);
   return true;
 }
@@ -412,14 +414,14 @@
   RegLocation rl_dest = InlineTargetWide(info);
   rl_src = LoadValueWide(rl_src, kFPReg);
   RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
-  NewLIR2(FWIDE(kA64Frintn2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+  NewLIR2(WIDE(kA64Frintn2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
   StoreValueWide(rl_dest, rl_result);
   return true;
 }
 
 bool Arm64Mir2Lir::GenInlinedRound(CallInfo* info, bool is_double) {
   int32_t encoded_imm = EncodeImmSingle(bit_cast<float, uint32_t>(0.5f));
-  ArmOpcode wide = (is_double) ? FWIDE(0) : FUNWIDE(0);
+  A64Opcode wide = (is_double) ? WIDE(0) : UNWIDE(0);
   RegLocation rl_src = info->args[0];
   RegLocation rl_dest = (is_double) ? InlineTargetWide(info) : InlineTarget(info);
   rl_src = (is_double) ? LoadValueWide(rl_src, kFPReg) : LoadValue(rl_src, kFPReg);
@@ -437,7 +439,7 @@
 bool Arm64Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) {
   DCHECK_EQ(cu_->instruction_set, kArm64);
   int op = (is_min) ? kA64Fmin3fff : kA64Fmax3fff;
-  ArmOpcode wide = (is_double) ? FWIDE(0) : FUNWIDE(0);
+  A64Opcode wide = (is_double) ? WIDE(0) : UNWIDE(0);
   RegLocation rl_src1 = info->args[0];
   RegLocation rl_src2 = (is_double) ? info->args[2] : info->args[1];
   rl_src1 = (is_double) ? LoadValueWide(rl_src1, kFPReg) : LoadValue(rl_src1, kFPReg);
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 1e97a32..305a6c9 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -262,17 +262,21 @@
   ArmConditionCode arm_cond = ArmConditionEncoding(cond);
   if (check_value == 0) {
     if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) {
-      ArmOpcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
-      ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
+      A64Opcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
+      A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
       branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
     } else if (arm_cond == kArmCondLs) {
       // kArmCondLs is an unsigned less or equal. A comparison r <= 0 is then the same as cbz.
       // This case happens for a bounds check of array[0].
-      ArmOpcode opcode = kA64Cbz2rt;
-      ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
+      A64Opcode opcode = kA64Cbz2rt;
+      A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
       branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
+    } else if (arm_cond == kArmCondLt || arm_cond == kArmCondGe) {
+      A64Opcode opcode = (arm_cond == kArmCondLt) ? kA64Tbnz3rht : kA64Tbz3rht;
+      A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
+      int value = reg.Is64Bit() ? 63 : 31;
+      branch = NewLIR3(opcode | wide, reg.GetReg(), value, 0);
     }
-    // TODO: Use tbz/tbnz for < 0 or >= 0.
   }
 
   if (branch == nullptr) {
@@ -301,7 +305,7 @@
 LIR* Arm64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
   bool dest_is_fp = r_dest.IsFloat();
   bool src_is_fp = r_src.IsFloat();
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
   LIR* res;
 
   if (LIKELY(dest_is_fp == src_is_fp)) {
@@ -329,7 +333,7 @@
       DCHECK_EQ(dest_is_double, src_is_double);
 
       // Homogeneous float/float copy.
-      opcode = (dest_is_double) ? FWIDE(kA64Fmov2ff) : kA64Fmov2ff;
+      opcode = (dest_is_double) ? WIDE(kA64Fmov2ff) : kA64Fmov2ff;
     }
   } else {
     // Inhomogeneous register copy.
@@ -625,7 +629,7 @@
     // temp = r_src1 / r_src2
     // dest = r_src1 - temp * r_src2
     RegStorage temp;
-    ArmOpcode wide;
+    A64Opcode wide;
     if (rl_result.reg.Is64Bit()) {
       temp = AllocTempWide();
       wide = WIDE(0);
@@ -641,16 +645,32 @@
   return rl_result;
 }
 
+bool Arm64Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
+  RegLocation rl_src = info->args[0];
+  rl_src = LoadValue(rl_src, kCoreReg);
+  RegLocation rl_dest = InlineTarget(info);
+  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+
+  // Compare the source value with zero. Write the negated value to the result if
+  // negative, otherwise write the original value.
+  OpRegImm(kOpCmp, rl_src.reg, 0);
+  NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_src.reg.GetReg(), rl_src.reg.GetReg(),
+          kArmCondPl);
+  StoreValue(rl_dest, rl_result);
+  return true;
+}
+
 bool Arm64Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
   RegLocation rl_src = info->args[0];
   rl_src = LoadValueWide(rl_src, kCoreReg);
   RegLocation rl_dest = InlineTargetWide(info);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
-  RegStorage sign_reg = AllocTempWide();
-  // abs(x) = y<=x>>63, (x+y)^y.
-  OpRegRegImm(kOpAsr, sign_reg, rl_src.reg, 63);
-  OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, sign_reg);
-  OpRegReg(kOpXor, rl_result.reg, sign_reg);
+
+  // Compare the source value with zero. Write the negated value to the result if
+  // negative, otherwise write the original value.
+  OpRegImm(kOpCmp, rl_src.reg, 0);
+  NewLIR4(WIDE(kA64Csneg4rrrc), rl_result.reg.GetReg(), rl_src.reg.GetReg(),
+          rl_src.reg.GetReg(), kArmCondPl);
   StoreValueWide(rl_dest, rl_result);
   return true;
 }
@@ -749,7 +769,7 @@
   RegStorage r_tmp;
   RegStorage r_tmp_stored;
   RegStorage rl_new_value_stored = rl_new_value.reg;
-  ArmOpcode wide = UNWIDE(0);
+  A64Opcode wide = UNWIDE(0);
   if (is_long) {
     r_tmp_stored = r_tmp = AllocTempWide();
     wide = WIDE(0);
@@ -855,16 +875,14 @@
   OpRegRegImm(kOpLsl, rs_length, rs_length, 1);
 
   // Copy one element.
-  OpRegRegImm(kOpAnd, rs_tmp, As32BitReg(rs_length), 2);
-  LIR* jmp_to_copy_two = OpCmpImmBranch(kCondEq, rs_tmp, 0, nullptr);
+  LIR* jmp_to_copy_two = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 1, 0);
   OpRegImm(kOpSub, rs_length, 2);
   LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, kSignedHalf);
   StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, kSignedHalf);
 
   // Copy two elements.
   LIR *copy_two = NewLIR0(kPseudoTargetLabel);
-  OpRegRegImm(kOpAnd, rs_tmp, As32BitReg(rs_length), 4);
-  LIR* jmp_to_copy_four = OpCmpImmBranch(kCondEq, rs_tmp, 0, nullptr);
+  LIR* jmp_to_copy_four = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 2, 0);
   OpRegImm(kOpSub, rs_length, 4);
   LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, k32);
   StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, k32);
@@ -943,7 +961,7 @@
   // Combine sub & test using sub setflags encoding here.  We need to make sure a
   // subtract form that sets carry is used, so generate explicitly.
   // TODO: might be best to add a new op, kOpSubs, and handle it generically.
-  ArmOpcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
+  A64Opcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
   NewLIR3(opcode, reg.GetReg(), reg.GetReg(), 1);  // For value == 1, this should set flags.
   DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
   return OpCondBranch(c_code, target);
@@ -1440,7 +1458,7 @@
   for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
     reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
     if (UNLIKELY(reg2 < 0)) {
-      m2l->NewLIR3(FWIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
+      m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
                    offset);
     } else {
       m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
@@ -1551,7 +1569,7 @@
       // Have some FP regs to do.
       fp_reg_mask = GenPairWise(fp_reg_mask, &reg1, &reg2);
       if (UNLIKELY(reg2 < 0)) {
-        m2l->NewLIR3(FWIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
+        m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
                      cur_offset);
         // Do not increment offset here, as the second half will be filled by a core reg.
       } else {
@@ -1624,7 +1642,7 @@
   for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
      reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
     if (UNLIKELY(reg2 < 0)) {
-      m2l->NewLIR3(FWIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
+      m2l->NewLIR3(WIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
                    offset);
     } else {
       m2l->NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
@@ -1686,13 +1704,13 @@
 }
 
 bool Arm64Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
-  ArmOpcode wide = (size == k64) ? WIDE(0) : UNWIDE(0);
+  A64Opcode wide = IsWide(size) ? WIDE(0) : UNWIDE(0);
   RegLocation rl_src_i = info->args[0];
-  RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);  // result reg
+  RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info);  // result reg
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
-  RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
+  RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
   NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
-  (size == k64) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
+  IsWide(size) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
   return true;
 }
 
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 9b4546a..0462530 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -20,6 +20,7 @@
 
 #include <string>
 
+#include "backend_arm64.h"
 #include "dex/compiler_internals.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
@@ -83,23 +84,23 @@
 static constexpr ArrayRef<const RegStorage> dp_temps(dp_temps_arr);
 
 RegLocation Arm64Mir2Lir::LocCReturn() {
-  return arm_loc_c_return;
+  return a64_loc_c_return;
 }
 
 RegLocation Arm64Mir2Lir::LocCReturnRef() {
-  return arm_loc_c_return_ref;
+  return a64_loc_c_return_ref;
 }
 
 RegLocation Arm64Mir2Lir::LocCReturnWide() {
-  return arm_loc_c_return_wide;
+  return a64_loc_c_return_wide;
 }
 
 RegLocation Arm64Mir2Lir::LocCReturnFloat() {
-  return arm_loc_c_return_float;
+  return a64_loc_c_return_float;
 }
 
 RegLocation Arm64Mir2Lir::LocCReturnDouble() {
-  return arm_loc_c_return_double;
+  return a64_loc_c_return_double;
 }
 
 // Return a target-dependent special register.
@@ -152,7 +153,7 @@
 
   return ResourceMask::Bit(
       // FP register starts at bit position 32.
-      (reg.IsFloat() ? kArm64FPReg0 : 0) + reg.GetRegNum());
+      (reg.IsFloat() ? kA64FPReg0 : 0) + reg.GetRegNum());
 }
 
 ResourceMask Arm64Mir2Lir::GetPCUseDefEncoding() const {
@@ -172,15 +173,15 @@
   // These flags are somewhat uncommon - bypass if we can.
   if ((flags & (REG_DEF_SP | REG_USE_SP | REG_DEF_LR)) != 0) {
     if (flags & REG_DEF_SP) {
-      def_mask->SetBit(kArm64RegSP);
+      def_mask->SetBit(kA64RegSP);
     }
 
     if (flags & REG_USE_SP) {
-      use_mask->SetBit(kArm64RegSP);
+      use_mask->SetBit(kA64RegSP);
     }
 
     if (flags & REG_DEF_LR) {
-      def_mask->SetBit(kArm64RegLR);
+      def_mask->SetBit(kA64RegLR);
     }
   }
 }
@@ -248,19 +249,22 @@
   }
 }
 
-#define BIT_MASK(w) ((UINT64_C(1) << (w)) - UINT64_C(1))
+static uint64_t bit_mask(unsigned width) {
+  DCHECK_LE(width, 64U);
+  return (width == 64) ? static_cast<uint64_t>(-1) : ((UINT64_C(1) << (width)) - UINT64_C(1));
+}
 
 static uint64_t RotateRight(uint64_t value, unsigned rotate, unsigned width) {
   DCHECK_LE(width, 64U);
   rotate &= 63;
-  value = value & BIT_MASK(width);
-  return ((value & BIT_MASK(rotate)) << (width - rotate)) | (value >> rotate);
+  value = value & bit_mask(width);
+  return ((value & bit_mask(rotate)) << (width - rotate)) | (value >> rotate);
 }
 
 static uint64_t RepeatBitsAcrossReg(bool is_wide, uint64_t value, unsigned width) {
   unsigned i;
   unsigned reg_size = (is_wide) ? 64 : 32;
-  uint64_t result = value & BIT_MASK(width);
+  uint64_t result = value & bit_mask(width);
   for (i = width; i < reg_size; i *= 2) {
     result |= (result << i);
   }
@@ -299,7 +303,7 @@
 
   if (n == 1) {
     DCHECK_NE(imm_s, 0x3fU);
-    uint64_t bits = BIT_MASK(imm_s + 1);
+    uint64_t bits = bit_mask(imm_s + 1);
     return RotateRight(bits, imm_r, 64);
   } else {
     DCHECK_NE((imm_s >> 1), 0x1fU);
@@ -307,7 +311,7 @@
       if ((imm_s & width) == 0) {
         unsigned mask = (unsigned)(width - 1);
         DCHECK_NE((imm_s & mask), mask);
-        uint64_t bits = BIT_MASK((imm_s & mask) + 1);
+        uint64_t bits = bit_mask((imm_s & mask) + 1);
         return RepeatBitsAcrossReg(is_wide, RotateRight(bits, imm_r & mask, width), width);
       }
     }
@@ -404,7 +408,7 @@
              snprintf(tbuf, arraysize(tbuf), "d%d", operand & RegStorage::kRegNumMask);
              break;
            case 'f':
-             snprintf(tbuf, arraysize(tbuf), "%c%d", (IS_FWIDE(lir->opcode)) ? 'd' : 's',
+             snprintf(tbuf, arraysize(tbuf), "%c%d", (IS_WIDE(lir->opcode)) ? 'd' : 's',
                       operand & RegStorage::kRegNumMask);
              break;
            case 'l': {
@@ -504,6 +508,9 @@
              else
                strcpy(tbuf, ", DecodeError3");
              break;
+           case 'h':
+             snprintf(tbuf, arraysize(tbuf), "%d", operand);
+             break;
            default:
              strcpy(tbuf, "DecodeError1");
              break;
@@ -527,7 +534,7 @@
     char num[8];
     int i;
 
-    for (i = 0; i < kArm64RegEnd; i++) {
+    for (i = 0; i < kA64RegEnd; i++) {
       if (mask.HasBit(i)) {
         snprintf(num, arraysize(num), "%d ", i);
         strcat(buf, num);
@@ -595,14 +602,13 @@
 }
 
 void Arm64Mir2Lir::CompilerInitializeRegAlloc() {
-  reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs, core64_regs, sp_regs, dp_regs,
-                                        reserved_regs, reserved64_regs, core_temps, core64_temps,
-                                        sp_temps, dp_temps);
+  reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, core64_regs, sp_regs, dp_regs,
+                                            reserved_regs, reserved64_regs,
+                                            core_temps, core64_temps, sp_temps, dp_temps));
 
   // Target-specific adjustments.
   // Alias single precision float registers to corresponding double registers.
-  GrowableArray<RegisterInfo*>::Iterator fp_it(&reg_pool_->sp_regs_);
-  for (RegisterInfo* info = fp_it.Next(); info != nullptr; info = fp_it.Next()) {
+  for (RegisterInfo* info : reg_pool_->sp_regs_) {
     int fp_reg_num = info->GetReg().GetRegNum();
     RegStorage dp_reg = RegStorage::FloatSolo64(fp_reg_num);
     RegisterInfo* dp_reg_info = GetRegInfo(dp_reg);
@@ -615,8 +621,7 @@
   }
 
   // Alias 32bit W registers to corresponding 64bit X registers.
-  GrowableArray<RegisterInfo*>::Iterator w_it(&reg_pool_->core_regs_);
-  for (RegisterInfo* info = w_it.Next(); info != nullptr; info = w_it.Next()) {
+  for (RegisterInfo* info : reg_pool_->core_regs_) {
     int x_reg_num = info->GetReg().GetRegNum();
     RegStorage x_reg = RegStorage::Solo64(x_reg_num);
     RegisterInfo* x_reg_info = GetRegInfo(x_reg);
@@ -889,11 +894,11 @@
 
 RegStorage Arm64Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
   if (!in_to_reg_storage_mapping_.IsInitialized()) {
-    int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+    int start_vreg = mir_graph_->GetFirstInVR();
     RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
 
     InToRegStorageArm64Mapper mapper;
-    in_to_reg_storage_mapping_.Initialize(arg_locs, cu_->num_ins, &mapper);
+    in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
   }
   return in_to_reg_storage_mapping_.Get(arg_num);
 }
@@ -927,14 +932,14 @@
     StoreRefDisp(TargetPtrReg(kSp), 0, rl_src.reg, kNotVolatile);
   }
 
-  if (cu_->num_ins == 0) {
+  if (mir_graph_->GetNumOfInVRs() == 0) {
     return;
   }
 
   // Handle dalvik registers.
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-  int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
-  for (int i = 0; i < cu_->num_ins; i++) {
+  int start_vreg = mir_graph_->GetFirstInVR();
+  for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
     RegLocation* t_loc = &ArgLocs[i];
     OpSize op_size;
     RegStorage reg = GetArgPhysicalReg(t_loc, &num_gpr_used, &num_fpr_used, &op_size);
@@ -1077,9 +1082,6 @@
       }
     }
 
-    // Logic below assumes that Method pointer is at offset zero from SP.
-    DCHECK_EQ(VRegOffset(static_cast<int>(kVRegMethodPtrBaseReg)), 0);
-
     // The rest can be copied together
     int start_offset = SRegOffset(info->args[last_mapped_in + 1].s_reg_low);
     int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + 1,
diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index f58f830..9266d5c 100644
--- a/compiler/dex/quick/arm64/utility_arm64.cc
+++ b/compiler/dex/quick/arm64/utility_arm64.cc
@@ -89,9 +89,9 @@
 
 size_t Arm64Mir2Lir::GetLoadStoreSize(LIR* lir) {
   bool opcode_is_wide = IS_WIDE(lir->opcode);
-  ArmOpcode opcode = UNWIDE(lir->opcode);
+  A64Opcode opcode = UNWIDE(lir->opcode);
   DCHECK(!IsPseudoLirOp(opcode));
-  const ArmEncodingMap *encoder = &EncodingMap[opcode];
+  const A64EncodingMap *encoder = &EncodingMap[opcode];
   uint32_t bits = opcode_is_wide ? encoder->xskeleton : encoder->wskeleton;
   return (bits >> 30);
 }
@@ -138,7 +138,7 @@
   } else {
     int32_t encoded_imm = EncodeImmDouble(value);
     if (encoded_imm >= 0) {
-      return NewLIR2(FWIDE(kA64Fmov2fI), r_dest.GetReg(), encoded_imm);
+      return NewLIR2(WIDE(kA64Fmov2fI), r_dest.GetReg(), encoded_imm);
     }
   }
 
@@ -151,7 +151,7 @@
   }
 
   ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-  LIR* load_pc_rel = RawLIR(current_dalvik_offset_, FWIDE(kA64Ldr2fp),
+  LIR* load_pc_rel = RawLIR(current_dalvik_offset_, WIDE(kA64Ldr2fp),
                             r_dest.GetReg(), 0, 0, 0, 0, data_target);
   AppendLIR(load_pc_rel);
   return load_pc_rel;
@@ -415,7 +415,7 @@
     // 1 instruction is enough to load the immediate.
     if (LIKELY(low_bits == high_bits)) {
       // Value is either 0 or -1: we can just use wzr.
-      ArmOpcode opcode = LIKELY(low_bits == 0) ? kA64Mov2rr : kA64Mvn2rr;
+      A64Opcode opcode = LIKELY(low_bits == 0) ? kA64Mov2rr : kA64Mvn2rr;
       res = NewLIR2(opcode, r_dest.GetReg(), rwzr);
     } else {
       uint16_t uniform_bits, useful_bits;
@@ -466,7 +466,7 @@
 
   if (LIKELY(value == INT64_C(0) || value == INT64_C(-1))) {
     // value is either 0 or -1: we can just use xzr.
-    ArmOpcode opcode = LIKELY(value == 0) ? WIDE(kA64Mov2rr) : WIDE(kA64Mvn2rr);
+    A64Opcode opcode = LIKELY(value == 0) ? WIDE(kA64Mov2rr) : WIDE(kA64Mvn2rr);
     return NewLIR2(opcode, r_dest.GetReg(), rxzr);
   }
 
@@ -486,7 +486,7 @@
 
   if (num_slow_halfwords <= max_num_ops_per_const_load) {
     // We can encode the number using a movz/movn followed by one or more movk.
-    ArmOpcode op;
+    A64Opcode op;
     uint16_t background;
     LIR* res = nullptr;
 
@@ -548,15 +548,11 @@
 }
 
 LIR* Arm64Mir2Lir::OpReg(OpKind op, RegStorage r_dest_src) {
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
   switch (op) {
     case kOpBlx:
       opcode = kA64Blr1x;
       break;
-    // TODO(Arm64): port kThumbBx.
-    // case kOpBx:
-    //   opcode = kThumbBx;
-    //   break;
     default:
       LOG(FATAL) << "Bad opcode " << op;
   }
@@ -564,9 +560,9 @@
 }
 
 LIR* Arm64Mir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r_src2, int shift) {
-  ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
+  A64Opcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
   CHECK_EQ(r_dest_src1.Is64Bit(), r_src2.Is64Bit());
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
 
   switch (op) {
     case kOpCmn:
@@ -621,7 +617,7 @@
     DCHECK_EQ(shift, ENCODE_NO_SHIFT);
     return NewLIR2(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg());
   } else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
-    ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
+    A64EncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
     if (kind == kFmtShift) {
       return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(), shift);
     }
@@ -633,8 +629,8 @@
 
 LIR* Arm64Mir2Lir::OpRegRegExtend(OpKind op, RegStorage r_dest_src1, RegStorage r_src2,
                                   A64RegExtEncodings ext, uint8_t amount) {
-  ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
+  A64Opcode opcode = kA64Brk1d;
 
   switch (op) {
     case kOpCmn:
@@ -655,7 +651,7 @@
 
   DCHECK(!IsPseudoLirOp(opcode));
   if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
-    ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
+    A64EncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
     if (kind == kFmtExtend) {
       return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(),
                      EncodeExtend(ext, amount));
@@ -694,7 +690,7 @@
 
 LIR* Arm64Mir2Lir::OpRegRegRegShift(OpKind op, RegStorage r_dest, RegStorage r_src1,
                                     RegStorage r_src2, int shift) {
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
 
   switch (op) {
     case kOpAdd:
@@ -747,7 +743,7 @@
   // The instructions above belong to two kinds:
   // - 4-operands instructions, where the last operand is a shift/extend immediate,
   // - 3-operands instructions with no shift/extend.
-  ArmOpcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
+  A64Opcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
   CHECK_EQ(r_dest.Is64Bit(), r_src1.Is64Bit());
   CHECK_EQ(r_dest.Is64Bit(), r_src2.Is64Bit());
   if (EncodingMap[opcode].flags & IS_QUAD_OP) {
@@ -762,7 +758,7 @@
 
 LIR* Arm64Mir2Lir::OpRegRegRegExtend(OpKind op, RegStorage r_dest, RegStorage r_src1,
                                      RegStorage r_src2, A64RegExtEncodings ext, uint8_t amount) {
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
 
   switch (op) {
     case kOpAdd:
@@ -775,7 +771,7 @@
       LOG(FATAL) << "Unimplemented opcode: " << op;
       break;
   }
-  ArmOpcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
+  A64Opcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
 
   if (r_dest.Is64Bit()) {
     CHECK(r_src1.Is64Bit());
@@ -810,11 +806,11 @@
   LIR* res;
   bool neg = (value < 0);
   uint64_t abs_value = (neg & !(value == LLONG_MIN)) ? -value : value;
-  ArmOpcode opcode = kA64Brk1d;
-  ArmOpcode alt_opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
+  A64Opcode alt_opcode = kA64Brk1d;
   bool is_logical = false;
   bool is_wide = r_dest.Is64Bit();
-  ArmOpcode wide = (is_wide) ? WIDE(0) : UNWIDE(0);
+  A64Opcode wide = (is_wide) ? WIDE(0) : UNWIDE(0);
   int info = 0;
 
   switch (op) {
@@ -937,9 +933,9 @@
 }
 
 LIR* Arm64Mir2Lir::OpRegImm64(OpKind op, RegStorage r_dest_src1, int64_t value) {
-  ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
-  ArmOpcode opcode = kA64Brk1d;
-  ArmOpcode neg_opcode = kA64Brk1d;
+  A64Opcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
+  A64Opcode opcode = kA64Brk1d;
+  A64Opcode neg_opcode = kA64Brk1d;
   bool shift;
   bool neg = (value < 0);
   uint64_t abs_value = (neg & !(value == LLONG_MIN)) ? -value : value;
@@ -1025,7 +1021,7 @@
                                    int scale, OpSize size) {
   LIR* load;
   int expected_scale = 0;
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
   r_base = Check64BitReg(r_base);
 
   // TODO(Arm64): The sign extension of r_index should be carried out by using an extended
@@ -1040,7 +1036,7 @@
     if (r_dest.IsDouble()) {
       DCHECK(size == k64 || size == kDouble);
       expected_scale = 3;
-      opcode = FWIDE(kA64Ldr4fXxG);
+      opcode = WIDE(kA64Ldr4fXxG);
     } else {
       DCHECK(r_dest.IsSingle());
       DCHECK(size == k32 || size == kSingle);
@@ -1113,7 +1109,7 @@
                                     int scale, OpSize size) {
   LIR* store;
   int expected_scale = 0;
-  ArmOpcode opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
   r_base = Check64BitReg(r_base);
 
   // TODO(Arm64): The sign extension of r_index should be carried out by using an extended
@@ -1128,7 +1124,7 @@
     if (r_src.IsDouble()) {
       DCHECK(size == k64 || size == kDouble);
       expected_scale = 3;
-      opcode = FWIDE(kA64Str4fXxG);
+      opcode = WIDE(kA64Str4fXxG);
     } else {
       DCHECK(r_src.IsSingle());
       DCHECK(size == k32 || size == kSingle);
@@ -1197,8 +1193,8 @@
 LIR* Arm64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest,
                                     OpSize size) {
   LIR* load = NULL;
-  ArmOpcode opcode = kA64Brk1d;
-  ArmOpcode alt_opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
+  A64Opcode alt_opcode = kA64Brk1d;
   int scale = 0;
 
   switch (size) {
@@ -1209,8 +1205,8 @@
       scale = 3;
       if (r_dest.IsFloat()) {
         DCHECK(r_dest.IsDouble());
-        opcode = FWIDE(kA64Ldr3fXD);
-        alt_opcode = FWIDE(kA64Ldur3fXd);
+        opcode = WIDE(kA64Ldr3fXD);
+        alt_opcode = WIDE(kA64Ldur3fXd);
       } else {
         opcode = WIDE(kA64Ldr3rXD);
         alt_opcode = WIDE(kA64Ldur3rXd);
@@ -1294,8 +1290,8 @@
 LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src,
                                      OpSize size) {
   LIR* store = NULL;
-  ArmOpcode opcode = kA64Brk1d;
-  ArmOpcode alt_opcode = kA64Brk1d;
+  A64Opcode opcode = kA64Brk1d;
+  A64Opcode alt_opcode = kA64Brk1d;
   int scale = 0;
 
   switch (size) {
@@ -1306,11 +1302,11 @@
       scale = 3;
       if (r_src.IsFloat()) {
         DCHECK(r_src.IsDouble());
-        opcode = FWIDE(kA64Str3fXD);
-        alt_opcode = FWIDE(kA64Stur3fXd);
+        opcode = WIDE(kA64Str3fXD);
+        alt_opcode = WIDE(kA64Stur3fXd);
       } else {
-        opcode = FWIDE(kA64Str3rXD);
-        alt_opcode = FWIDE(kA64Stur3rXd);
+        opcode = WIDE(kA64Str3rXD);
+        alt_opcode = WIDE(kA64Stur3rXd);
       }
       break;
     case kSingle:     // Intentional fall-through.
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index ee1c467..f305017 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -15,6 +15,7 @@
  */
 
 #include "dex/compiler_internals.h"
+#include "driver/compiler_options.h"
 #include "dex_file-inl.h"
 #include "gc_map.h"
 #include "gc_map_builder.h"
@@ -274,8 +275,8 @@
 }
 
 void Mir2Lir::DumpPromotionMap() {
-  int num_regs = cu_->num_dalvik_registers + mir_graph_->GetNumUsedCompilerTemps();
-  for (int i = 0; i < num_regs; i++) {
+  uint32_t num_regs = mir_graph_->GetNumOfCodeAndTempVRs();
+  for (uint32_t i = 0; i < num_regs; i++) {
     PromotionMap v_reg_map = promotion_map_[i];
     std::string buf;
     if (v_reg_map.fp_location == kLocPhysReg) {
@@ -283,12 +284,13 @@
     }
 
     std::string buf3;
-    if (i < cu_->num_dalvik_registers) {
+    if (i < mir_graph_->GetNumOfCodeVRs()) {
       StringAppendF(&buf3, "%02d", i);
-    } else if (i == mir_graph_->GetMethodSReg()) {
+    } else if (i == mir_graph_->GetNumOfCodeVRs()) {
       buf3 = "Method*";
     } else {
-      StringAppendF(&buf3, "ct%d", i - cu_->num_dalvik_registers);
+      uint32_t diff = i - mir_graph_->GetNumOfCodeVRs();
+      StringAppendF(&buf3, "ct%d", diff);
     }
 
     LOG(INFO) << StringPrintf("V[%s] -> %s%d%s", buf3.c_str(),
@@ -317,11 +319,11 @@
   LOG(INFO) << "Dumping LIR insns for "
             << PrettyMethod(cu_->method_idx, *cu_->dex_file);
   LIR* lir_insn;
-  int insns_size = cu_->code_item->insns_size_in_code_units_;
+  int insns_size = mir_graph_->GetNumDalvikInsns();
 
-  LOG(INFO) << "Regs (excluding ins) : " << cu_->num_regs;
-  LOG(INFO) << "Ins          : " << cu_->num_ins;
-  LOG(INFO) << "Outs         : " << cu_->num_outs;
+  LOG(INFO) << "Regs (excluding ins) : " << mir_graph_->GetNumOfLocalCodeVRs();
+  LOG(INFO) << "Ins          : " << mir_graph_->GetNumOfInVRs();
+  LOG(INFO) << "Outs         : " << mir_graph_->GetNumOfOutVRs();
   LOG(INFO) << "CoreSpills       : " << num_core_spills_;
   LOG(INFO) << "FPSpills       : " << num_fp_spills_;
   LOG(INFO) << "CompilerTemps    : " << mir_graph_->GetNumUsedCompilerTemps();
@@ -401,6 +403,18 @@
   return nullptr;
 }
 
+/* Search the existing constants in the literal pool for an exact class match */
+LIR* Mir2Lir::ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx) {
+  while (data_target) {
+    if (static_cast<uint32_t>(data_target->operands[0]) == type_idx &&
+        UnwrapPointer(data_target->operands[1]) == &dex_file) {
+      return data_target;
+    }
+    data_target = data_target->next;
+  }
+  return nullptr;
+}
+
 /*
  * The following are building blocks to insert constants into the pool or
  * instruction streams.
@@ -458,20 +472,15 @@
     Push32(code_buffer_, data_lir->operands[0]);
     data_lir = NEXT_LIR(data_lir);
   }
+  // TODO: patches_.reserve() as needed.
   // Push code and method literals, record offsets for the compiler to patch.
   data_lir = code_literal_list_;
   while (data_lir != NULL) {
     uint32_t target_method_idx = data_lir->operands[0];
     const DexFile* target_dex_file =
         reinterpret_cast<const DexFile*>(UnwrapPointer(data_lir->operands[1]));
-    cu_->compiler_driver->AddCodePatch(cu_->dex_file,
-                                       cu_->class_def_idx,
-                                       cu_->method_idx,
-                                       cu_->invoke_type,
-                                       target_method_idx,
-                                       target_dex_file,
-                                       static_cast<InvokeType>(data_lir->operands[2]),
-                                       code_buffer_.size());
+    patches_.push_back(LinkerPatch::CodePatch(code_buffer_.size(),
+                                              target_dex_file, target_method_idx));
     const DexFile::MethodId& target_method_id = target_dex_file->GetMethodId(target_method_idx);
     // unique value based on target to ensure code deduplication works
     PushPointer(code_buffer_, &target_method_id, cu_->target64);
@@ -482,14 +491,8 @@
     uint32_t target_method_idx = data_lir->operands[0];
     const DexFile* target_dex_file =
         reinterpret_cast<const DexFile*>(UnwrapPointer(data_lir->operands[1]));
-    cu_->compiler_driver->AddMethodPatch(cu_->dex_file,
-                                         cu_->class_def_idx,
-                                         cu_->method_idx,
-                                         cu_->invoke_type,
-                                         target_method_idx,
-                                         target_dex_file,
-                                         static_cast<InvokeType>(data_lir->operands[2]),
-                                         code_buffer_.size());
+    patches_.push_back(LinkerPatch::MethodPatch(code_buffer_.size(),
+                                                target_dex_file, target_method_idx));
     const DexFile::MethodId& target_method_id = target_dex_file->GetMethodId(target_method_idx);
     // unique value based on target to ensure code deduplication works
     PushPointer(code_buffer_, &target_method_id, cu_->target64);
@@ -498,13 +501,12 @@
   // Push class literals.
   data_lir = class_literal_list_;
   while (data_lir != NULL) {
-    uint32_t target_method_idx = data_lir->operands[0];
-    cu_->compiler_driver->AddClassPatch(cu_->dex_file,
-                                        cu_->class_def_idx,
-                                        cu_->method_idx,
-                                        target_method_idx,
-                                        code_buffer_.size());
-    const DexFile::TypeId& target_method_id = cu_->dex_file->GetTypeId(target_method_idx);
+    uint32_t target_type_idx = data_lir->operands[0];
+    const DexFile* class_dex_file =
+      reinterpret_cast<const DexFile*>(UnwrapPointer(data_lir->operands[1]));
+    patches_.push_back(LinkerPatch::TypePatch(code_buffer_.size(),
+                                              class_dex_file, target_type_idx));
+    const DexFile::TypeId& target_method_id = class_dex_file->GetTypeId(target_type_idx);
     // unique value based on target to ensure code deduplication works
     PushPointer(code_buffer_, &target_method_id, cu_->target64);
     data_lir = NEXT_LIR(data_lir);
@@ -513,10 +515,7 @@
 
 /* Write the switch tables to the output stream */
 void Mir2Lir::InstallSwitchTables() {
-  GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
-  while (true) {
-    Mir2Lir::SwitchTable* tab_rec = iterator.Next();
-    if (tab_rec == NULL) break;
+  for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
     AlignBuffer(code_buffer_, tab_rec->offset);
     /*
      * For Arm, our reference point is the address of the bx
@@ -573,10 +572,7 @@
 
 /* Write the fill array dta to the output stream */
 void Mir2Lir::InstallFillArrayData() {
-  GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
-  while (true) {
-    Mir2Lir::FillArrayData *tab_rec = iterator.Next();
-    if (tab_rec == NULL) break;
+  for (Mir2Lir::FillArrayData* tab_rec : fill_array_data_) {
     AlignBuffer(code_buffer_, tab_rec->offset);
     for (int i = 0; i < (tab_rec->size + 1) / 2; i++) {
       code_buffer_.push_back(tab_rec->table[i] & 0xFF);
@@ -640,15 +636,19 @@
 
 
 void Mir2Lir::CreateMappingTables() {
+  bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols();
+
   uint32_t pc2dex_data_size = 0u;
   uint32_t pc2dex_entries = 0u;
   uint32_t pc2dex_offset = 0u;
   uint32_t pc2dex_dalvik_offset = 0u;
+  uint32_t pc2dex_src_entries = 0u;
   uint32_t dex2pc_data_size = 0u;
   uint32_t dex2pc_entries = 0u;
   uint32_t dex2pc_offset = 0u;
   uint32_t dex2pc_dalvik_offset = 0u;
   for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+    pc2dex_src_entries++;
     if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
       pc2dex_entries += 1;
       DCHECK(pc2dex_offset <= tgt_lir->offset);
@@ -669,6 +669,10 @@
     }
   }
 
+  if (generate_src_map) {
+    src_mapping_table_.reserve(pc2dex_src_entries);
+  }
+
   uint32_t total_entries = pc2dex_entries + dex2pc_entries;
   uint32_t hdr_data_size = UnsignedLeb128Size(total_entries) + UnsignedLeb128Size(pc2dex_entries);
   uint32_t data_size = hdr_data_size + pc2dex_data_size + dex2pc_data_size;
@@ -684,6 +688,10 @@
   dex2pc_offset = 0u;
   dex2pc_dalvik_offset = 0u;
   for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+    if (generate_src_map && !tgt_lir->flags.is_nop) {
+      src_mapping_table_.push_back(SrcMapElem({tgt_lir->offset,
+              static_cast<int32_t>(tgt_lir->dalvik_offset)}));
+    }
     if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
       DCHECK(pc2dex_offset <= tgt_lir->offset);
       write_pos = EncodeUnsignedLeb128(write_pos, tgt_lir->offset - pc2dex_offset);
@@ -772,10 +780,7 @@
 }
 
 int Mir2Lir::AssignSwitchTablesOffset(CodeOffset offset) {
-  GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
-  while (true) {
-    Mir2Lir::SwitchTable* tab_rec = iterator.Next();
-    if (tab_rec == NULL) break;
+  for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
     tab_rec->offset = offset;
     if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
       offset += tab_rec->table[1] * (sizeof(int) * 2);
@@ -789,15 +794,12 @@
 }
 
 int Mir2Lir::AssignFillArrayDataOffset(CodeOffset offset) {
-  GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
-  while (true) {
-    Mir2Lir::FillArrayData *tab_rec = iterator.Next();
-    if (tab_rec == NULL) break;
+  for (Mir2Lir::FillArrayData* tab_rec : fill_array_data_) {
     tab_rec->offset = offset;
     offset += tab_rec->size;
     // word align
     offset = RoundUp(offset, 4);
-    }
+  }
   return offset;
 }
 
@@ -849,10 +851,7 @@
 }
 
 void Mir2Lir::ProcessSwitchTables() {
-  GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
-  while (true) {
-    Mir2Lir::SwitchTable *tab_rec = iterator.Next();
-    if (tab_rec == NULL) break;
+  for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
     if (tab_rec->table[0] == Instruction::kPackedSwitchSignature) {
       MarkPackedCaseLabels(tab_rec);
     } else if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
@@ -977,21 +976,22 @@
       first_fixup_(NULL),
       cu_(cu),
       mir_graph_(mir_graph),
-      switch_tables_(arena, 4, kGrowableArraySwitchTables),
-      fill_array_data_(arena, 4, kGrowableArrayFillArrayData),
-      tempreg_info_(arena, 20, kGrowableArrayMisc),
-      reginfo_map_(arena, RegStorage::kMaxRegs, kGrowableArrayMisc),
-      pointer_storage_(arena, 128, kGrowableArrayMisc),
+      switch_tables_(arena->Adapter(kArenaAllocSwitchTable)),
+      fill_array_data_(arena->Adapter(kArenaAllocFillArrayData)),
+      tempreg_info_(arena->Adapter()),
+      reginfo_map_(arena->Adapter()),
+      pointer_storage_(arena->Adapter()),
       data_offset_(0),
       total_size_(0),
       block_label_list_(NULL),
       promotion_map_(NULL),
       current_dalvik_offset_(0),
       estimated_native_code_size_(0),
-      reg_pool_(NULL),
+      reg_pool_(nullptr),
       live_sreg_(0),
       core_vmap_table_(mir_graph->GetArena()->Adapter()),
       fp_vmap_table_(mir_graph->GetArena()->Adapter()),
+      patches_(mir_graph->GetArena()->Adapter()),
       num_core_spills_(0),
       num_fp_spills_(0),
       frame_size_(0),
@@ -999,9 +999,15 @@
       fp_spill_mask_(0),
       first_lir_insn_(NULL),
       last_lir_insn_(NULL),
-      slow_paths_(arena, 32, kGrowableArraySlowPaths),
+      slow_paths_(arena->Adapter(kArenaAllocSlowPaths)),
       mem_ref_type_(ResourceMask::kHeapRef),
       mask_cache_(arena) {
+  switch_tables_.reserve(4);
+  fill_array_data_.reserve(4);
+  tempreg_info_.reserve(20);
+  reginfo_map_.reserve(RegStorage::kMaxRegs);
+  pointer_storage_.reserve(128);
+  slow_paths_.reserve(32);
   // Reserve pointer id 0 for NULL.
   size_t null_idx = WrapPointer(NULL);
   DCHECK_EQ(null_idx, 0U);
@@ -1077,11 +1083,17 @@
     vmap_encoder.PushBackUnsigned(0u);  // Size is 0.
   }
 
-  std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnCallFrameInformation());
+  // Sort patches by literal offset for better deduplication.
+  std::sort(patches_.begin(), patches_.end(), [](const LinkerPatch& lhs, const LinkerPatch& rhs) {
+    return lhs.LiteralOffset() < rhs.LiteralOffset();
+  });
+
+  std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnFrameDescriptionEntry());
   CompiledMethod* result =
       new CompiledMethod(cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
-                         core_spill_mask_, fp_spill_mask_, encoded_mapping_table_,
-                         vmap_encoder.GetData(), native_gc_map_, cfi_info.get());
+                         core_spill_mask_, fp_spill_mask_, &src_mapping_table_, encoded_mapping_table_,
+                         vmap_encoder.GetData(), native_gc_map_, cfi_info.get(),
+                         ArrayRef<LinkerPatch>(patches_));
   return result;
 }
 
@@ -1096,7 +1108,8 @@
   // By default assume that the Mir2Lir will need one slot for each temporary.
   // If the backend can better determine temps that have non-overlapping ranges and
   // temps that do not need spilled, it can actually provide a small region.
-  return (mir_graph_->GetNumUsedCompilerTemps() * sizeof(uint32_t));
+  mir_graph_->CommitCompilerTemps();
+  return mir_graph_->GetNumBytesForSpecialTemps() + mir_graph_->GetMaximumBytesForNonSpecialTemps();
 }
 
 int Mir2Lir::ComputeFrameSize() {
@@ -1104,7 +1117,8 @@
   uint32_t size = num_core_spills_ * GetBytesPerGprSpillLocation(cu_->instruction_set)
                   + num_fp_spills_ * GetBytesPerFprSpillLocation(cu_->instruction_set)
                   + sizeof(uint32_t)  // Filler.
-                  + (cu_->num_regs + cu_->num_outs) * sizeof(uint32_t)
+                  + mir_graph_->GetNumOfLocalCodeVRs()  * sizeof(uint32_t)
+                  + mir_graph_->GetNumOfOutVRs() * sizeof(uint32_t)
                   + GetNumBytesForCompilerTempSpillRegion();
   /* Align and set */
   return RoundUp(size, kStackAlignment);
@@ -1192,7 +1206,8 @@
 }
 
 void Mir2Lir::AddSlowPath(LIRSlowPath* slowpath) {
-  slow_paths_.Insert(slowpath);
+  slow_paths_.push_back(slowpath);
+  ResetDefTracking();
 }
 
 void Mir2Lir::LoadCodeAddress(const MethodReference& target_method, InvokeType type,
@@ -1229,18 +1244,20 @@
   DCHECK_NE(cu_->instruction_set, kMips) << reinterpret_cast<void*>(data_target);
 }
 
-void Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) {
+void Mir2Lir::LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+                            SpecialTargetRegister symbolic_reg) {
   // Use the literal pool and a PC-relative load from a data word.
-  LIR* data_target = ScanLiteralPool(class_literal_list_, type_idx, 0);
+  LIR* data_target = ScanLiteralPoolClass(class_literal_list_, dex_file, type_idx);
   if (data_target == nullptr) {
     data_target = AddWordData(&class_literal_list_, type_idx);
+    data_target->operands[1] = WrapPointer(const_cast<DexFile*>(&dex_file));
   }
   // Loads a Class pointer, which is a reference as it lives in the heap.
   LIR* load_pc_rel = OpPcRelLoad(TargetReg(symbolic_reg, kRef), data_target);
   AppendLIR(load_pc_rel);
 }
 
-std::vector<uint8_t>* Mir2Lir::ReturnCallFrameInformation() {
+std::vector<uint8_t>* Mir2Lir::ReturnFrameDescriptionEntry() {
   // Default case is to do nothing.
   return nullptr;
 }
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 34ed0f4..2523380 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -25,8 +25,10 @@
 #include "thread.h"
 #include "thread-inl.h"
 #include "dex/mir_graph.h"
+#include "dex/quick/mir_to_lir.h"
 #include "dex_instruction.h"
 #include "dex_instruction-inl.h"
+#include "driver/dex_compilation_unit.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 
@@ -561,9 +563,25 @@
       break;
     default:
       LOG(FATAL) << "Unexpected inline op: " << method.opcode;
+      break;
   }
   if (result) {
     invoke->optimization_flags |= MIR_INLINED;
+    // If the invoke has not been eliminated yet, check now whether we should do it.
+    // This is done so that dataflow analysis does not get tripped up seeing nop invoke.
+    if (static_cast<int>(invoke->dalvikInsn.opcode) != kMirOpNop) {
+      bool is_static = invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
+          invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE;
+      if (is_static || (invoke->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) {
+        // No null object register involved here so we can eliminate the invoke.
+        invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+      } else {
+        // Invoke was kept around because null check needed to be done.
+        invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNullCheck);
+        // For invokes, the object register is in vC. For null check mir, it is in vA.
+        invoke->dalvikInsn.vA = invoke->dalvikInsn.vC;
+      }
+    }
     if (move_result != nullptr) {
       move_result->optimization_flags |= MIR_INLINED;
       move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index f6c77fc..9f7a881 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -361,7 +361,7 @@
                                    &direct_type_ptr, &is_finalizable)) {
       // The fast path.
       if (!use_direct_type_ptr) {
-        LoadClassType(type_idx, kArg0);
+        LoadClassType(*dex_file, type_idx, kArg0);
         CallRuntimeHelperRegMethodRegLocation(kQuickAllocArrayResolved, TargetReg(kArg0, kNotWide),
                                               rl_src, true);
       } else {
@@ -524,11 +524,9 @@
   const RegStorage r_base_;
 };
 
-void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double,
-                      bool is_object) {
+void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, OpSize size) {
   const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
   cu_->compiler_driver->ProcessedStaticField(field_info.FastPut(), field_info.IsReferrersClass());
-  OpSize store_size = LoadStoreOpSize(is_long_or_double, is_object);
   if (!SLOW_FIELD_PATH && field_info.FastPut()) {
     DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
     RegStorage r_base;
@@ -587,37 +585,59 @@
       FreeTemp(r_method);
     }
     // rBase now holds static storage base
-    RegisterClass reg_class = RegClassForFieldLoadStore(store_size, field_info.IsVolatile());
-    if (is_long_or_double) {
+    RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
+    if (IsWide(size)) {
       rl_src = LoadValueWide(rl_src, reg_class);
     } else {
       rl_src = LoadValue(rl_src, reg_class);
     }
-    if (is_object) {
+    if (IsRef(size)) {
       StoreRefDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg,
                    field_info.IsVolatile() ? kVolatile : kNotVolatile);
     } else {
-      StoreBaseDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, store_size,
+      StoreBaseDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, size,
                     field_info.IsVolatile() ? kVolatile : kNotVolatile);
     }
-    if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) {
+    if (IsRef(size) && !mir_graph_->IsConstantNullRef(rl_src)) {
       MarkGCCard(rl_src.reg, r_base);
     }
     FreeTemp(r_base);
   } else {
     FlushAllRegs();  // Everything to home locations
-    QuickEntrypointEnum target =
-        is_long_or_double ? kQuickSet64Static
-            : (is_object ? kQuickSetObjStatic : kQuickSet32Static);
+    QuickEntrypointEnum target;
+    switch (size) {
+      case kReference:
+        target = kQuickSetObjStatic;
+        break;
+      case k64:
+      case kDouble:
+        target = kQuickSet64Static;
+        break;
+      case k32:
+      case kSingle:
+        target = kQuickSet32Static;
+        break;
+      case kSignedHalf:
+      case kUnsignedHalf:
+        target = kQuickSet16Static;
+        break;
+      case kSignedByte:
+      case kUnsignedByte:
+        target = kQuickSet8Static;
+        break;
+      case kWord:  // Intentional fallthrough.
+      default:
+        LOG(FATAL) << "Can't determine entrypoint for: " << size;
+        target = kQuickSet32Static;
+    }
     CallRuntimeHelperImmRegLocation(target, field_info.FieldIndex(), rl_src, true);
   }
 }
 
-void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest,
-                      bool is_long_or_double, bool is_object) {
+void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, OpSize size, Primitive::Type type) {
   const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
   cu_->compiler_driver->ProcessedStaticField(field_info.FastGet(), field_info.IsReferrersClass());
-  OpSize load_size = LoadStoreOpSize(is_long_or_double, is_object);
+
   if (!SLOW_FIELD_PATH && field_info.FastGet()) {
     DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
     RegStorage r_base;
@@ -668,33 +688,62 @@
       FreeTemp(r_method);
     }
     // r_base now holds static storage base
-    RegisterClass reg_class = RegClassForFieldLoadStore(load_size, field_info.IsVolatile());
+    RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
     RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
 
     int field_offset = field_info.FieldOffset().Int32Value();
-    if (is_object) {
+    if (IsRef(size)) {
+      // TODO: DCHECK?
       LoadRefDisp(r_base, field_offset, rl_result.reg, field_info.IsVolatile() ? kVolatile :
           kNotVolatile);
     } else {
-      LoadBaseDisp(r_base, field_offset, rl_result.reg, load_size, field_info.IsVolatile() ?
+      LoadBaseDisp(r_base, field_offset, rl_result.reg, size, field_info.IsVolatile() ?
           kVolatile : kNotVolatile);
     }
     FreeTemp(r_base);
 
-    if (is_long_or_double) {
+    if (IsWide(size)) {
       StoreValueWide(rl_dest, rl_result);
     } else {
       StoreValue(rl_dest, rl_result);
     }
   } else {
+    DCHECK(SizeMatchesTypeForEntrypoint(size, type));
     FlushAllRegs();  // Everything to home locations
-    QuickEntrypointEnum target =
-        is_long_or_double ? kQuickGet64Static
-            : (is_object ? kQuickGetObjStatic : kQuickGet32Static);
+    QuickEntrypointEnum target;
+    switch (type) {
+      case Primitive::kPrimNot:
+        target = kQuickGetObjStatic;
+        break;
+      case Primitive::kPrimLong:
+      case Primitive::kPrimDouble:
+        target = kQuickGet64Static;
+        break;
+      case Primitive::kPrimInt:
+      case Primitive::kPrimFloat:
+        target = kQuickGet32Static;
+        break;
+      case Primitive::kPrimShort:
+        target = kQuickGetShortStatic;
+        break;
+      case Primitive::kPrimChar:
+        target = kQuickGetCharStatic;
+        break;
+      case Primitive::kPrimByte:
+        target = kQuickGetByteStatic;
+        break;
+      case Primitive::kPrimBoolean:
+        target = kQuickGetBooleanStatic;
+        break;
+      case Primitive::kPrimVoid:  // Intentional fallthrough.
+      default:
+        LOG(FATAL) << "Can't determine entrypoint for: " << type;
+        target = kQuickGet32Static;
+    }
     CallRuntimeHelperImm(target, field_info.FieldIndex(), true);
 
     // FIXME: pGetXXStatic always return an int or int64 regardless of rl_dest.fp.
-    if (is_long_or_double) {
+    if (IsWide(size)) {
       RegLocation rl_result = GetReturnWide(kCoreReg);
       StoreValueWide(rl_dest, rl_result);
     } else {
@@ -708,21 +757,18 @@
 void Mir2Lir::HandleSlowPaths() {
   // We should check slow_paths_.Size() every time, because a new slow path
   // may be created during slowpath->Compile().
-  for (size_t i = 0; i < slow_paths_.Size(); ++i) {
-    LIRSlowPath* slowpath = slow_paths_.Get(i);
+  for (LIRSlowPath* slowpath : slow_paths_) {
     slowpath->Compile();
   }
-  slow_paths_.Reset();
+  slow_paths_.clear();
 }
 
-void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size,
-                      RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double,
-                      bool is_object) {
+void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, Primitive::Type type,
+                      RegLocation rl_dest, RegLocation rl_obj) {
   const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
   cu_->compiler_driver->ProcessedInstanceField(field_info.FastGet());
-  OpSize load_size = LoadStoreOpSize(is_long_or_double, is_object);
   if (!SLOW_FIELD_PATH && field_info.FastGet()) {
-    RegisterClass reg_class = RegClassForFieldLoadStore(load_size, field_info.IsVolatile());
+    RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
     // A load of the class will lead to an iget with offset 0.
     DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
     rl_obj = LoadValue(rl_obj, kRefReg);
@@ -730,29 +776,57 @@
     RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
     int field_offset = field_info.FieldOffset().Int32Value();
     LIR* load_lir;
-    if (is_object) {
+    if (IsRef(size)) {
       load_lir = LoadRefDisp(rl_obj.reg, field_offset, rl_result.reg, field_info.IsVolatile() ?
           kVolatile : kNotVolatile);
     } else {
-      load_lir = LoadBaseDisp(rl_obj.reg, field_offset, rl_result.reg, load_size,
+      load_lir = LoadBaseDisp(rl_obj.reg, field_offset, rl_result.reg, size,
                               field_info.IsVolatile() ? kVolatile : kNotVolatile);
     }
     MarkPossibleNullPointerExceptionAfter(opt_flags, load_lir);
-    if (is_long_or_double) {
+    if (IsWide(size)) {
       StoreValueWide(rl_dest, rl_result);
     } else {
       StoreValue(rl_dest, rl_result);
     }
   } else {
-    QuickEntrypointEnum target =
-        is_long_or_double ? kQuickGet64Instance
-            : (is_object ? kQuickGetObjInstance : kQuickGet32Instance);
+    DCHECK(SizeMatchesTypeForEntrypoint(size, type));
+    QuickEntrypointEnum target;
+    switch (type) {
+      case Primitive::kPrimNot:
+        target = kQuickGetObjInstance;
+        break;
+      case Primitive::kPrimLong:
+      case Primitive::kPrimDouble:
+        target = kQuickGet64Instance;
+        break;
+      case Primitive::kPrimFloat:
+      case Primitive::kPrimInt:
+        target = kQuickGet32Instance;
+        break;
+      case Primitive::kPrimShort:
+        target = kQuickGetShortInstance;
+        break;
+      case Primitive::kPrimChar:
+        target = kQuickGetCharInstance;
+        break;
+      case Primitive::kPrimByte:
+        target = kQuickGetByteInstance;
+        break;
+      case Primitive::kPrimBoolean:
+        target = kQuickGetBooleanInstance;
+        break;
+      case Primitive::kPrimVoid:  // Intentional fallthrough.
+      default:
+        LOG(FATAL) << "Can't determine entrypoint for: " << type;
+        target = kQuickGet32Instance;
+    }
     // Second argument of pGetXXInstance is always a reference.
     DCHECK_EQ(static_cast<unsigned int>(rl_obj.wide), 0U);
     CallRuntimeHelperImmRegLocation(target, field_info.FieldIndex(), rl_obj, true);
 
     // FIXME: pGetXXInstance always return an int or int64 regardless of rl_dest.fp.
-    if (is_long_or_double) {
+    if (IsWide(size)) {
       RegLocation rl_result = GetReturnWide(kCoreReg);
       StoreValueWide(rl_dest, rl_result);
     } else {
@@ -763,18 +837,16 @@
 }
 
 void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size,
-                      RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double,
-                      bool is_object) {
+                      RegLocation rl_src, RegLocation rl_obj) {
   const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
   cu_->compiler_driver->ProcessedInstanceField(field_info.FastPut());
-  OpSize store_size = LoadStoreOpSize(is_long_or_double, is_object);
   if (!SLOW_FIELD_PATH && field_info.FastPut()) {
-    RegisterClass reg_class = RegClassForFieldLoadStore(store_size, field_info.IsVolatile());
+    RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
     // Dex code never writes to the class field.
     DCHECK_GE(static_cast<uint32_t>(field_info.FieldOffset().Int32Value()),
               sizeof(mirror::HeapReference<mirror::Class>));
     rl_obj = LoadValue(rl_obj, kRefReg);
-    if (is_long_or_double) {
+    if (IsWide(size)) {
       rl_src = LoadValueWide(rl_src, reg_class);
     } else {
       rl_src = LoadValue(rl_src, reg_class);
@@ -782,21 +854,44 @@
     GenNullCheck(rl_obj.reg, opt_flags);
     int field_offset = field_info.FieldOffset().Int32Value();
     LIR* store;
-    if (is_object) {
+    if (IsRef(size)) {
       store = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
           kVolatile : kNotVolatile);
     } else {
-      store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, store_size,
+      store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
                             field_info.IsVolatile() ? kVolatile : kNotVolatile);
     }
     MarkPossibleNullPointerExceptionAfter(opt_flags, store);
-    if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) {
+    if (IsRef(size) && !mir_graph_->IsConstantNullRef(rl_src)) {
       MarkGCCard(rl_src.reg, rl_obj.reg);
     }
   } else {
-    QuickEntrypointEnum target =
-        is_long_or_double ? kQuickSet64Instance
-            : (is_object ? kQuickSetObjInstance : kQuickSet32Instance);
+    QuickEntrypointEnum target;
+    switch (size) {
+      case kReference:
+        target = kQuickSetObjInstance;
+        break;
+      case k64:
+      case kDouble:
+        target = kQuickSet64Instance;
+        break;
+      case k32:
+      case kSingle:
+        target = kQuickSet32Instance;
+        break;
+      case kSignedHalf:
+      case kUnsignedHalf:
+        target = kQuickSet16Instance;
+        break;
+      case kSignedByte:
+      case kUnsignedByte:
+        target = kQuickSet8Instance;
+        break;
+      case kWord:  // Intentional fallthrough.
+      default:
+        LOG(FATAL) << "Can't determine entrypoint for: " << size;
+        target = kQuickSet32Instance;
+    }
     CallRuntimeHelperImmRegLocationRegLocation(target, field_info.FieldIndex(), rl_obj, rl_src,
                                                true);
   }
@@ -961,7 +1056,7 @@
                                    !is_finalizable) {
       // The fast path.
       if (!use_direct_type_ptr) {
-        LoadClassType(type_idx, kArg0);
+        LoadClassType(*dex_file, type_idx, kArg0);
         if (!is_type_initialized) {
           CallRuntimeHelperRegMethod(kQuickAllocObjectResolved, TargetReg(kArg0, kRef), true);
         } else {
@@ -2009,7 +2104,7 @@
 }
 
 void Mir2Lir::GenSmallPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   const uint16_t entries = table[1];
   // Chained cmp-and-branch.
   const int32_t* as_int32 = reinterpret_cast<const int32_t*>(&table[2]);
@@ -2052,7 +2147,7 @@
 }
 
 void Mir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpSparseSwitchTable(table);
   }
@@ -2067,7 +2162,7 @@
 }
 
 void Mir2Lir::GenSmallSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   const uint16_t entries = table[1];
   // Chained cmp-and-branch.
   const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
@@ -2082,7 +2177,7 @@
 }
 
 void Mir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpSparseSwitchTable(table);
   }
@@ -2096,4 +2191,28 @@
   }
 }
 
+bool Mir2Lir::SizeMatchesTypeForEntrypoint(OpSize size, Primitive::Type type) {
+  switch (size) {
+    case kReference:
+      return type == Primitive::kPrimNot;
+    case k64:
+    case kDouble:
+      return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
+    case k32:
+    case kSingle:
+      return type == Primitive::kPrimInt || type == Primitive::kPrimFloat;
+    case kSignedHalf:
+      return type == Primitive::kPrimShort;
+    case kUnsignedHalf:
+      return type == Primitive::kPrimChar;
+    case kSignedByte:
+      return type == Primitive::kPrimByte;
+    case kUnsignedByte:
+      return type == Primitive::kPrimBoolean;
+    case kWord:  // Intentional fallthrough.
+    default:
+      return false;  // There are no sane types with this op size.
+  }
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 7958886..67a75cb 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "arm/codegen_arm.h"
 #include "dex/compiler_ir.h"
 #include "dex/frontend.h"
 #include "dex/quick/dex_file_method_inliner.h"
@@ -25,11 +26,9 @@
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "mirror/object_array-inl.h"
-#include "mirror/reference-inl.h"
 #include "mirror/string.h"
 #include "mir_to_lir-inl.h"
 #include "scoped_thread_state_change.h"
-#include "x86/codegen_x86.h"
 
 namespace art {
 
@@ -383,11 +382,11 @@
     StoreRefDisp(TargetPtrReg(kSp), 0, rl_src.reg, kNotVolatile);
   }
 
-  if (cu_->num_ins == 0) {
+  if (mir_graph_->GetNumOfInVRs() == 0) {
     return;
   }
 
-  int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+  int start_vreg = mir_graph_->GetFirstInVR();
   /*
    * Copy incoming arguments to their proper home locations.
    * NOTE: an older version of dx had an issue in which
@@ -401,7 +400,7 @@
    * half to memory as well.
    */
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-  for (int i = 0; i < cu_->num_ins; i++) {
+  for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
     PromotionMap* v_map = &promotion_map_[start_vreg + i];
     RegStorage reg = GetArgMappingToPhysicalReg(i);
 
@@ -495,15 +494,15 @@
                           uint32_t unused,
                           uintptr_t direct_code, uintptr_t direct_method,
                           InvokeType type) {
+  DCHECK(cu->instruction_set != kX86 && cu->instruction_set != kX86_64 &&
+         cu->instruction_set != kThumb2 && cu->instruction_set != kArm);
   Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
   if (direct_code != 0 && direct_method != 0) {
     switch (state) {
     case 0:  // Get the current Method* [sets kArg0]
       if (direct_code != static_cast<uintptr_t>(-1)) {
-        if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
-          cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
-        }
-      } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+        cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+      } else {
         cg->LoadCodeAddress(target_method, type, kInvokeTgt);
       }
       if (direct_method != static_cast<uintptr_t>(-1)) {
@@ -531,7 +530,7 @@
       if (direct_code != 0) {
         if (direct_code != static_cast<uintptr_t>(-1)) {
           cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
-        } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+        } else {
           CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
           cg->LoadCodeAddress(target_method, type, kInvokeTgt);
         }
@@ -549,7 +548,7 @@
         if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, &arg0_ref, cu, cg)) {
           break;                                    // kInvokeTgt := arg0_ref->entrypoint
         }
-      } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+      } else {
         break;
       }
       // Intentional fallthrough for x86
@@ -935,9 +934,6 @@
     }
   }
 
-  // Logic below assumes that Method pointer is at offset zero from SP.
-  DCHECK_EQ(VRegOffset(static_cast<int>(kVRegMethodPtrBaseReg)), 0);
-
   // The first 3 arguments are passed via registers.
   // TODO: For 64-bit, instead of hardcoding 4 for Method* size, we should either
   // get size of uintptr_t or size of object reference according to model being used.
@@ -1112,9 +1108,12 @@
 RegLocation Mir2Lir::InlineTarget(CallInfo* info) {
   RegLocation res;
   if (info->result.location == kLocInvalid) {
-    res = GetReturn(LocToRegClass(info->result));
+    // If result is unused, return a sink target based on type of invoke target.
+    res = GetReturn(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   } else {
     res = info->result;
+    DCHECK_EQ(LocToRegClass(res),
+              ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   }
   return res;
 }
@@ -1122,9 +1121,12 @@
 RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) {
   RegLocation res;
   if (info->result.location == kLocInvalid) {
-    res = GetReturnWide(kCoreReg);
+    // If result is unused, return a sink target based on type of invoke target.
+    res = GetReturnWide(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   } else {
     res = info->result;
+    DCHECK_EQ(LocToRegClass(res),
+              ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   }
   return res;
 }
@@ -1135,68 +1137,37 @@
     return false;
   }
 
-  // the refrence class is stored in the image dex file which might not be the same as the cu's
-  // dex file. Query the reference class for the image dex file then reset to starting dex file
-  // in after loading class type.
-  uint16_t type_idx = 0;
-  const DexFile* ref_dex_file = nullptr;
-  {
-    ScopedObjectAccess soa(Thread::Current());
-    type_idx = mirror::Reference::GetJavaLangRefReference()->GetDexTypeIndex();
-    ref_dex_file = mirror::Reference::GetJavaLangRefReference()->GetDexCache()->GetDexFile();
-  }
-  CHECK(LIKELY(ref_dex_file != nullptr));
-
-  // address is either static within the image file, or needs to be patched up after compilation.
-  bool unused_type_initialized;
   bool use_direct_type_ptr;
   uintptr_t direct_type_ptr;
-  bool is_finalizable;
-  const DexFile* old_dex = cu_->dex_file;
-  cu_->dex_file = ref_dex_file;
+  ClassReference ref;
+  if (!cu_->compiler_driver->CanEmbedReferenceTypeInCode(&ref,
+        &use_direct_type_ptr, &direct_type_ptr)) {
+    return false;
+  }
+
   RegStorage reg_class = TargetReg(kArg1, kRef);
   Clobber(reg_class);
   LockTemp(reg_class);
-  if (!cu_->compiler_driver->CanEmbedTypeInCode(*ref_dex_file, type_idx, &unused_type_initialized,
-                                                &use_direct_type_ptr, &direct_type_ptr,
-                                                &is_finalizable) || is_finalizable) {
-    cu_->dex_file = old_dex;
-    // address is not known and post-compile patch is not possible, cannot insert intrinsic.
-    return false;
-  }
   if (use_direct_type_ptr) {
     LoadConstant(reg_class, direct_type_ptr);
-  } else if (cu_->dex_file == old_dex) {
-    // TODO: Bug 16656190 If cu_->dex_file != old_dex the patching could retrieve the wrong class
-    // since the load class is indexed only by the type_idx. We should include which dex file a
-    // class is from in the LoadClassType LIR.
-    LoadClassType(type_idx, kArg1);
   } else {
-    cu_->dex_file = old_dex;
-    return false;
+    uint16_t type_idx = ref.first->GetClassDef(ref.second).class_idx_;
+    LoadClassType(*ref.first, type_idx, kArg1);
   }
-  cu_->dex_file = old_dex;
 
-  // get the offset for flags in reference class.
-  uint32_t slow_path_flag_offset = 0;
-  uint32_t disable_flag_offset = 0;
-  {
-    ScopedObjectAccess soa(Thread::Current());
-    mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference();
-    slow_path_flag_offset = reference_class->GetSlowPathFlagOffset().Uint32Value();
-    disable_flag_offset = reference_class->GetDisableIntrinsicFlagOffset().Uint32Value();
-  }
+  uint32_t slow_path_flag_offset = cu_->compiler_driver->GetReferenceSlowFlagOffset();
+  uint32_t disable_flag_offset = cu_->compiler_driver->GetReferenceDisableFlagOffset();
   CHECK(slow_path_flag_offset && disable_flag_offset &&
         (slow_path_flag_offset != disable_flag_offset));
 
   // intrinsic logic start.
   RegLocation rl_obj = info->args[0];
-  rl_obj = LoadValue(rl_obj);
+  rl_obj = LoadValue(rl_obj, kRefReg);
 
   RegStorage reg_slow_path = AllocTemp();
   RegStorage reg_disabled = AllocTemp();
-  Load32Disp(reg_class, slow_path_flag_offset, reg_slow_path);
-  Load32Disp(reg_class, disable_flag_offset, reg_disabled);
+  Load8Disp(reg_class, slow_path_flag_offset, reg_slow_path);
+  Load8Disp(reg_class, disable_flag_offset, reg_disabled);
   FreeTemp(reg_class);
   LIR* or_inst = OpRegRegReg(kOpOr, reg_slow_path, reg_slow_path, reg_disabled);
   FreeTemp(reg_disabled);
@@ -1330,10 +1301,10 @@
     return false;
   }
   RegLocation rl_src_i = info->args[0];
-  RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
-  RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);  // result reg
+  RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
+  RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info);  // result reg
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
-  if (size == k64) {
+  if (IsWide(size)) {
     if (cu_->instruction_set == kArm64 || cu_->instruction_set == kX86_64) {
       OpRegReg(kOpRev, rl_result.reg, rl_i.reg);
       StoreValueWide(rl_dest, rl_result);
@@ -1713,31 +1684,6 @@
   GenInvokeNoInline(info);
 }
 
-static LIR* GenInvokeNoInlineCall(Mir2Lir* mir_to_lir, InvokeType type) {
-  QuickEntrypointEnum trampoline;
-  switch (type) {
-    case kInterface:
-      trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
-      break;
-    case kDirect:
-      trampoline = kQuickInvokeDirectTrampolineWithAccessCheck;
-      break;
-    case kStatic:
-      trampoline = kQuickInvokeStaticTrampolineWithAccessCheck;
-      break;
-    case kSuper:
-      trampoline = kQuickInvokeSuperTrampolineWithAccessCheck;
-      break;
-    case kVirtual:
-      trampoline = kQuickInvokeVirtualTrampolineWithAccessCheck;
-      break;
-    default:
-      LOG(FATAL) << "Unexpected invoke type";
-      trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
-  }
-  return mir_to_lir->InvokeTrampoline(kOpBlx, RegStorage::InvalidReg(), trampoline);
-}
-
 void Mir2Lir::GenInvokeNoInline(CallInfo* info) {
   int call_state = 0;
   LIR* null_ck;
@@ -1751,7 +1697,7 @@
   cu_->compiler_driver->ProcessedInvoke(method_info.GetInvokeType(), method_info.StatsFlags());
   BeginInvoke(info);
   InvokeType original_type = static_cast<InvokeType>(method_info.GetInvokeType());
-  info->type = static_cast<InvokeType>(method_info.GetSharpType());
+  info->type = method_info.GetSharpType();
   bool fast_path = method_info.FastPath();
   bool skip_this;
   if (info->type == kInterface) {
@@ -1761,10 +1707,10 @@
     if (fast_path) {
       p_null_ck = &null_ck;
     }
-    next_call_insn = fast_path ? NextSDCallInsn : NextDirectCallInsnSP;
+    next_call_insn = fast_path ? GetNextSDCallInsn() : NextDirectCallInsnSP;
     skip_this = false;
   } else if (info->type == kStatic) {
-    next_call_insn = fast_path ? NextSDCallInsn : NextStaticCallInsnSP;
+    next_call_insn = fast_path ? GetNextSDCallInsn() : NextStaticCallInsnSP;
     skip_this = false;
   } else if (info->type == kSuper) {
     DCHECK(!fast_path);  // Fast path is a direct call.
@@ -1792,25 +1738,9 @@
     call_state = next_call_insn(cu_, info, call_state, target_method, method_info.VTableIndex(),
                                 method_info.DirectCode(), method_info.DirectMethod(), original_type);
   }
-  LIR* call_inst;
-  if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) {
-    call_inst = OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
-  } else {
-    if (fast_path) {
-      if (method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
-        // We can have the linker fixup a call relative.
-        call_inst =
-          reinterpret_cast<X86Mir2Lir*>(this)->CallWithLinkerFixup(target_method, info->type);
-      } else {
-        call_inst = OpMem(kOpBlx, TargetReg(kArg0, kRef),
-                          mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
-      }
-    } else {
-      call_inst = GenInvokeNoInlineCall(this, info->type);
-    }
-  }
+  LIR* call_insn = GenCallInsn(method_info);
   EndInvoke(info);
-  MarkSafepointPC(call_inst);
+  MarkSafepointPC(call_insn);
 
   ClobberCallerSave();
   if (info->result.location != kLocInvalid) {
@@ -1825,4 +1755,14 @@
   }
 }
 
+NextCallInsn Mir2Lir::GetNextSDCallInsn() {
+  return NextSDCallInsn;
+}
+
+LIR* Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+  DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64 &&
+         cu_->instruction_set != kThumb2 && cu_->instruction_set != kArm);
+  return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/mips/backend_mips.h b/compiler/dex/quick/mips/backend_mips.h
new file mode 100644
index 0000000..f65e984
--- /dev/null
+++ b/compiler/dex/quick/mips/backend_mips.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEX_QUICK_MIPS_BACKEND_MIPS_H_
+#define ART_COMPILER_DEX_QUICK_MIPS_BACKEND_MIPS_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+                           ArenaAllocator* const arena);
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_QUICK_MIPS_BACKEND_MIPS_H_
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index e8cb356..6536c41 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -62,7 +62,7 @@
  *
  */
 void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpSparseSwitchTable(table);
   }
@@ -74,7 +74,7 @@
   int elements = table[1];
   tab_rec->targets =
       static_cast<LIR**>(arena_->Alloc(elements * sizeof(LIR*), kArenaAllocLIR));
-  switch_tables_.Insert(tab_rec);
+  switch_tables_.push_back(tab_rec);
 
   // The table is composed of 8-byte key/disp pairs
   int byte_size = elements * 8;
@@ -139,7 +139,7 @@
  * done:
  */
 void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpPackedSwitchTable(table);
   }
@@ -151,7 +151,7 @@
   int size = table[1];
   tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
                                                       kArenaAllocLIR));
-  switch_tables_.Insert(tab_rec);
+  switch_tables_.push_back(tab_rec);
 
   // Get the switch value
   rl_src = LoadValue(rl_src, kCoreReg);
@@ -220,8 +220,8 @@
  *
  * Total size is 4+(width * size + 1)/2 16-bit code units.
  */
-void MipsMir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void MipsMir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   // Add the table to the list - we'll process it later
   FillArrayData* tab_rec =
       reinterpret_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData),
@@ -232,7 +232,7 @@
   uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
   tab_rec->size = (size * width) + 8;
 
-  fill_array_data_.Insert(tab_rec);
+  fill_array_data_.push_back(tab_rec);
 
   // Making a call - use explicit registers
   FlushAllRegs();   /* Everything to home location */
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 43cbde7..be94019 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_
 
 #include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
 #include "mips_lir.h"
 
 namespace art {
@@ -115,7 +116,7 @@
     void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
     void GenExitSequence();
     void GenSpecialExitSequence();
-    void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+    void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src);
     void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
     void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
     void GenSelect(BasicBlock* bb, MIR* mir);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index ea56989..95c1262 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -220,9 +220,9 @@
                                    int dest_reg_class) {
   // Implement as a branch-over.
   // TODO: Conditional move?
-  LoadConstant(rs_dest, false_val);  // Favors false.
-  LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL);
   LoadConstant(rs_dest, true_val);
+  LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL);
+  LoadConstant(rs_dest, false_val);
   LIR* target_label = NewLIR0(kPseudoTargetLabel);
   ne_branchover->target = target_label;
 }
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index bc91fbc..d3719ab 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -20,6 +20,7 @@
 
 #include <string>
 
+#include "backend_mips.h"
 #include "dex/compiler_internals.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "mips_lir.h"
@@ -429,16 +430,16 @@
 }
 
 void MipsMir2Lir::CompilerInitializeRegAlloc() {
-  reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */, sp_regs,
-                                        dp_regs, reserved_regs, empty_pool /* reserved64 */,
-                                        core_temps, empty_pool /* core64_temps */, sp_temps,
-                                        dp_temps);
+  reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */,
+                                            sp_regs, dp_regs,
+                                            reserved_regs, empty_pool /* reserved64 */,
+                                            core_temps, empty_pool /* core64_temps */,
+                                            sp_temps, dp_temps));
 
   // Target-specific adjustments.
 
   // Alias single precision floats to appropriate half of overlapping double.
-  GrowableArray<RegisterInfo*>::Iterator it(&reg_pool_->sp_regs_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : reg_pool_->sp_regs_) {
     int sp_reg_num = info->GetReg().GetRegNum();
 #if (FR_BIT == 0)
     int dp_reg_num = sp_reg_num & ~1;
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 2e4e292..0aefc2d 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -97,7 +97,7 @@
 }
 
 inline LIR* Mir2Lir::NewLIR2NoDest(int opcode, int src, int info) {
-  DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP))
+  DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP))
       << GetTargetInstName(opcode) << " " << opcode << " "
       << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
       << current_dalvik_offset_;
@@ -142,8 +142,9 @@
  */
 inline void Mir2Lir::SetupRegMask(ResourceMask* mask, int reg) {
   DCHECK_EQ((reg & ~RegStorage::kRegValMask), 0);
-  DCHECK(reginfo_map_.Get(reg) != nullptr) << "No info for 0x" << reg;
-  *mask = mask->Union(reginfo_map_.Get(reg)->DefUseMask());
+  DCHECK_LT(static_cast<size_t>(reg), reginfo_map_.size());
+  DCHECK(reginfo_map_[reg] != nullptr) << "No info for 0x" << reg;
+  *mask = mask->Union(reginfo_map_[reg]->DefUseMask());
 }
 
 /*
@@ -151,8 +152,9 @@
  */
 inline void Mir2Lir::ClearRegMask(ResourceMask* mask, int reg) {
   DCHECK_EQ((reg & ~RegStorage::kRegValMask), 0);
-  DCHECK(reginfo_map_.Get(reg) != nullptr) << "No info for 0x" << reg;
-  *mask = mask->ClearBits(reginfo_map_.Get(reg)->DefUseMask());
+  DCHECK_LT(static_cast<size_t>(reg), reginfo_map_.size());
+  DCHECK(reginfo_map_[reg] != nullptr) << "No info for 0x" << reg;
+  *mask = mask->ClearBits(reginfo_map_[reg]->DefUseMask());
 }
 
 /*
@@ -256,8 +258,7 @@
 }
 
 inline art::Mir2Lir::RegisterInfo* Mir2Lir::GetRegInfo(RegStorage reg) {
-  RegisterInfo* res = reg.IsPair() ? reginfo_map_.Get(reg.GetLowReg()) :
-      reginfo_map_.Get(reg.GetReg());
+  RegisterInfo* res = reg.IsPair() ? reginfo_map_[reg.GetLowReg()] : reginfo_map_[reg.GetReg()];
   DCHECK(res != nullptr);
   return res;
 }
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index e519011..6942c0f 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -18,6 +18,7 @@
 #include "dex/dataflow_iterator-inl.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "mir_to_lir-inl.h"
+#include "primitive.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -223,9 +224,27 @@
     return false;
   }
 
-  bool wide = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE));
-  bool ref = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT));
-  OpSize size = LoadStoreOpSize(wide, ref);
+  OpSize size = k32;
+  switch (data.op_variant) {
+    case InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT):
+      size = kReference;
+      break;
+    case InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE):
+      size = k64;
+      break;
+    case InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT):
+      size = kSignedHalf;
+      break;
+    case InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR):
+      size = kUnsignedHalf;
+      break;
+    case InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE):
+      size = kSignedByte;
+      break;
+    case InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN):
+      size = kUnsignedByte;
+      break;
+  }
 
   // Point of no return - no aborts after this
   GenPrintLabel(mir);
@@ -233,20 +252,20 @@
   RegStorage reg_obj = LoadArg(data.object_arg, kRefReg);
   RegisterClass reg_class = RegClassForFieldLoadStore(size, data.is_volatile);
   RegisterClass ret_reg_class = ShortyToRegClass(cu_->shorty[0]);
-  RegLocation rl_dest = wide ? GetReturnWide(ret_reg_class) : GetReturn(ret_reg_class);
+  RegLocation rl_dest = IsWide(size) ? GetReturnWide(ret_reg_class) : GetReturn(ret_reg_class);
   RegStorage r_result = rl_dest.reg;
   if (!RegClassMatches(reg_class, r_result)) {
-    r_result = wide ? AllocTypedTempWide(rl_dest.fp, reg_class)
-                    : AllocTypedTemp(rl_dest.fp, reg_class);
+    r_result = IsWide(size) ? AllocTypedTempWide(rl_dest.fp, reg_class)
+                            : AllocTypedTemp(rl_dest.fp, reg_class);
   }
-  if (ref) {
+  if (IsRef(size)) {
     LoadRefDisp(reg_obj, data.field_offset, r_result, data.is_volatile ? kVolatile : kNotVolatile);
   } else {
     LoadBaseDisp(reg_obj, data.field_offset, r_result, size, data.is_volatile ? kVolatile :
         kNotVolatile);
   }
   if (r_result.NotExactlyEquals(rl_dest.reg)) {
-    if (wide) {
+    if (IsWide(size)) {
       OpRegCopyWide(rl_dest.reg, r_result);
     } else {
       OpRegCopy(rl_dest.reg, r_result);
@@ -267,24 +286,42 @@
     return false;
   }
 
-  bool wide = (data.op_variant == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE));
-  bool ref = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT));
-  OpSize size = LoadStoreOpSize(wide, ref);
+  OpSize size = k32;
+  switch (data.op_variant) {
+    case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT):
+      size = kReference;
+      break;
+    case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE):
+      size = k64;
+      break;
+    case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT):
+      size = kSignedHalf;
+      break;
+    case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR):
+      size = kUnsignedHalf;
+      break;
+    case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE):
+      size = kSignedByte;
+      break;
+    case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN):
+      size = kUnsignedByte;
+      break;
+  }
 
   // Point of no return - no aborts after this
   GenPrintLabel(mir);
   LockArg(data.object_arg);
-  LockArg(data.src_arg, wide);
+  LockArg(data.src_arg, IsWide(size));
   RegStorage reg_obj = LoadArg(data.object_arg, kRefReg);
   RegisterClass reg_class = RegClassForFieldLoadStore(size, data.is_volatile);
-  RegStorage reg_src = LoadArg(data.src_arg, reg_class, wide);
-  if (ref) {
+  RegStorage reg_src = LoadArg(data.src_arg, reg_class, IsWide(size));
+  if (IsRef(size)) {
     StoreRefDisp(reg_obj, data.field_offset, reg_src, data.is_volatile ? kVolatile : kNotVolatile);
   } else {
     StoreBaseDisp(reg_obj, data.field_offset, reg_src, size, data.is_volatile ? kVolatile :
         kNotVolatile);
   }
-  if (ref) {
+  if (IsRef(size)) {
     MarkGCCard(reg_src, reg_obj);
   }
   return true;
@@ -562,7 +599,7 @@
       break;
 
     case Instruction::FILL_ARRAY_DATA:
-      GenFillArrayData(vB, rl_src[0]);
+      GenFillArrayData(mir, vB, rl_src[0]);
       break;
 
     case Instruction::FILLED_NEW_ARRAY:
@@ -720,84 +757,112 @@
       break;
 
     case Instruction::IGET_OBJECT:
-      GenIGet(mir, opt_flags, kReference, rl_dest, rl_src[0], false, true);
+      GenIGet(mir, opt_flags, kReference, Primitive::kPrimNot, rl_dest, rl_src[0]);
       break;
 
     case Instruction::IGET_WIDE:
-      GenIGet(mir, opt_flags, k64, rl_dest, rl_src[0], true, false);
+      // kPrimLong and kPrimDouble share the same entrypoints.
+      GenIGet(mir, opt_flags, k64, Primitive::kPrimLong, rl_dest, rl_src[0]);
       break;
 
     case Instruction::IGET:
-      GenIGet(mir, opt_flags, k32, rl_dest, rl_src[0], false, false);
+      GenIGet(mir, opt_flags, k32, Primitive::kPrimInt, rl_dest, rl_src[0]);
       break;
 
     case Instruction::IGET_CHAR:
-      GenIGet(mir, opt_flags, kUnsignedHalf, rl_dest, rl_src[0], false, false);
+      GenIGet(mir, opt_flags, kUnsignedHalf, Primitive::kPrimChar, rl_dest, rl_src[0]);
       break;
 
     case Instruction::IGET_SHORT:
-      GenIGet(mir, opt_flags, kSignedHalf, rl_dest, rl_src[0], false, false);
+      GenIGet(mir, opt_flags, kSignedHalf, Primitive::kPrimShort, rl_dest, rl_src[0]);
       break;
 
     case Instruction::IGET_BOOLEAN:
+      GenIGet(mir, opt_flags, kUnsignedByte, Primitive::kPrimBoolean, rl_dest, rl_src[0]);
+      break;
+
     case Instruction::IGET_BYTE:
-      GenIGet(mir, opt_flags, kUnsignedByte, rl_dest, rl_src[0], false, false);
+      GenIGet(mir, opt_flags, kSignedByte, Primitive::kPrimByte, rl_dest, rl_src[0]);
       break;
 
     case Instruction::IPUT_WIDE:
-      GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1], true, false);
+      GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1]);
       break;
 
     case Instruction::IPUT_OBJECT:
-      GenIPut(mir, opt_flags, kReference, rl_src[0], rl_src[1], false, true);
+      GenIPut(mir, opt_flags, kReference, rl_src[0], rl_src[1]);
       break;
 
     case Instruction::IPUT:
-      GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1], false, false);
+      GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1]);
       break;
 
-    case Instruction::IPUT_BOOLEAN:
     case Instruction::IPUT_BYTE:
-      GenIPut(mir, opt_flags, kUnsignedByte, rl_src[0], rl_src[1], false, false);
+    case Instruction::IPUT_BOOLEAN:
+      GenIPut(mir, opt_flags, kUnsignedByte, rl_src[0], rl_src[1]);
       break;
 
     case Instruction::IPUT_CHAR:
-      GenIPut(mir, opt_flags, kUnsignedHalf, rl_src[0], rl_src[1], false, false);
+      GenIPut(mir, opt_flags, kUnsignedHalf, rl_src[0], rl_src[1]);
       break;
 
     case Instruction::IPUT_SHORT:
-      GenIPut(mir, opt_flags, kSignedHalf, rl_src[0], rl_src[1], false, false);
+      GenIPut(mir, opt_flags, kSignedHalf, rl_src[0], rl_src[1]);
       break;
 
     case Instruction::SGET_OBJECT:
-      GenSget(mir, rl_dest, false, true);
+      GenSget(mir, rl_dest, kReference, Primitive::kPrimNot);
       break;
+
     case Instruction::SGET:
-    case Instruction::SGET_BOOLEAN:
-    case Instruction::SGET_BYTE:
+      GenSget(mir, rl_dest, k32, Primitive::kPrimInt);
+      break;
+
     case Instruction::SGET_CHAR:
+      GenSget(mir, rl_dest, kUnsignedHalf, Primitive::kPrimChar);
+      break;
+
     case Instruction::SGET_SHORT:
-      GenSget(mir, rl_dest, false, false);
+      GenSget(mir, rl_dest, kSignedHalf, Primitive::kPrimShort);
+      break;
+
+    case Instruction::SGET_BOOLEAN:
+      GenSget(mir, rl_dest, kUnsignedByte, Primitive::kPrimBoolean);
+      break;
+
+    case Instruction::SGET_BYTE:
+      GenSget(mir, rl_dest, kSignedByte, Primitive::kPrimByte);
       break;
 
     case Instruction::SGET_WIDE:
-      GenSget(mir, rl_dest, true, false);
+      // kPrimLong and kPrimDouble share the same entrypoints.
+      GenSget(mir, rl_dest, k64, Primitive::kPrimLong);
       break;
 
     case Instruction::SPUT_OBJECT:
-      GenSput(mir, rl_src[0], false, true);
+      GenSput(mir, rl_src[0], kReference);
       break;
 
     case Instruction::SPUT:
-    case Instruction::SPUT_BOOLEAN:
-    case Instruction::SPUT_BYTE:
-    case Instruction::SPUT_CHAR:
-    case Instruction::SPUT_SHORT:
-      GenSput(mir, rl_src[0], false, false);
+      GenSput(mir, rl_src[0], k32);
       break;
 
+    case Instruction::SPUT_BYTE:
+    case Instruction::SPUT_BOOLEAN:
+      GenSput(mir, rl_src[0], kUnsignedByte);
+      break;
+
+    case Instruction::SPUT_CHAR:
+      GenSput(mir, rl_src[0], kUnsignedHalf);
+      break;
+
+    case Instruction::SPUT_SHORT:
+      GenSput(mir, rl_src[0], kSignedHalf);
+      break;
+
+
     case Instruction::SPUT_WIDE:
-      GenSput(mir, rl_src[0], true, false);
+      GenSput(mir, rl_src[0], k64);
       break;
 
     case Instruction::INVOKE_STATIC_RANGE:
@@ -1077,9 +1142,17 @@
     case kMirOpSelect:
       GenSelect(bb, mir);
       break;
+    case kMirOpNullCheck: {
+      RegLocation rl_obj = mir_graph_->GetSrc(mir, 0);
+      rl_obj = LoadValue(rl_obj, kRefReg);
+      // An explicit check is done because it is not expected that when this is used,
+      // that it will actually trip up the implicit checks (since an invalid access
+      // is needed on the null object).
+      GenExplicitNullCheck(rl_obj.reg, mir->optimization_flags);
+      break;
+    }
     case kMirOpPhi:
     case kMirOpNop:
-    case kMirOpNullCheck:
     case kMirOpRangeCheck:
     case kMirOpDivZeroCheck:
     case kMirOpCheck:
@@ -1127,9 +1200,8 @@
 
   if (bb->block_type == kEntryBlock) {
     ResetRegPool();
-    int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
-    GenEntrySequence(&mir_graph_->reg_location_[start_vreg],
-                         mir_graph_->reg_location_[mir_graph_->GetMethodSReg()]);
+    int start_vreg = mir_graph_->GetFirstInVR();
+    GenEntrySequence(&mir_graph_->reg_location_[start_vreg], mir_graph_->GetMethodLoc());
   } else if (bb->block_type == kExitBlock) {
     ResetRegPool();
     GenExitSequence();
@@ -1196,13 +1268,12 @@
 bool Mir2Lir::SpecialMIR2LIR(const InlineMethod& special) {
   cu_->NewTimingSplit("SpecialMIR2LIR");
   // Find the first DalvikByteCode block.
-  int num_reachable_blocks = mir_graph_->GetNumReachableBlocks();
+  DCHECK_EQ(mir_graph_->GetNumReachableBlocks(), mir_graph_->GetDfsOrder().size());
   BasicBlock*bb = NULL;
-  for (int idx = 0; idx < num_reachable_blocks; idx++) {
-    // TODO: no direct access of growable lists.
-    int dfs_index = mir_graph_->GetDfsOrder()->Get(idx);
-    bb = mir_graph_->GetBasicBlock(dfs_index);
-    if (bb->block_type == kDalvikByteCode) {
+  for (BasicBlockId dfs_id : mir_graph_->GetDfsOrder()) {
+    BasicBlock* candidate = mir_graph_->GetBasicBlock(dfs_id);
+    if (candidate->block_type == kDalvikByteCode) {
+      bb = candidate;
       break;
     }
   }
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 3dc111f..67a8c0f 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -33,19 +33,10 @@
 #include "utils/array_ref.h"
 #include "utils/arena_allocator.h"
 #include "utils/arena_containers.h"
-#include "utils/growable_array.h"
 #include "utils/stack_checks.h"
 
 namespace art {
 
-/*
- * TODO: refactoring pass to move these (and other) typdefs towards usage style of runtime to
- * add type safety (see runtime/offsets.h).
- */
-typedef uint32_t DexOffset;          // Dex offset in code units.
-typedef uint16_t NarrowDexOffset;    // For use in structs, Dex offsets range from 0 .. 0xffff.
-typedef uint32_t CodeOffset;         // Native code offset in bytes.
-
 // Set to 1 to measure cost of suspend check.
 #define NO_SUSPEND 0
 
@@ -147,6 +138,7 @@
 struct RegisterInfo;
 class DexFileMethodInliner;
 class MIRGraph;
+class MirMethodLoweringInfo;
 class Mir2Lir;
 
 typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
@@ -187,16 +179,6 @@
   int32_t operands[5];           // [0..4] = [dest, src1, src2, extra, extra2].
 };
 
-// Target-specific initialization.
-Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
-                          ArenaAllocator* const arena);
-Mir2Lir* Arm64CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
-                            ArenaAllocator* const arena);
-Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
-                          ArenaAllocator* const arena);
-Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
-                          ArenaAllocator* const arena);
-
 // Utility macros to traverse the LIR list.
 #define NEXT_LIR(lir) (lir->next)
 #define PREV_LIR(lir) (lir->prev)
@@ -455,20 +437,21 @@
       static void* operator new(size_t size, ArenaAllocator* arena) {
         return arena->Alloc(size, kArenaAllocRegAlloc);
       }
+      static void operator delete(void* ptr) { UNUSED(ptr); }
       void ResetNextTemp() {
         next_core_reg_ = 0;
         next_sp_reg_ = 0;
         next_dp_reg_ = 0;
       }
-      GrowableArray<RegisterInfo*> core_regs_;
+      ArenaVector<RegisterInfo*> core_regs_;
       int next_core_reg_;
-      GrowableArray<RegisterInfo*> core64_regs_;
+      ArenaVector<RegisterInfo*> core64_regs_;
       int next_core64_reg_;
-      GrowableArray<RegisterInfo*> sp_regs_;    // Single precision float.
+      ArenaVector<RegisterInfo*> sp_regs_;    // Single precision float.
       int next_sp_reg_;
-      GrowableArray<RegisterInfo*> dp_regs_;    // Double precision float.
+      ArenaVector<RegisterInfo*> dp_regs_;    // Double precision float.
       int next_dp_reg_;
-      GrowableArray<RegisterInfo*>* ref_regs_;  // Points to core_regs_ or core64_regs_
+      ArenaVector<RegisterInfo*>* ref_regs_;  // Points to core_regs_ or core64_regs_
       int* next_ref_reg_;
 
      private:
@@ -615,13 +598,13 @@
      * may be worth conditionally-compiling a set of identity functions here.
      */
     uint32_t WrapPointer(void* pointer) {
-      uint32_t res = pointer_storage_.Size();
-      pointer_storage_.Insert(pointer);
+      uint32_t res = pointer_storage_.size();
+      pointer_storage_.push_back(pointer);
       return res;
     }
 
     void* UnwrapPointer(size_t index) {
-      return pointer_storage_.Get(index);
+      return pointer_storage_[index];
     }
 
     // strdup(), but allocates from the arena.
@@ -685,6 +668,7 @@
     LIR* ScanLiteralPool(LIR* data_target, int value, unsigned int delta);
     LIR* ScanLiteralPoolWide(LIR* data_target, int val_lo, int val_hi);
     LIR* ScanLiteralPoolMethod(LIR* data_target, const MethodReference& method);
+    LIR* ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx);
     LIR* AddWordData(LIR* *constant_list_p, int value);
     LIR* AddWideData(LIR* *constant_list_p, int val_lo, int val_hi);
     void ProcessSwitchTables();
@@ -730,7 +714,7 @@
     void SimpleRegAlloc();
     void ResetRegPool();
     void CompilerInitPool(RegisterInfo* info, RegStorage* regs, int num);
-    void DumpRegPool(GrowableArray<RegisterInfo*>* regs);
+    void DumpRegPool(ArenaVector<RegisterInfo*>* regs);
     void DumpCoreRegPool();
     void DumpFpRegPool();
     void DumpRegPools();
@@ -745,7 +729,7 @@
     RegStorage AllocPreservedFpReg(int s_reg);
     virtual RegStorage AllocPreservedSingle(int s_reg);
     virtual RegStorage AllocPreservedDouble(int s_reg);
-    RegStorage AllocTempBody(GrowableArray<RegisterInfo*> &regs, int* next_temp, bool required);
+    RegStorage AllocTempBody(ArenaVector<RegisterInfo*>& regs, int* next_temp, bool required);
     virtual RegStorage AllocTemp(bool required = true);
     virtual RegStorage AllocTempWide(bool required = true);
     virtual RegStorage AllocTempRef(bool required = true);
@@ -756,7 +740,7 @@
     void FlushReg(RegStorage reg);
     void FlushRegWide(RegStorage reg);
     RegStorage AllocLiveReg(int s_reg, int reg_class, bool wide);
-    RegStorage FindLiveReg(GrowableArray<RegisterInfo*> &regs, int s_reg);
+    RegStorage FindLiveReg(ArenaVector<RegisterInfo*>& regs, int s_reg);
     virtual void FreeTemp(RegStorage reg);
     virtual void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free);
     virtual bool IsLive(RegStorage reg);
@@ -848,14 +832,14 @@
     void GenNewArray(uint32_t type_idx, RegLocation rl_dest,
                      RegLocation rl_src);
     void GenFilledNewArray(CallInfo* info);
-    void GenSput(MIR* mir, RegLocation rl_src,
-                 bool is_long_or_double, bool is_object);
-    void GenSget(MIR* mir, RegLocation rl_dest,
-                 bool is_long_or_double, bool is_object);
-    void GenIGet(MIR* mir, int opt_flags, OpSize size,
-                 RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+    void GenSput(MIR* mir, RegLocation rl_src, OpSize size);
+    // Get entrypoints are specific for types, size alone is not sufficient to safely infer
+    // entrypoint.
+    void GenSget(MIR* mir, RegLocation rl_dest, OpSize size, Primitive::Type type);
+    void GenIGet(MIR* mir, int opt_flags, OpSize size, Primitive::Type type,
+                 RegLocation rl_dest, RegLocation rl_obj);
     void GenIPut(MIR* mir, int opt_flags, OpSize size,
-                 RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+                 RegLocation rl_src, RegLocation rl_obj);
     void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
                         RegLocation rl_src);
 
@@ -926,6 +910,15 @@
                                                             bool safepoint_pc);
     void GenInvoke(CallInfo* info);
     void GenInvokeNoInline(CallInfo* info);
+    virtual NextCallInsn GetNextSDCallInsn();
+
+    /*
+     * @brief Generate the actual call insn based on the method info.
+     * @param method_info the lowering info for the method call.
+     * @returns Call instruction
+     */
+    virtual LIR* GenCallInsn(const MirMethodLoweringInfo& method_info);
+
     virtual void FlushIns(RegLocation* ArgLocs, RegLocation rl_method);
     virtual int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
                              NextCallInsn next_call_insn,
@@ -963,7 +956,7 @@
     bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty);
     virtual bool GenInlinedReverseBits(CallInfo* info, OpSize size);
     bool GenInlinedReverseBytes(CallInfo* info, OpSize size);
-    bool GenInlinedAbsInt(CallInfo* info);
+    virtual bool GenInlinedAbsInt(CallInfo* info);
     virtual bool GenInlinedAbsLong(CallInfo* info);
     virtual bool GenInlinedAbsFloat(CallInfo* info) = 0;
     virtual bool GenInlinedAbsDouble(CallInfo* info) = 0;
@@ -995,6 +988,10 @@
     virtual LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
       return LoadBaseDisp(r_base, displacement, r_dest, kWord, kNotVolatile);
     }
+    // Load 8 bits, regardless of target.
+    virtual LIR* Load8Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
+      return LoadBaseDisp(r_base, displacement, r_dest, kSignedByte, kNotVolatile);
+    }
     // Load 32 bits, regardless of target.
     virtual LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest)  {
       return LoadBaseDisp(r_base, displacement, r_dest, k32, kNotVolatile);
@@ -1113,11 +1110,13 @@
 
     /*
      * @brief Load the Class* of a Dex Class type into the register.
+     * @param dex DexFile that contains the class type.
      * @param type How the method will be invoked.
      * @param register that will contain the code address.
      * @note register will be passed to TargetReg to get physical register.
      */
-    virtual void LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg);
+    virtual void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+                               SpecialTargetRegister symbolic_reg);
 
     // Routines that work for the generic case, but may be overriden by target.
     /*
@@ -1164,6 +1163,14 @@
              (info1->StorageMask() & info2->StorageMask()) != 0);
     }
 
+    static constexpr bool IsWide(OpSize size) {
+      return size == k64 || size == kDouble;
+    }
+
+    static constexpr bool IsRef(OpSize size) {
+      return size == kReference;
+    }
+
     /**
      * @brief Portable way of getting special registers from the backend.
      * @param reg Enumeration describing the purpose of the register.
@@ -1316,7 +1323,7 @@
 
     virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) = 0;
     virtual void GenExitSequence() = 0;
-    virtual void GenFillArrayData(DexOffset table_offset, RegLocation rl_src) = 0;
+    virtual void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) = 0;
     virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) = 0;
     virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) = 0;
 
@@ -1498,10 +1505,6 @@
      */
     virtual RegLocation ForceTempWide(RegLocation loc);
 
-    static constexpr OpSize LoadStoreOpSize(bool wide, bool ref) {
-      return wide ? k64 : ref ? kReference : k32;
-    }
-
     virtual void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx,
                                     RegLocation rl_dest, RegLocation rl_src);
 
@@ -1525,10 +1528,10 @@
                                     uint32_t type_idx, RegLocation rl_dest,
                                     RegLocation rl_src);
     /*
-     * @brief Generate the debug_frame FDE information if possible.
-     * @returns pointer to vector containg CFE information, or NULL.
+     * @brief Generate the eh_frame FDE information if possible.
+     * @returns pointer to vector containg FDE information, or NULL.
      */
-    virtual std::vector<uint8_t>* ReturnCallFrameInformation();
+    virtual std::vector<uint8_t>* ReturnFrameDescriptionEntry();
 
     /**
      * @brief Used to insert marker that can be used to associate MIR with LIR.
@@ -1683,11 +1686,11 @@
   protected:
     CompilationUnit* const cu_;
     MIRGraph* const mir_graph_;
-    GrowableArray<SwitchTable*> switch_tables_;
-    GrowableArray<FillArrayData*> fill_array_data_;
-    GrowableArray<RegisterInfo*> tempreg_info_;
-    GrowableArray<RegisterInfo*> reginfo_map_;
-    GrowableArray<void*> pointer_storage_;
+    ArenaVector<SwitchTable*> switch_tables_;
+    ArenaVector<FillArrayData*> fill_array_data_;
+    ArenaVector<RegisterInfo*> tempreg_info_;
+    ArenaVector<RegisterInfo*> reginfo_map_;
+    ArenaVector<void*> pointer_storage_;
     CodeOffset current_code_offset_;    // Working byte offset of machine instructons.
     CodeOffset data_offset_;            // starting offset of literal pool.
     size_t total_size_;                   // header + code size.
@@ -1704,7 +1707,7 @@
      */
     DexOffset current_dalvik_offset_;
     size_t estimated_native_code_size_;     // Just an estimate; used to reserve code_buffer_ size.
-    RegisterPool* reg_pool_;
+    std::unique_ptr<RegisterPool> reg_pool_;
     /*
      * Sanity checking for the register temp tracking.  The same ssa
      * name should never be associated with one temp register per
@@ -1712,11 +1715,14 @@
      */
     int live_sreg_;
     CodeBuffer code_buffer_;
+    // The source mapping table data (pc -> dex). More entries than in encoded_mapping_table_
+    SrcMap src_mapping_table_;
     // The encoding mapping table data (dex -> pc offset and pc offset -> dex) with a size prefix.
     std::vector<uint8_t> encoded_mapping_table_;
     ArenaVector<uint32_t> core_vmap_table_;
     ArenaVector<uint32_t> fp_vmap_table_;
     std::vector<uint8_t> native_gc_map_;
+    ArenaVector<LinkerPatch> patches_;
     int num_core_spills_;
     int num_fp_spills_;
     int frame_size_;
@@ -1725,7 +1731,7 @@
     LIR* first_lir_insn_;
     LIR* last_lir_insn_;
 
-    GrowableArray<LIRSlowPath*> slow_paths_;
+    ArenaVector<LIRSlowPath*> slow_paths_;
 
     // The memory reference type for new LIRs.
     // NOTE: Passing this as an explicit parameter by all functions that directly or indirectly
@@ -1737,6 +1743,9 @@
     // (i.e. 8 bytes on 32-bit arch, 16 bytes on 64-bit arch) and we use ResourceMaskCache
     // to deduplicate the masks.
     ResourceMaskCache mask_cache_;
+
+  private:
+    static bool SizeMatchesTypeForEntrypoint(OpSize size, Primitive::Type type);
 };  // Class Mir2Lir
 
 }  // namespace art
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
new file mode 100644
index 0000000..6f2a647
--- /dev/null
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "quick_compiler.h"
+
+#include <cstdint>
+
+#include "compiler.h"
+#include "dex/frontend.h"
+#include "dex/mir_graph.h"
+#include "dex/quick/mir_to_lir.h"
+#include "driver/compiler_driver.h"
+#include "elf_writer_quick.h"
+#include "jni/quick/jni_compiler.h"
+#include "mirror/art_method-inl.h"
+#include "base/logging.h"
+
+// Specific compiler backends.
+#include "dex/quick/arm/backend_arm.h"
+#include "dex/quick/arm64/backend_arm64.h"
+#include "dex/quick/mips/backend_mips.h"
+#include "dex/quick/x86/backend_x86.h"
+
+namespace art {
+
+class QuickCompiler : public Compiler {
+ public:
+  explicit QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {}
+
+  void Init() const OVERRIDE;
+
+  void UnInit() const OVERRIDE;
+
+  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+      OVERRIDE;
+
+  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+                          uint32_t access_flags,
+                          InvokeType invoke_type,
+                          uint16_t class_def_idx,
+                          uint32_t method_idx,
+                          jobject class_loader,
+                          const DexFile& dex_file) const OVERRIDE;
+
+  CompiledMethod* JniCompile(uint32_t access_flags,
+                             uint32_t method_idx,
+                             const DexFile& dex_file) const OVERRIDE;
+
+  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool WriteElf(art::File* file,
+                OatWriter* oat_writer,
+                const std::vector<const art::DexFile*>& dex_files,
+                const std::string& android_root,
+                bool is_host) const
+    OVERRIDE
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
+
+  void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
+};
+
+COMPILE_ASSERT(0U == static_cast<size_t>(kNone), kNone_not_0);
+COMPILE_ASSERT(1U == static_cast<size_t>(kArm), kArm_not_1);
+COMPILE_ASSERT(2U == static_cast<size_t>(kArm64), kArm64_not_2);
+COMPILE_ASSERT(3U == static_cast<size_t>(kThumb2), kThumb2_not_3);
+COMPILE_ASSERT(4U == static_cast<size_t>(kX86), kX86_not_4);
+COMPILE_ASSERT(5U == static_cast<size_t>(kX86_64), kX86_64_not_5);
+COMPILE_ASSERT(6U == static_cast<size_t>(kMips), kMips_not_6);
+COMPILE_ASSERT(7U == static_cast<size_t>(kMips64), kMips64_not_7);
+
+// Additional disabled optimizations (over generally disabled) per instruction set.
+static constexpr uint32_t kDisabledOptimizationsPerISA[] = {
+    // 0 = kNone.
+    ~0U,
+    // 1 = kArm, unused (will use kThumb2).
+    ~0U,
+    // 2 = kArm64.
+    0,
+    // 3 = kThumb2.
+    0,
+    // 4 = kX86.
+    (1 << kLoadStoreElimination) |
+    0,
+    // 5 = kX86_64.
+    (1 << kLoadStoreElimination) |
+    0,
+    // 6 = kMips.
+    (1 << kLoadStoreElimination) |
+    (1 << kLoadHoisting) |
+    (1 << kSuppressLoads) |
+    (1 << kNullCheckElimination) |
+    (1 << kPromoteRegs) |
+    (1 << kTrackLiveTemps) |
+    (1 << kSafeOptimizations) |
+    (1 << kBBOpt) |
+    (1 << kMatch) |
+    (1 << kPromoteCompilerTemps) |
+    0,
+    // 7 = kMips64.
+    ~0U
+};
+COMPILE_ASSERT(sizeof(kDisabledOptimizationsPerISA) == 8 * sizeof(uint32_t), kDisabledOpts_unexp);
+
+// Supported shorty types per instruction set. nullptr means that all are available.
+// Z : boolean
+// B : byte
+// S : short
+// C : char
+// I : int
+// J : long
+// F : float
+// D : double
+// L : reference(object, array)
+// V : void
+static const char* kSupportedTypes[] = {
+    // 0 = kNone.
+    "",
+    // 1 = kArm, unused (will use kThumb2).
+    "",
+    // 2 = kArm64.
+    nullptr,
+    // 3 = kThumb2.
+    nullptr,
+    // 4 = kX86.
+    nullptr,
+    // 5 = kX86_64.
+    nullptr,
+    // 6 = kMips.
+    nullptr,
+    // 7 = kMips64.
+    ""
+};
+COMPILE_ASSERT(sizeof(kSupportedTypes) == 8 * sizeof(char*), kSupportedTypes_unexp);
+
+static int kAllOpcodes[] = {
+    Instruction::NOP,
+    Instruction::MOVE,
+    Instruction::MOVE_FROM16,
+    Instruction::MOVE_16,
+    Instruction::MOVE_WIDE,
+    Instruction::MOVE_WIDE_FROM16,
+    Instruction::MOVE_WIDE_16,
+    Instruction::MOVE_OBJECT,
+    Instruction::MOVE_OBJECT_FROM16,
+    Instruction::MOVE_OBJECT_16,
+    Instruction::MOVE_RESULT,
+    Instruction::MOVE_RESULT_WIDE,
+    Instruction::MOVE_RESULT_OBJECT,
+    Instruction::MOVE_EXCEPTION,
+    Instruction::RETURN_VOID,
+    Instruction::RETURN,
+    Instruction::RETURN_WIDE,
+    Instruction::RETURN_OBJECT,
+    Instruction::CONST_4,
+    Instruction::CONST_16,
+    Instruction::CONST,
+    Instruction::CONST_HIGH16,
+    Instruction::CONST_WIDE_16,
+    Instruction::CONST_WIDE_32,
+    Instruction::CONST_WIDE,
+    Instruction::CONST_WIDE_HIGH16,
+    Instruction::CONST_STRING,
+    Instruction::CONST_STRING_JUMBO,
+    Instruction::CONST_CLASS,
+    Instruction::MONITOR_ENTER,
+    Instruction::MONITOR_EXIT,
+    Instruction::CHECK_CAST,
+    Instruction::INSTANCE_OF,
+    Instruction::ARRAY_LENGTH,
+    Instruction::NEW_INSTANCE,
+    Instruction::NEW_ARRAY,
+    Instruction::FILLED_NEW_ARRAY,
+    Instruction::FILLED_NEW_ARRAY_RANGE,
+    Instruction::FILL_ARRAY_DATA,
+    Instruction::THROW,
+    Instruction::GOTO,
+    Instruction::GOTO_16,
+    Instruction::GOTO_32,
+    Instruction::PACKED_SWITCH,
+    Instruction::SPARSE_SWITCH,
+    Instruction::CMPL_FLOAT,
+    Instruction::CMPG_FLOAT,
+    Instruction::CMPL_DOUBLE,
+    Instruction::CMPG_DOUBLE,
+    Instruction::CMP_LONG,
+    Instruction::IF_EQ,
+    Instruction::IF_NE,
+    Instruction::IF_LT,
+    Instruction::IF_GE,
+    Instruction::IF_GT,
+    Instruction::IF_LE,
+    Instruction::IF_EQZ,
+    Instruction::IF_NEZ,
+    Instruction::IF_LTZ,
+    Instruction::IF_GEZ,
+    Instruction::IF_GTZ,
+    Instruction::IF_LEZ,
+    Instruction::UNUSED_3E,
+    Instruction::UNUSED_3F,
+    Instruction::UNUSED_40,
+    Instruction::UNUSED_41,
+    Instruction::UNUSED_42,
+    Instruction::UNUSED_43,
+    Instruction::AGET,
+    Instruction::AGET_WIDE,
+    Instruction::AGET_OBJECT,
+    Instruction::AGET_BOOLEAN,
+    Instruction::AGET_BYTE,
+    Instruction::AGET_CHAR,
+    Instruction::AGET_SHORT,
+    Instruction::APUT,
+    Instruction::APUT_WIDE,
+    Instruction::APUT_OBJECT,
+    Instruction::APUT_BOOLEAN,
+    Instruction::APUT_BYTE,
+    Instruction::APUT_CHAR,
+    Instruction::APUT_SHORT,
+    Instruction::IGET,
+    Instruction::IGET_WIDE,
+    Instruction::IGET_OBJECT,
+    Instruction::IGET_BOOLEAN,
+    Instruction::IGET_BYTE,
+    Instruction::IGET_CHAR,
+    Instruction::IGET_SHORT,
+    Instruction::IPUT,
+    Instruction::IPUT_WIDE,
+    Instruction::IPUT_OBJECT,
+    Instruction::IPUT_BOOLEAN,
+    Instruction::IPUT_BYTE,
+    Instruction::IPUT_CHAR,
+    Instruction::IPUT_SHORT,
+    Instruction::SGET,
+    Instruction::SGET_WIDE,
+    Instruction::SGET_OBJECT,
+    Instruction::SGET_BOOLEAN,
+    Instruction::SGET_BYTE,
+    Instruction::SGET_CHAR,
+    Instruction::SGET_SHORT,
+    Instruction::SPUT,
+    Instruction::SPUT_WIDE,
+    Instruction::SPUT_OBJECT,
+    Instruction::SPUT_BOOLEAN,
+    Instruction::SPUT_BYTE,
+    Instruction::SPUT_CHAR,
+    Instruction::SPUT_SHORT,
+    Instruction::INVOKE_VIRTUAL,
+    Instruction::INVOKE_SUPER,
+    Instruction::INVOKE_DIRECT,
+    Instruction::INVOKE_STATIC,
+    Instruction::INVOKE_INTERFACE,
+    Instruction::RETURN_VOID_BARRIER,
+    Instruction::INVOKE_VIRTUAL_RANGE,
+    Instruction::INVOKE_SUPER_RANGE,
+    Instruction::INVOKE_DIRECT_RANGE,
+    Instruction::INVOKE_STATIC_RANGE,
+    Instruction::INVOKE_INTERFACE_RANGE,
+    Instruction::UNUSED_79,
+    Instruction::UNUSED_7A,
+    Instruction::NEG_INT,
+    Instruction::NOT_INT,
+    Instruction::NEG_LONG,
+    Instruction::NOT_LONG,
+    Instruction::NEG_FLOAT,
+    Instruction::NEG_DOUBLE,
+    Instruction::INT_TO_LONG,
+    Instruction::INT_TO_FLOAT,
+    Instruction::INT_TO_DOUBLE,
+    Instruction::LONG_TO_INT,
+    Instruction::LONG_TO_FLOAT,
+    Instruction::LONG_TO_DOUBLE,
+    Instruction::FLOAT_TO_INT,
+    Instruction::FLOAT_TO_LONG,
+    Instruction::FLOAT_TO_DOUBLE,
+    Instruction::DOUBLE_TO_INT,
+    Instruction::DOUBLE_TO_LONG,
+    Instruction::DOUBLE_TO_FLOAT,
+    Instruction::INT_TO_BYTE,
+    Instruction::INT_TO_CHAR,
+    Instruction::INT_TO_SHORT,
+    Instruction::ADD_INT,
+    Instruction::SUB_INT,
+    Instruction::MUL_INT,
+    Instruction::DIV_INT,
+    Instruction::REM_INT,
+    Instruction::AND_INT,
+    Instruction::OR_INT,
+    Instruction::XOR_INT,
+    Instruction::SHL_INT,
+    Instruction::SHR_INT,
+    Instruction::USHR_INT,
+    Instruction::ADD_LONG,
+    Instruction::SUB_LONG,
+    Instruction::MUL_LONG,
+    Instruction::DIV_LONG,
+    Instruction::REM_LONG,
+    Instruction::AND_LONG,
+    Instruction::OR_LONG,
+    Instruction::XOR_LONG,
+    Instruction::SHL_LONG,
+    Instruction::SHR_LONG,
+    Instruction::USHR_LONG,
+    Instruction::ADD_FLOAT,
+    Instruction::SUB_FLOAT,
+    Instruction::MUL_FLOAT,
+    Instruction::DIV_FLOAT,
+    Instruction::REM_FLOAT,
+    Instruction::ADD_DOUBLE,
+    Instruction::SUB_DOUBLE,
+    Instruction::MUL_DOUBLE,
+    Instruction::DIV_DOUBLE,
+    Instruction::REM_DOUBLE,
+    Instruction::ADD_INT_2ADDR,
+    Instruction::SUB_INT_2ADDR,
+    Instruction::MUL_INT_2ADDR,
+    Instruction::DIV_INT_2ADDR,
+    Instruction::REM_INT_2ADDR,
+    Instruction::AND_INT_2ADDR,
+    Instruction::OR_INT_2ADDR,
+    Instruction::XOR_INT_2ADDR,
+    Instruction::SHL_INT_2ADDR,
+    Instruction::SHR_INT_2ADDR,
+    Instruction::USHR_INT_2ADDR,
+    Instruction::ADD_LONG_2ADDR,
+    Instruction::SUB_LONG_2ADDR,
+    Instruction::MUL_LONG_2ADDR,
+    Instruction::DIV_LONG_2ADDR,
+    Instruction::REM_LONG_2ADDR,
+    Instruction::AND_LONG_2ADDR,
+    Instruction::OR_LONG_2ADDR,
+    Instruction::XOR_LONG_2ADDR,
+    Instruction::SHL_LONG_2ADDR,
+    Instruction::SHR_LONG_2ADDR,
+    Instruction::USHR_LONG_2ADDR,
+    Instruction::ADD_FLOAT_2ADDR,
+    Instruction::SUB_FLOAT_2ADDR,
+    Instruction::MUL_FLOAT_2ADDR,
+    Instruction::DIV_FLOAT_2ADDR,
+    Instruction::REM_FLOAT_2ADDR,
+    Instruction::ADD_DOUBLE_2ADDR,
+    Instruction::SUB_DOUBLE_2ADDR,
+    Instruction::MUL_DOUBLE_2ADDR,
+    Instruction::DIV_DOUBLE_2ADDR,
+    Instruction::REM_DOUBLE_2ADDR,
+    Instruction::ADD_INT_LIT16,
+    Instruction::RSUB_INT,
+    Instruction::MUL_INT_LIT16,
+    Instruction::DIV_INT_LIT16,
+    Instruction::REM_INT_LIT16,
+    Instruction::AND_INT_LIT16,
+    Instruction::OR_INT_LIT16,
+    Instruction::XOR_INT_LIT16,
+    Instruction::ADD_INT_LIT8,
+    Instruction::RSUB_INT_LIT8,
+    Instruction::MUL_INT_LIT8,
+    Instruction::DIV_INT_LIT8,
+    Instruction::REM_INT_LIT8,
+    Instruction::AND_INT_LIT8,
+    Instruction::OR_INT_LIT8,
+    Instruction::XOR_INT_LIT8,
+    Instruction::SHL_INT_LIT8,
+    Instruction::SHR_INT_LIT8,
+    Instruction::USHR_INT_LIT8,
+    Instruction::IGET_QUICK,
+    Instruction::IGET_WIDE_QUICK,
+    Instruction::IGET_OBJECT_QUICK,
+    Instruction::IPUT_QUICK,
+    Instruction::IPUT_WIDE_QUICK,
+    Instruction::IPUT_OBJECT_QUICK,
+    Instruction::INVOKE_VIRTUAL_QUICK,
+    Instruction::INVOKE_VIRTUAL_RANGE_QUICK,
+    Instruction::IPUT_BOOLEAN_QUICK,
+    Instruction::IPUT_BYTE_QUICK,
+    Instruction::IPUT_CHAR_QUICK,
+    Instruction::IPUT_SHORT_QUICK,
+    Instruction::UNUSED_EF,
+    Instruction::UNUSED_F0,
+    Instruction::UNUSED_F1,
+    Instruction::UNUSED_F2,
+    Instruction::UNUSED_F3,
+    Instruction::UNUSED_F4,
+    Instruction::UNUSED_F5,
+    Instruction::UNUSED_F6,
+    Instruction::UNUSED_F7,
+    Instruction::UNUSED_F8,
+    Instruction::UNUSED_F9,
+    Instruction::UNUSED_FA,
+    Instruction::UNUSED_FB,
+    Instruction::UNUSED_FC,
+    Instruction::UNUSED_FD,
+    Instruction::UNUSED_FE,
+    Instruction::UNUSED_FF,
+    // ----- ExtendedMIROpcode -----
+    kMirOpPhi,
+    kMirOpCopy,
+    kMirOpFusedCmplFloat,
+    kMirOpFusedCmpgFloat,
+    kMirOpFusedCmplDouble,
+    kMirOpFusedCmpgDouble,
+    kMirOpFusedCmpLong,
+    kMirOpNop,
+    kMirOpNullCheck,
+    kMirOpRangeCheck,
+    kMirOpDivZeroCheck,
+    kMirOpCheck,
+    kMirOpCheckPart2,
+    kMirOpSelect,
+};
+
+// Unsupported opcodes. nullptr can be used when everything is supported. Size of the lists is
+// recorded below.
+static const int* kUnsupportedOpcodes[] = {
+    // 0 = kNone.
+    kAllOpcodes,
+    // 1 = kArm, unused (will use kThumb2).
+    kAllOpcodes,
+    // 2 = kArm64.
+    nullptr,
+    // 3 = kThumb2.
+    nullptr,
+    // 4 = kX86.
+    nullptr,
+    // 5 = kX86_64.
+    nullptr,
+    // 6 = kMips.
+    nullptr,
+    // 7 = kMips64.
+    kAllOpcodes
+};
+COMPILE_ASSERT(sizeof(kUnsupportedOpcodes) == 8 * sizeof(int*), kUnsupportedOpcodes_unexp);
+
+// Size of the arrays stored above.
+static const size_t kUnsupportedOpcodesSize[] = {
+    // 0 = kNone.
+    arraysize(kAllOpcodes),
+    // 1 = kArm, unused (will use kThumb2).
+    arraysize(kAllOpcodes),
+    // 2 = kArm64.
+    0,
+    // 3 = kThumb2.
+    0,
+    // 4 = kX86.
+    0,
+    // 5 = kX86_64.
+    0,
+    // 6 = kMips.
+    0,
+    // 7 = kMips64.
+    arraysize(kAllOpcodes),
+};
+COMPILE_ASSERT(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t),
+               kUnsupportedOpcodesSize_unexp);
+
+// The maximum amount of Dalvik register in a method for which we will start compiling. Tries to
+// avoid an abort when we need to manage more SSA registers than we can.
+static constexpr size_t kMaxAllowedDalvikRegisters = INT16_MAX / 2;
+
+static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) {
+  const char* supported_types = kSupportedTypes[instruction_set];
+  if (supported_types == nullptr) {
+    // Everything available.
+    return true;
+  }
+
+  uint32_t shorty_size = strlen(shorty);
+  CHECK_GE(shorty_size, 1u);
+
+  for (uint32_t i = 0; i < shorty_size; i++) {
+    if (strchr(supported_types, shorty[i]) == nullptr) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Skip the method that we do not support currently.
+bool QuickCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
+                                     CompilationUnit* cu) const {
+  // This is a limitation in mir_graph. See MirGraph::SetNumSSARegs.
+  if (cu->mir_graph->GetNumOfCodeAndTempVRs() > kMaxAllowedDalvikRegisters) {
+    VLOG(compiler) << "Too many dalvik registers : " << cu->mir_graph->GetNumOfCodeAndTempVRs();
+    return false;
+  }
+
+  // Check whether we do have limitations at all.
+  if (kSupportedTypes[cu->instruction_set] == nullptr &&
+      kUnsupportedOpcodesSize[cu->instruction_set] == 0U) {
+    return true;
+  }
+
+  // Check if we can compile the prototype.
+  const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
+  if (!CanCompileShorty(shorty, cu->instruction_set)) {
+    VLOG(compiler) << "Unsupported shorty : " << shorty;
+    return false;
+  }
+
+  const int *unsupport_list = kUnsupportedOpcodes[cu->instruction_set];
+  int unsupport_list_size = kUnsupportedOpcodesSize[cu->instruction_set];
+
+  for (unsigned int idx = 0; idx < cu->mir_graph->GetNumBlocks(); idx++) {
+    BasicBlock* bb = cu->mir_graph->GetBasicBlock(idx);
+    if (bb == NULL) continue;
+    if (bb->block_type == kDead) continue;
+    for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+      int opcode = mir->dalvikInsn.opcode;
+      // Check if we support the byte code.
+      if (std::find(unsupport_list, unsupport_list + unsupport_list_size,
+                    opcode) != unsupport_list + unsupport_list_size) {
+        if (!MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
+          VLOG(compiler) << "Unsupported dalvik byte code : "
+              << mir->dalvikInsn.opcode;
+        } else {
+          VLOG(compiler) << "Unsupported extended MIR opcode : "
+              << MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst];
+        }
+        return false;
+      }
+      // Check if it invokes a prototype that we cannot support.
+      if (Instruction::INVOKE_VIRTUAL == opcode ||
+          Instruction::INVOKE_SUPER == opcode ||
+          Instruction::INVOKE_DIRECT == opcode ||
+          Instruction::INVOKE_STATIC == opcode ||
+          Instruction::INVOKE_INTERFACE == opcode) {
+        uint32_t invoke_method_idx = mir->dalvikInsn.vB;
+        const char* invoke_method_shorty = dex_file.GetMethodShorty(
+            dex_file.GetMethodId(invoke_method_idx));
+        if (!CanCompileShorty(invoke_method_shorty, cu->instruction_set)) {
+          VLOG(compiler) << "Unsupported to invoke '"
+              << PrettyMethod(invoke_method_idx, dex_file)
+              << "' with shorty : " << invoke_method_shorty;
+          return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+void QuickCompiler::InitCompilationUnit(CompilationUnit& cu) const {
+  // Disable optimizations according to instruction set.
+  cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set];
+}
+
+void QuickCompiler::Init() const {
+  CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
+}
+
+void QuickCompiler::UnInit() const {
+  CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
+}
+
+CompiledMethod* QuickCompiler::Compile(const DexFile::CodeItem* code_item,
+                                       uint32_t access_flags,
+                                       InvokeType invoke_type,
+                                       uint16_t class_def_idx,
+                                       uint32_t method_idx,
+                                       jobject class_loader,
+                                       const DexFile& dex_file) const {
+  CompiledMethod* method = TryCompileWithSeaIR(code_item,
+                                               access_flags,
+                                               invoke_type,
+                                               class_def_idx,
+                                               method_idx,
+                                               class_loader,
+                                               dex_file);
+  if (method != nullptr) {
+    return method;
+  }
+
+  // TODO: check method fingerprint here to determine appropriate backend type.  Until then, use
+  // build default.
+  CompilerDriver* driver = GetCompilerDriver();
+  return CompileOneMethod(driver, this, code_item, access_flags, invoke_type, class_def_idx,
+                          method_idx, class_loader, dex_file, nullptr /* use thread llvm_info */);
+}
+
+CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags,
+                                          uint32_t method_idx,
+                                          const DexFile& dex_file) const {
+  return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
+}
+
+uintptr_t QuickCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
+  return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode());
+}
+
+bool QuickCompiler::WriteElf(art::File* file,
+                             OatWriter* oat_writer,
+                             const std::vector<const art::DexFile*>& dex_files,
+                             const std::string& android_root,
+                             bool is_host) const {
+  return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
+                                       *GetCompilerDriver());
+}
+
+Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+  Mir2Lir* mir_to_lir = nullptr;
+  switch (cu->instruction_set) {
+    case kThumb2:
+      mir_to_lir = ArmCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+      break;
+    case kArm64:
+      mir_to_lir = Arm64CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+      break;
+    case kMips:
+      mir_to_lir = MipsCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+      break;
+    case kX86:
+      // Fall-through.
+    case kX86_64:
+      mir_to_lir = X86CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected instruction set: " << cu->instruction_set;
+  }
+
+  /* The number of compiler temporaries depends on backend so set it up now if possible */
+  if (mir_to_lir) {
+    size_t max_temps = mir_to_lir->GetMaxPossibleCompilerTemps();
+    bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps);
+    CHECK(set_max);
+  }
+  return mir_to_lir;
+}
+
+
+Compiler* CreateQuickCompiler(CompilerDriver* driver) {
+  return new QuickCompiler(driver);
+}
+
+}  // namespace art
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
new file mode 100644
index 0000000..10de5fb
--- /dev/null
+++ b/compiler/dex/quick/quick_compiler.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
+#define ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
+
+namespace art {
+
+class Compiler;
+class CompilerDriver;
+
+Compiler* CreateQuickCompiler(CompilerDriver* driver);
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index bed86d8..6305b22 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -28,8 +28,7 @@
  * live until it is either explicitly killed or reallocated.
  */
 void Mir2Lir::ResetRegPool() {
-  GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
-  for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+  for (RegisterInfo* info : tempreg_info_) {
     info->MarkFree();
   }
   // Reset temp tracking sanity check.
@@ -66,41 +65,38 @@
                                     const ArrayRef<const RegStorage>& core64_temps,
                                     const ArrayRef<const RegStorage>& sp_temps,
                                     const ArrayRef<const RegStorage>& dp_temps) :
-    core_regs_(arena, core_regs.size()), next_core_reg_(0),
-    core64_regs_(arena, core64_regs.size()), next_core64_reg_(0),
-    sp_regs_(arena, sp_regs.size()), next_sp_reg_(0),
-    dp_regs_(arena, dp_regs.size()), next_dp_reg_(0), m2l_(m2l)  {
+    core_regs_(arena->Adapter()), next_core_reg_(0),
+    core64_regs_(arena->Adapter()), next_core64_reg_(0),
+    sp_regs_(arena->Adapter()), next_sp_reg_(0),
+    dp_regs_(arena->Adapter()), next_dp_reg_(0), m2l_(m2l)  {
   // Initialize the fast lookup map.
-  m2l_->reginfo_map_.Reset();
-  if (kIsDebugBuild) {
-    m2l_->reginfo_map_.Resize(RegStorage::kMaxRegs);
-    for (unsigned i = 0; i < RegStorage::kMaxRegs; i++) {
-      m2l_->reginfo_map_.Insert(nullptr);
-    }
-  } else {
-    m2l_->reginfo_map_.SetSize(RegStorage::kMaxRegs);
-  }
+  m2l_->reginfo_map_.clear();
+  m2l_->reginfo_map_.resize(RegStorage::kMaxRegs, nullptr);
 
   // Construct the register pool.
+  core_regs_.reserve(core_regs.size());
   for (const RegStorage& reg : core_regs) {
     RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
-    m2l_->reginfo_map_.Put(reg.GetReg(), info);
-    core_regs_.Insert(info);
+    m2l_->reginfo_map_[reg.GetReg()] = info;
+    core_regs_.push_back(info);
   }
+  core64_regs_.reserve(core64_regs.size());
   for (const RegStorage& reg : core64_regs) {
     RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
-    m2l_->reginfo_map_.Put(reg.GetReg(), info);
-    core64_regs_.Insert(info);
+    m2l_->reginfo_map_[reg.GetReg()] = info;
+    core64_regs_.push_back(info);
   }
+  sp_regs_.reserve(sp_regs.size());
   for (const RegStorage& reg : sp_regs) {
     RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
-    m2l_->reginfo_map_.Put(reg.GetReg(), info);
-    sp_regs_.Insert(info);
+    m2l_->reginfo_map_[reg.GetReg()] = info;
+    sp_regs_.push_back(info);
   }
+  dp_regs_.reserve(dp_regs.size());
   for (const RegStorage& reg : dp_regs) {
     RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
-    m2l_->reginfo_map_.Put(reg.GetReg(), info);
-    dp_regs_.Insert(info);
+    m2l_->reginfo_map_[reg.GetReg()] = info;
+    dp_regs_.push_back(info);
   }
 
   // Keep special registers from being allocated.
@@ -127,10 +123,10 @@
 
   // Add an entry for InvalidReg with zero'd mask.
   RegisterInfo* invalid_reg = new (arena) RegisterInfo(RegStorage::InvalidReg(), kEncodeNone);
-  m2l_->reginfo_map_.Put(RegStorage::InvalidReg().GetReg(), invalid_reg);
+  m2l_->reginfo_map_[RegStorage::InvalidReg().GetReg()] = invalid_reg;
 
   // Existence of core64 registers implies wide references.
-  if (core64_regs_.Size() != 0) {
+  if (core64_regs_.size() != 0) {
     ref_regs_ = &core64_regs_;
     next_ref_reg_ = &next_core64_reg_;
   } else {
@@ -139,10 +135,9 @@
   }
 }
 
-void Mir2Lir::DumpRegPool(GrowableArray<RegisterInfo*>* regs) {
+void Mir2Lir::DumpRegPool(ArenaVector<RegisterInfo*>* regs) {
   LOG(INFO) << "================================================";
-  GrowableArray<RegisterInfo*>::Iterator it(regs);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : *regs) {
     LOG(INFO) << StringPrintf(
         "R[%d:%d:%c]: T:%d, U:%d, W:%d, p:%d, LV:%d, D:%d, SR:%d, DEF:%d",
         info->GetReg().GetReg(), info->GetReg().GetRegNum(), info->GetReg().IsFloat() ?  'f' : 'c',
@@ -222,8 +217,7 @@
     if (kIsDebugBuild && s_reg == live_sreg_) {
       live_sreg_ = INVALID_SREG;
     }
-    GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
-    for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+    for (RegisterInfo* info : tempreg_info_) {
       if (info->SReg() == s_reg) {
         if (info->GetReg().NotExactlyEquals(info->Partner())) {
           // Dealing with a pair - clobber the other half.
@@ -252,20 +246,7 @@
   DCHECK_LT(s_reg, mir_graph_->GetNumSSARegs());
   DCHECK_GE(s_reg, 0);
   int v_reg = mir_graph_->SRegToVReg(s_reg);
-  if (v_reg >= 0) {
-    DCHECK_LT(v_reg, cu_->num_dalvik_registers);
-    return v_reg;
-  } else {
-    /*
-     * It must be the case that the v_reg for temporary is less than or equal to the
-     * base reg for temps. For that reason, "position" must be zero or positive.
-     */
-    unsigned int position = std::abs(v_reg) - std::abs(static_cast<int>(kVRegTempBaseReg));
-
-    // The temporaries are placed after dalvik registers in the promotion map
-    DCHECK_LT(position, mir_graph_->GetNumUsedCompilerTemps());
-    return cu_->num_dalvik_registers + position;
-  }
+  return v_reg;
 }
 
 // TODO: refactor following Alloc/Record routines - much commonality.
@@ -291,8 +272,7 @@
    * happens from the single or double pool.  This entire section of code could stand
    * a good refactoring.
    */
-  GrowableArray<RegisterInfo*>::Iterator it(&reg_pool_->core_regs_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : reg_pool_->core_regs_) {
     if (!info->IsTemp() && !info->InUse()) {
       res = info->GetReg();
       RecordCorePromotion(res, s_reg);
@@ -324,8 +304,7 @@
    */
   DCHECK_NE(cu_->instruction_set, kThumb2);
   RegStorage res;
-  GrowableArray<RegisterInfo*>::Iterator it(&reg_pool_->sp_regs_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : reg_pool_->sp_regs_) {
     if (!info->IsTemp() && !info->InUse()) {
       res = info->GetReg();
       RecordFpPromotion(res, s_reg);
@@ -350,13 +329,14 @@
 }
 
 
-RegStorage Mir2Lir::AllocTempBody(GrowableArray<RegisterInfo*> &regs, int* next_temp, bool required) {
-  int num_regs = regs.Size();
+RegStorage Mir2Lir::AllocTempBody(ArenaVector<RegisterInfo*>& regs, int* next_temp, bool required) {
+  int num_regs = regs.size();
   int next = *next_temp;
   for (int i = 0; i< num_regs; i++) {
-    if (next >= num_regs)
+    if (next >= num_regs) {
       next = 0;
-    RegisterInfo* info = regs.Get(next);
+    }
+    RegisterInfo* info = regs[next];
     // Try to allocate a register that doesn't hold a live value.
     if (info->IsTemp() && !info->InUse() && info->IsDead()) {
       // If it's wide, split it up.
@@ -380,9 +360,10 @@
   next = *next_temp;
   // No free non-live regs.  Anything we can kill?
   for (int i = 0; i< num_regs; i++) {
-    if (next >= num_regs)
+    if (next >= num_regs) {
       next = 0;
-    RegisterInfo* info = regs.Get(next);
+    }
+    RegisterInfo* info = regs[next];
     if (info->IsTemp() && !info->InUse()) {
       // Got one.  Kill it.
       ClobberSReg(info->SReg());
@@ -414,7 +395,7 @@
 
 RegStorage Mir2Lir::AllocTempWide(bool required) {
   RegStorage res;
-  if (reg_pool_->core64_regs_.Size() != 0) {
+  if (reg_pool_->core64_regs_.size() != 0) {
     res = AllocTempBody(reg_pool_->core64_regs_, &reg_pool_->next_core64_reg_, required);
   } else {
     RegStorage low_reg = AllocTemp();
@@ -471,10 +452,9 @@
   return AllocTemp(required);
 }
 
-RegStorage Mir2Lir::FindLiveReg(GrowableArray<RegisterInfo*> &regs, int s_reg) {
+RegStorage Mir2Lir::FindLiveReg(ArenaVector<RegisterInfo*>& regs, int s_reg) {
   RegStorage res;
-  GrowableArray<RegisterInfo*>::Iterator it(&regs);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : regs) {
     if ((info->SReg() == s_reg) && info->IsLive()) {
       res = info->GetReg();
       break;
@@ -727,15 +707,13 @@
 }
 
 void Mir2Lir::ResetDefTracking() {
-  GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
-  for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+  for (RegisterInfo* info : tempreg_info_) {
     info->ResetDefBody();
   }
 }
 
 void Mir2Lir::ClobberAllTemps() {
-  GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
-  for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+  for (RegisterInfo* info : tempreg_info_) {
     ClobberBody(info);
   }
 }
@@ -793,8 +771,7 @@
 }
 
 void Mir2Lir::FlushAllRegs() {
-  GrowableArray<RegisterInfo*>::Iterator it(&tempreg_info_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : tempreg_info_) {
     if (info->IsDirty() && info->IsLive()) {
       FlushSpecificReg(info);
     }
@@ -866,14 +843,16 @@
 void Mir2Lir::MarkTemp(RegStorage reg) {
   DCHECK(!reg.IsPair());
   RegisterInfo* info = GetRegInfo(reg);
-  tempreg_info_.Insert(info);
+  tempreg_info_.push_back(info);
   info->SetIsTemp(true);
 }
 
 void Mir2Lir::UnmarkTemp(RegStorage reg) {
   DCHECK(!reg.IsPair());
   RegisterInfo* info = GetRegInfo(reg);
-  tempreg_info_.Delete(info);
+  auto pos = std::find(tempreg_info_.begin(), tempreg_info_.end(), info);
+  DCHECK(pos != tempreg_info_.end());
+  tempreg_info_.erase(pos);
   info->SetIsTemp(false);
 }
 
@@ -945,8 +924,7 @@
 }
 
 bool Mir2Lir::CheckCorePoolSanity() {
-  GrowableArray<RegisterInfo*>::Iterator it(&tempreg_info_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : tempreg_info_) {
     int my_sreg = info->SReg();
     if (info->IsTemp() && info->IsLive() && info->IsWide() && my_sreg != INVALID_SREG) {
       RegStorage my_reg = info->GetReg();
@@ -1207,8 +1185,7 @@
  * optimization is disabled.
  */
 void Mir2Lir::DoPromotion() {
-  int dalvik_regs = cu_->num_dalvik_registers;
-  int num_regs = dalvik_regs + mir_graph_->GetNumUsedCompilerTemps();
+  int num_regs = mir_graph_->GetNumOfCodeAndTempVRs();
   const int promotion_threshold = 1;
   // Allocate the promotion map - one entry for each Dalvik vReg or compiler temp
   promotion_map_ = static_cast<PromotionMap*>
@@ -1237,17 +1214,10 @@
       static_cast<RefCounts *>(arena_->Alloc(sizeof(RefCounts) * fp_reg_count_size,
                                              kArenaAllocRegAlloc));
   // Set ssa names for original Dalvik registers
-  for (int i = 0; i < dalvik_regs; i++) {
+  for (int i = 0; i < num_regs; i++) {
     core_regs[i].s_reg = fp_regs[i].s_reg = i;
   }
 
-  // Set ssa names for compiler temporaries
-  for (unsigned int ct_idx = 0; ct_idx < mir_graph_->GetNumUsedCompilerTemps(); ct_idx++) {
-    CompilerTemp* ct = mir_graph_->GetCompilerTemp(ct_idx);
-    core_regs[dalvik_regs + ct_idx].s_reg = ct->s_reg_low;
-    fp_regs[dalvik_regs + ct_idx].s_reg = ct->s_reg_low;
-  }
-
   // Duplicate in upper half to represent possible wide starting sregs.
   for (size_t i = num_regs; i < fp_reg_count_size; i++) {
     fp_regs[i].s_reg = fp_regs[i - num_regs].s_reg | STARTING_WIDE_SREG;
@@ -1353,7 +1323,8 @@
 
 /* Returns sp-relative offset in bytes for a VReg */
 int Mir2Lir::VRegOffset(int v_reg) {
-  return StackVisitor::GetVRegOffset(cu_->code_item, core_spill_mask_,
+  const DexFile::CodeItem* code_item = mir_graph_->GetCurrentDexCompilationUnit()->GetCodeItem();
+  return StackVisitor::GetVRegOffset(code_item, core_spill_mask_,
                                      fp_spill_mask_, frame_size_, v_reg,
                                      cu_->instruction_set);
 }
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 8ebe55c..dce2b73 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -16,6 +16,7 @@
 
 #include "codegen_x86.h"
 #include "dex/quick/mir_to_lir-inl.h"
+#include "oat.h"
 #include "x86_lir.h"
 
 namespace art {
@@ -188,8 +189,10 @@
 
   { kX86Mov32MR, kMemReg,    IS_STORE | IS_TERTIARY_OP | REG_USE02,      { 0,             0, 0x89, 0, 0, 0, 0, 0, false }, "Mov32MR", "[!0r+!1d],!2r" },
   { kX86Mov32AR, kArrayReg,  IS_STORE | IS_QUIN_OP     | REG_USE014,     { 0,             0, 0x89, 0, 0, 0, 0, 0, false }, "Mov32AR", "[!0r+!1r<<!2d+!3d],!4r" },
+  { kX86Movnti32MR, kMemReg,    IS_STORE | IS_TERTIARY_OP | REG_USE02,   { 0,             0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti32MR", "[!0r+!1d],!2r" },
+  { kX86Movnti32AR, kArrayReg,  IS_STORE | IS_QUIN_OP     | REG_USE014,  { 0,             0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti32AR", "[!0r+!1r<<!2d+!3d],!4r" },
   { kX86Mov32TR, kThreadReg, IS_STORE | IS_BINARY_OP   | REG_USE1,       { THREAD_PREFIX, 0, 0x89, 0, 0, 0, 0, 0, false }, "Mov32TR", "fs:[!0d],!1r" },
-  { kX86Mov32RR, kRegReg,               IS_BINARY_OP   | REG_DEF0_USE1,  { 0,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RR", "!0r,!1r" },
+  { kX86Mov32RR, kRegReg,    IS_MOVE  | IS_BINARY_OP   | REG_DEF0_USE1,  { 0,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RR", "!0r,!1r" },
   { kX86Mov32RM, kRegMem,    IS_LOAD  | IS_TERTIARY_OP | REG_DEF0_USE1,  { 0,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RM", "!0r,[!1r+!2d]" },
   { kX86Mov32RA, kRegArray,  IS_LOAD  | IS_QUIN_OP     | REG_DEF0_USE12, { 0,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
   { kX86Mov32RT, kRegThread, IS_LOAD  | IS_BINARY_OP   | REG_DEF0,       { THREAD_PREFIX, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RT", "!0r,fs:[!1d]" },
@@ -198,13 +201,15 @@
   { kX86Mov32AI, kArrayImm,  IS_STORE | IS_QUIN_OP     | REG_USE01,      { 0,             0, 0xC7, 0, 0, 0, 0, 4, false }, "Mov32AI", "[!0r+!1r<<!2d+!3d],!4d" },
   { kX86Mov32TI, kThreadImm, IS_STORE | IS_BINARY_OP,                    { THREAD_PREFIX, 0, 0xC7, 0, 0, 0, 0, 4, false }, "Mov32TI", "fs:[!0d],!1d" },
 
-  { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE1,      { 0,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RM", "!0r,[!1r+!2d]" },
-  { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12,                 { 0,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+  { kX86Lea32RM, kRegMem,               IS_TERTIARY_OP | REG_DEF0_USE1,  { 0,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RM", "!0r,[!1r+!2d]" },
+  { kX86Lea32RA, kRegArray,             IS_QUIN_OP | REG_DEF0_USE12,     { 0,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
 
   { kX86Mov64MR, kMemReg,    IS_STORE | IS_TERTIARY_OP | REG_USE02,      { REX_W,             0, 0x89, 0, 0, 0, 0, 0, false }, "Mov64MR", "[!0r+!1d],!2r" },
   { kX86Mov64AR, kArrayReg,  IS_STORE | IS_QUIN_OP     | REG_USE014,     { REX_W,             0, 0x89, 0, 0, 0, 0, 0, false }, "Mov64AR", "[!0r+!1r<<!2d+!3d],!4r" },
+  { kX86Movnti64MR, kMemReg,    IS_STORE | IS_TERTIARY_OP | REG_USE02,   { REX_W,             0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti64MR", "[!0r+!1d],!2r" },
+  { kX86Movnti64AR, kArrayReg,  IS_STORE | IS_QUIN_OP     | REG_USE014,  { REX_W,             0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti64AR", "[!0r+!1r<<!2d+!3d],!4r" },
   { kX86Mov64TR, kThreadReg, IS_STORE | IS_BINARY_OP   | REG_USE1,       { THREAD_PREFIX, REX_W, 0x89, 0, 0, 0, 0, 0, false }, "Mov64TR", "fs:[!0d],!1r" },
-  { kX86Mov64RR, kRegReg,               IS_BINARY_OP   | REG_DEF0_USE1,  { REX_W,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RR", "!0r,!1r" },
+  { kX86Mov64RR, kRegReg,    IS_MOVE  | IS_BINARY_OP   | REG_DEF0_USE1,  { REX_W,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RR", "!0r,!1r" },
   { kX86Mov64RM, kRegMem,    IS_LOAD  | IS_TERTIARY_OP | REG_DEF0_USE1,  { REX_W,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RM", "!0r,[!1r+!2d]" },
   { kX86Mov64RA, kRegArray,  IS_LOAD  | IS_QUIN_OP     | REG_DEF0_USE12, { REX_W,             0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RA", "!0r,[!1r+!2r<<!3d+!4d]" },
   { kX86Mov64RT, kRegThread, IS_LOAD  | IS_BINARY_OP   | REG_DEF0,       { THREAD_PREFIX, REX_W, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RT", "!0r,fs:[!1d]" },
@@ -214,8 +219,8 @@
   { kX86Mov64AI, kArrayImm,  IS_STORE | IS_QUIN_OP     | REG_USE01,      { REX_W,             0, 0xC7, 0, 0, 0, 0, 4, false }, "Mov64AI", "[!0r+!1r<<!2d+!3d],!4d" },
   { kX86Mov64TI, kThreadImm, IS_STORE | IS_BINARY_OP,                    { THREAD_PREFIX, REX_W, 0xC7, 0, 0, 0, 0, 4, false }, "Mov64TI", "fs:[!0d],!1d" },
 
-  { kX86Lea64RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE1,      { REX_W,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RM", "!0r,[!1r+!2d]" },
-  { kX86Lea64RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12,                 { REX_W,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+  { kX86Lea64RM, kRegMem,               IS_TERTIARY_OP | REG_DEF0_USE1,  { REX_W,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RM", "!0r,[!1r+!2d]" },
+  { kX86Lea64RA, kRegArray,             IS_QUIN_OP | REG_DEF0_USE12,     { REX_W,             0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RA", "!0r,[!1r+!2r<<!3d+!4d]" },
 
   { kX86Cmov32RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, { 0,     0, 0x0F, 0x40, 0, 0, 0, 0, false }, "Cmovcc32RR", "!2c !0r,!1r" },
   { kX86Cmov64RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, { REX_W, 0, 0x0F, 0x40, 0, 0, 0, 0, false }, "Cmovcc64RR", "!2c !0r,!1r" },
@@ -263,8 +268,10 @@
 
   { kX86Cmc, kNullary, NO_OPERAND, { 0, 0, 0xF5, 0, 0, 0, 0, 0, false }, "Cmc", "" },
   { kX86Shld32RRI,  kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01  | SETS_CCODES,            { 0,    0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld32RRI", "!0r,!1r,!2d" },
+  { kX86Shld32RRC,  kShiftRegRegCl,  IS_TERTIARY_OP | REG_DEF0_USE01  | REG_USEC | SETS_CCODES, { 0,    0, 0x0F, 0xA5, 0, 0, 0, 0, false }, "Shld32RRC", "!0r,!1r,cl" },
   { kX86Shld32MRI,  kMemRegImm,      IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { 0,    0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld32MRI", "[!0r+!1d],!2r,!3d" },
   { kX86Shrd32RRI,  kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01  | SETS_CCODES,            { 0,    0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd32RRI", "!0r,!1r,!2d" },
+  { kX86Shrd32RRC,  kShiftRegRegCl,  IS_TERTIARY_OP | REG_DEF0_USE01  | REG_USEC | SETS_CCODES, { 0,    0, 0x0F, 0xAD, 0, 0, 0, 0, false }, "Shrd32RRC", "!0r,!1r,cl" },
   { kX86Shrd32MRI,  kMemRegImm,      IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { 0,    0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd32MRI", "[!0r+!1d],!2r,!3d" },
   { kX86Shld64RRI,  kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01  | SETS_CCODES,            { REX_W,    0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld64RRI", "!0r,!1r,!2d" },
   { kX86Shld64MRI,  kMemRegImm,      IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { REX_W,    0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld64MRI", "[!0r+!1d],!2r,!3d" },
@@ -383,20 +390,27 @@
   EXT_0F_ENCODING_MAP(Subss,     0xF3, 0x5C, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Divsd,     0xF2, 0x5E, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Divss,     0xF3, 0x5E, REG_DEF0_USE0),
+  EXT_0F_ENCODING_MAP(Punpcklbw, 0x66, 0x60, REG_DEF0_USE0),
+  EXT_0F_ENCODING_MAP(Punpcklwd, 0x66, 0x61, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Punpckldq, 0x66, 0x62, REG_DEF0_USE0),
+  EXT_0F_ENCODING_MAP(Punpcklqdq, 0x66, 0x6C, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Sqrtsd,    0xF2, 0x51, REG_DEF0_USE0),
   EXT_0F_ENCODING2_MAP(Pmulld,   0x66, 0x38, 0x40, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Pmullw,    0x66, 0xD5, REG_DEF0_USE0),
+  EXT_0F_ENCODING_MAP(Pmuludq,   0x66, 0xF4, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Mulps,     0x00, 0x59, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Mulpd,     0x66, 0x59, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Paddb,     0x66, 0xFC, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Paddw,     0x66, 0xFD, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Paddd,     0x66, 0xFE, REG_DEF0_USE0),
+  EXT_0F_ENCODING_MAP(Paddq,     0x66, 0xD4, REG_DEF0_USE0),
+  EXT_0F_ENCODING_MAP(Psadbw,    0x66, 0xF6, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Addps,     0x00, 0x58, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Addpd,     0xF2, 0x58, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Psubb,     0x66, 0xF8, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Psubw,     0x66, 0xF9, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Psubd,     0x66, 0xFA, REG_DEF0_USE0),
+  EXT_0F_ENCODING_MAP(Psubq,     0x66, 0xFB, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Subps,     0x00, 0x5C, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Subpd,     0x66, 0x5C, REG_DEF0_USE0),
   EXT_0F_ENCODING_MAP(Pand,      0x66, 0xDB, REG_DEF0_USE0),
@@ -425,25 +439,26 @@
   { kX86PsrlwRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x71, 0, 2, 0, 1, false }, "PsrlwRI", "!0r,!1d" },
   { kX86PsrldRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x72, 0, 2, 0, 1, false }, "PsrldRI", "!0r,!1d" },
   { kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1, false }, "PsrlqRI", "!0r,!1d" },
+  { kX86PsrldqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 3, 0, 1, false }, "PsrldqRI", "!0r,!1d" },
   { kX86PsllwRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x71, 0, 6, 0, 1, false }, "PsllwRI", "!0r,!1d" },
   { kX86PslldRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x72, 0, 6, 0, 1, false }, "PslldRI", "!0r,!1d" },
   { kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1, false }, "PsllqRI", "!0r,!1d" },
 
-  { kX86Fild32M,  kMem,     IS_LOAD    | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDB, 0x00, 0, 0, 0, 0, false }, "Fild32M",  "[!0r,!1d]" },
-  { kX86Fild64M,  kMem,     IS_LOAD    | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDF, 0x00, 0, 5, 0, 0, false }, "Fild64M",  "[!0r,!1d]" },
-  { kX86Fld32M,   kMem,     IS_LOAD    | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xD9, 0x00, 0, 0, 0, 0, false }, "Fld32M",   "[!0r,!1d]" },
-  { kX86Fld64M,   kMem,     IS_LOAD    | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDD, 0x00, 0, 0, 0, 0, false }, "Fld64M",   "[!0r,!1d]" },
-  { kX86Fstp32M,  kMem,     IS_STORE   | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xD9, 0x00, 0, 3, 0, 0, false }, "Fstps32M", "[!0r,!1d]" },
-  { kX86Fstp64M,  kMem,     IS_STORE   | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDD, 0x00, 0, 3, 0, 0, false }, "Fstpd64M", "[!0r,!1d]" },
-  { kX86Fst32M,   kMem,     IS_STORE   | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xD9, 0x00, 0, 2, 0, 0, false }, "Fsts32M",  "[!0r,!1d]" },
-  { kX86Fst64M,   kMem,     IS_STORE   | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDD, 0x00, 0, 2, 0, 0, false }, "Fstd64M",  "[!0r,!1d]" },
+  { kX86Fild32M,  kMem,     IS_LOAD    | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDB, 0x00, 0, 0, 0, 0, false }, "Fild32M",  "[!0r,!1d]" },
+  { kX86Fild64M,  kMem,     IS_LOAD    | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDF, 0x00, 0, 5, 0, 0, false }, "Fild64M",  "[!0r,!1d]" },
+  { kX86Fld32M,   kMem,     IS_LOAD    | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xD9, 0x00, 0, 0, 0, 0, false }, "Fld32M",   "[!0r,!1d]" },
+  { kX86Fld64M,   kMem,     IS_LOAD    | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDD, 0x00, 0, 0, 0, 0, false }, "Fld64M",   "[!0r,!1d]" },
+  { kX86Fstp32M,  kMem,     IS_STORE   | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xD9, 0x00, 0, 3, 0, 0, false }, "Fstps32M", "[!0r,!1d]" },
+  { kX86Fstp64M,  kMem,     IS_STORE   | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDD, 0x00, 0, 3, 0, 0, false }, "Fstpd64M", "[!0r,!1d]" },
+  { kX86Fst32M,   kMem,     IS_STORE   | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xD9, 0x00, 0, 2, 0, 0, false }, "Fsts32M",  "[!0r,!1d]" },
+  { kX86Fst64M,   kMem,     IS_STORE   | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0,  0,    0xDD, 0x00, 0, 2, 0, 0, false }, "Fstd64M",  "[!0r,!1d]" },
   { kX86Fprem,    kNullary, NO_OPERAND | USE_FP_STACK,                          { 0xD9, 0,    0xF8, 0,    0, 0, 0, 0, false }, "Fprem64",  "" },
   { kX86Fucompp,  kNullary, NO_OPERAND | USE_FP_STACK,                          { 0xDA, 0,    0xE9, 0,    0, 0, 0, 0, false }, "Fucompp",  "" },
   { kX86Fstsw16R, kNullary, NO_OPERAND | REG_DEFA | USE_FP_STACK,               { 0x9B, 0xDF, 0xE0, 0,    0, 0, 0, 0, false }, "Fstsw16R", "ax" },
 
-  EXT_0F_ENCODING_MAP(Mova128,    0x66, 0x6F, REG_DEF0),
-  { kX86Mova128MR, kMemReg,   IS_STORE | IS_TERTIARY_OP | REG_USE02,  { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "Mova128MR", "[!0r+!1d],!2r" },
-  { kX86Mova128AR, kArrayReg, IS_STORE | IS_QUIN_OP     | REG_USE014, { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "Mova128AR", "[!0r+!1r<<!2d+!3d],!4r" },
+  EXT_0F_ENCODING_MAP(Movdqa,    0x66, 0x6F, REG_DEF0),
+  { kX86MovdqaMR, kMemReg,   IS_STORE | IS_TERTIARY_OP | REG_USE02,  { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "MovdqaMR", "[!0r+!1d],!2r" },
+  { kX86MovdqaAR, kArrayReg, IS_STORE | IS_QUIN_OP     | REG_USE014, { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "MovdqaAR", "[!0r+!1r<<!2d+!3d],!4r" },
 
 
   EXT_0F_ENCODING_MAP(Movups,    0x0, 0x10, REG_DEF0),
@@ -484,7 +499,9 @@
 
   // TODO: load/store?
   // Encode the modrm opcode as an extra opcode byte to avoid computation during assembly.
+  { kX86Lfence, kReg,                 NO_OPERAND,     { 0, 0, 0x0F, 0xAE, 0, 5, 0, 0, false }, "Lfence", "" },
   { kX86Mfence, kReg,                 NO_OPERAND,     { 0, 0, 0x0F, 0xAE, 0, 6, 0, 0, false }, "Mfence", "" },
+  { kX86Sfence, kReg,                 NO_OPERAND,     { 0, 0, 0x0F, 0xAE, 0, 7, 0, 0, false }, "Sfence", "" },
 
   EXT_0F_ENCODING_MAP(Imul16,  0x66, 0xAF, REG_USE0 | REG_DEF0 | SETS_CCODES),
   EXT_0F_ENCODING_MAP(Imul32,  0x00, 0xAF, REG_USE0 | REG_DEF0 | SETS_CCODES),
@@ -526,7 +543,7 @@
 
   { kX86StartOfMethod, kMacro,  IS_UNARY_OP | SETS_CCODES,             { 0, 0, 0,    0, 0, 0, 0, 0, false }, "StartOfMethod", "!0r" },
   { kX86PcRelLoadRA,   kPcRel,  IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0, false }, "PcRelLoadRA",   "!0r,[!1r+!2r<<!3d+!4p]" },
-  { kX86PcRelAdr,      kPcRel,  IS_LOAD | IS_BINARY_OP | REG_DEF0,     { 0, 0, 0xB8, 0, 0, 0, 0, 4, false }, "PcRelAdr",      "!0r,!1d" },
+  { kX86PcRelAdr,      kPcRel,  IS_LOAD | IS_BINARY_OP | REG_DEF0,     { 0, 0, 0xB8, 0, 0, 0, 0, 4, false }, "PcRelAdr",      "!0r,!1p" },
   { kX86RepneScasw,    kNullary, NO_OPERAND | REG_USEA | REG_USEC | SETS_CCODES, { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0, false }, "RepNE ScasW", "" },
 };
 
@@ -563,7 +580,7 @@
         case kX86CallA: return true;
         default: return false;
       }
-    case kPcRel: return true;
+    case kPcRel:
        switch (entry->opcode) {
          case kX86PcRelLoadRA: return true;
          default: return false;
@@ -591,6 +608,7 @@
     case kShiftRegCl: return true;
     case kRegCond: return true;
     case kRegRegCond: return true;
+    case kShiftRegRegCl: return true;
     case kJmp:
       switch (entry->opcode) {
         case kX86JmpR: return true;
@@ -662,7 +680,8 @@
     }
     if (displacement != 0 || LowRegisterBits(raw_base) == rs_rBP.GetRegNum()) {
       // BP requires an explicit displacement, even when it's 0.
-      if (entry->opcode != kX86Lea32RA && entry->opcode != kX86Lea64RA) {
+      if (entry->opcode != kX86Lea32RA && entry->opcode != kX86Lea64RA &&
+          entry->opcode != kX86Lea32RM && entry->opcode != kX86Lea64RM) {
         DCHECK_NE(entry->flags & (IS_LOAD | IS_STORE), UINT64_C(0)) << entry->name;
       }
       size += IS_SIMM8(displacement) ? 1 : 4;
@@ -768,6 +787,9 @@
       DCHECK_EQ(rs_rCX.GetRegNum(), RegStorage::RegNum(lir->operands[4]));
       return ComputeSize(entry, lir->operands[4], lir->operands[1], lir->operands[0],
                          lir->operands[3]);
+    case kShiftRegRegCl:  // lir operands - 0: reg1, 1: reg2, 2: cl
+      DCHECK_EQ(rs_rCX.GetRegNum(), RegStorage::RegNum(lir->operands[2]));
+      return ComputeSize(entry, lir->operands[0], NO_REG, lir->operands[1], 0);
     case kRegCond:  // lir operands - 0: reg, 1: cond
       return ComputeSize(entry, NO_REG, NO_REG, lir->operands[0], 0);
     case kMemCond:  // lir operands - 0: base, 1: disp, 2: cond
@@ -895,22 +917,22 @@
   if (r8_form) {
     // Do we need an empty REX prefix to normalize byte register addressing?
     if (RegStorage::RegNum(raw_reg_r) >= 4 && !IsByteSecondOperand(entry)) {
-      rex |= 0x40;  // REX.0000
+      rex |= REX;  // REX.0000
     } else if (modrm_is_reg_reg && RegStorage::RegNum(raw_reg_b) >= 4) {
-      rex |= 0x40;  // REX.0000
+      rex |= REX;  // REX.0000
     }
   }
   if (w) {
-    rex |= 0x48;  // REX.W000
+    rex |= REX_W;  // REX.W000
   }
   if (r) {
-    rex |= 0x44;  // REX.0R00
+    rex |= REX_R;  // REX.0R00
   }
   if (x) {
-    rex |= 0x42;  // REX.00X0
+    rex |= REX_X;  // REX.00X0
   }
   if (b) {
-    rex |= 0x41;  // REX.000B
+    rex |= REX_B;  // REX.000B
   }
   if (entry->skeleton.prefix1 != 0) {
     if (cu_->target64 && entry->skeleton.prefix1 == THREAD_PREFIX) {
@@ -1336,6 +1358,19 @@
   DCHECK_EQ(0, entry->skeleton.immediate_bytes);
 }
 
+void X86Mir2Lir::EmitShiftRegRegCl(const X86EncodingMap* entry, int32_t raw_reg1, int32_t raw_reg2, int32_t raw_cl) {
+  DCHECK_EQ(false, entry->skeleton.r8_form);
+  DCHECK_EQ(rs_rCX.GetRegNum(), RegStorage::RegNum(raw_cl));
+  EmitPrefixAndOpcode(entry, raw_reg1, NO_REG, raw_reg2);
+  uint8_t low_reg1 = LowRegisterBits(raw_reg1);
+  uint8_t low_reg2 = LowRegisterBits(raw_reg2);
+  uint8_t modrm = (3 << 6) | (low_reg1 << 3) | low_reg2;
+  code_buffer_.push_back(modrm);
+  DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+  DCHECK_EQ(0, entry->skeleton.ax_opcode);
+  DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
 void X86Mir2Lir::EmitShiftMemImm(const X86EncodingMap* entry, int32_t raw_base, int32_t disp,
                                  int32_t imm) {
   DCHECK_EQ(false, entry->skeleton.r8_form);
@@ -1829,6 +1864,9 @@
       case kShiftMemCl:  // lir operands - 0: base, 1:displacement, 2: cl
         EmitShiftMemCl(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
         break;
+      case kShiftRegRegCl:  // lir operands - 0: reg1, 1: reg2, 2: cl
+        EmitShiftRegRegCl(entry, lir->operands[1], lir->operands[0], lir->operands[2]);
+        break;
       case kRegCond:  // lir operands - 0: reg, 1: condition
         EmitRegCond(entry, lir->operands[0], lir->operands[1]);
         break;
@@ -1928,17 +1966,12 @@
   int offset = AssignInsnOffsets();
 
   if (const_vectors_ != nullptr) {
-    /* assign offsets to vector literals */
-
-    // First, get offset to 12 mod 16 to align to 16 byte boundary.
-    // This will ensure that the vector is 16 byte aligned, as the procedure is
-    // always aligned at at 4 mod 16.
-    int align_size = (16-4) - (offset & 0xF);
-    if (align_size < 0) {
-      align_size += 16;
-    }
-
-    offset += align_size;
+    // Vector literals must be 16-byte aligned. The header that is placed
+    // in the code section causes misalignment so we take it into account.
+    // Otherwise, we are sure that for x86 method is aligned to 16.
+    DCHECK_EQ(GetInstructionSetAlignment(cu_->instruction_set), 16u);
+    uint32_t bytes_to_fill = (0x10 - ((offset + sizeof(OatQuickMethodHeader)) & 0xF)) & 0xF;
+    offset += bytes_to_fill;
 
     // Now assign each literal the right offset.
     for (LIR *p = const_vectors_; p != nullptr; p = p->next) {
diff --git a/compiler/dex/quick/x86/backend_x86.h b/compiler/dex/quick/x86/backend_x86.h
new file mode 100644
index 0000000..f73db94
--- /dev/null
+++ b/compiler/dex/quick/x86/backend_x86.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEX_QUICK_X86_BACKEND_X86_H_
+#define ART_COMPILER_DEX_QUICK_X86_BACKEND_X86_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+                          ArenaAllocator* const arena);
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_QUICK_X86_BACKEND_X86_H_
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 996689a..441ec9e 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -19,6 +19,8 @@
 #include "codegen_x86.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "gc/accounting/card_table.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
 #include "x86_lir.h"
 
 namespace art {
@@ -28,7 +30,7 @@
  * pairs.
  */
 void X86Mir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpSparseSwitchTable(table);
   }
@@ -61,7 +63,7 @@
  * done:
  */
 void X86Mir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   if (cu_->verbose) {
     DumpPackedSwitchTable(table);
   }
@@ -73,7 +75,7 @@
   int size = table[1];
   tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
                                                       kArenaAllocLIR));
-  switch_tables_.Insert(tab_rec);
+  switch_tables_.push_back(tab_rec);
 
   // Get the switch value
   rl_src = LoadValue(rl_src, kCoreReg);
@@ -134,8 +136,8 @@
  *
  * Total size is 4+(width * size + 1)/2 16-bit code units.
  */
-void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void X86Mir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   // Add the table to the list - we'll process it later
   FillArrayData* tab_rec =
       static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
@@ -145,7 +147,7 @@
   uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
   tab_rec->size = (size * width) + 8;
 
-  fill_array_data_.Insert(tab_rec);
+  fill_array_data_.push_back(tab_rec);
 
   // Making a call - use explicit registers
   FlushAllRegs();   /* Everything to home location */
@@ -330,4 +332,58 @@
   MarkPossibleNullPointerException(opt_flags);
 }
 
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+                             int state, const MethodReference& target_method,
+                             uint32_t unused,
+                             uintptr_t direct_code, uintptr_t direct_method,
+                             InvokeType type) {
+  Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+  if (direct_method != 0) {
+    switch (state) {
+    case 0:  // Get the current Method* [sets kArg0]
+      if (direct_method != static_cast<uintptr_t>(-1)) {
+        cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
+      } else {
+        cg->LoadMethodAddress(target_method, type, kArg0);
+      }
+      break;
+    default:
+      return -1;
+    }
+  } else {
+    RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+    switch (state) {
+    case 0:  // Get the current Method* [sets kArg0]
+      // TUNING: we can save a reg copy if Method* has been promoted.
+      cg->LoadCurrMethodDirect(arg0_ref);
+      break;
+    case 1:  // Get method->dex_cache_resolved_methods_
+      cg->LoadRefDisp(arg0_ref,
+                      mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+                      arg0_ref,
+                      kNotVolatile);
+      break;
+    case 2:  // Grab target method*
+      CHECK_EQ(cu->dex_file, target_method.dex_file);
+      cg->LoadRefDisp(arg0_ref,
+                      mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+                          target_method.dex_method_index).Int32Value(),
+                      arg0_ref,
+                      kNotVolatile);
+      break;
+    default:
+      return -1;
+    }
+  }
+  return state + 1;
+}
+
+NextCallInsn X86Mir2Lir::GetNextSDCallInsn() {
+  return X86NextSDCallInsn;
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index d74caae..8edfc01 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -18,9 +18,11 @@
 #define ART_COMPILER_DEX_QUICK_X86_CODEGEN_X86_H_
 
 #include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
 #include "x86_lir.h"
 
 #include <map>
+#include <vector>
 
 namespace art {
 
@@ -60,6 +62,15 @@
     bool initialized_;
   };
 
+  class ExplicitTempRegisterLock {
+  public:
+    ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir, int n_regs, ...);
+    ~ExplicitTempRegisterLock();
+  protected:
+    std::vector<RegStorage> temp_regs_;
+    X86Mir2Lir* const mir_to_lir_;
+  };
+
  public:
   X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
@@ -124,7 +135,7 @@
 
   void CompilerInitializeRegAlloc() OVERRIDE;
   int VectorRegisterSize() OVERRIDE;
-  int NumReservableVectorRegisters(bool fp_used) OVERRIDE;
+  int NumReservableVectorRegisters(bool long_or_fp) OVERRIDE;
 
   // Required for target - miscellaneous.
   void AssembleLIR() OVERRIDE;
@@ -159,6 +170,7 @@
   bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object) OVERRIDE;
   bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) OVERRIDE;
   bool GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) OVERRIDE;
+  bool GenInlinedReverseBits(CallInfo* info, OpSize size) OVERRIDE;
   bool GenInlinedSqrt(CallInfo* info) OVERRIDE;
   bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
   bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
@@ -233,7 +245,7 @@
   void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
   void GenExitSequence() OVERRIDE;
   void GenSpecialExitSequence() OVERRIDE;
-  void GenFillArrayData(DexOffset table_offset, RegLocation rl_src) OVERRIDE;
+  void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
   void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) OVERRIDE;
   void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) OVERRIDE;
   void GenSelect(BasicBlock* bb, MIR* mir) OVERRIDE;
@@ -319,14 +331,17 @@
 
   /*
    * @brief Load the Class* of a Dex Class type into the register.
+   * @param dex DexFile that contains the class type.
    * @param type How the method will be invoked.
    * @param register that will contain the code address.
    * @note register will be passed to TargetReg to get physical register.
    */
-  void LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) OVERRIDE;
+  void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+                     SpecialTargetRegister symbolic_reg) OVERRIDE;
 
   void FlushIns(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
 
+  NextCallInsn GetNextSDCallInsn() OVERRIDE;
   int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
                            NextCallInsn next_call_insn,
                            const MethodReference& target_method,
@@ -347,7 +362,14 @@
    * @param type How the method will be invoked.
    * @returns Call instruction
    */
-  virtual LIR * CallWithLinkerFixup(const MethodReference& target_method, InvokeType type);
+  LIR* CallWithLinkerFixup(const MethodReference& target_method, InvokeType type);
+
+  /*
+   * @brief Generate the actual call insn based on the method info.
+   * @param method_info the lowering info for the method call.
+   * @returns Call instruction
+   */
+  LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
 
   /*
    * @brief Handle x86 specific literals
@@ -355,16 +377,10 @@
   void InstallLiteralPools() OVERRIDE;
 
   /*
-   * @brief Generate the debug_frame CFI information.
-   * @returns pointer to vector containing CFE information
-   */
-  static std::vector<uint8_t>* ReturnCommonCallFrameInformation(bool is_x86_64);
-
-  /*
    * @brief Generate the debug_frame FDE information.
    * @returns pointer to vector containing CFE information
    */
-  std::vector<uint8_t>* ReturnCallFrameInformation() OVERRIDE;
+  std::vector<uint8_t>* ReturnFrameDescriptionEntry() OVERRIDE;
 
   LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
 
@@ -410,7 +426,7 @@
   LIR* LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement,
                            RegStorage r_dest, OpSize size);
   LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement,
-                            RegStorage r_src, OpSize size);
+                            RegStorage r_src, OpSize size, int opt_flags = 0);
 
   RegStorage GetCoreArgMappingToPhysicalReg(int core_arg_num);
 
@@ -460,6 +476,8 @@
   void EmitShiftRegImm(const X86EncodingMap* entry, int32_t raw_reg, int32_t imm);
   void EmitShiftRegCl(const X86EncodingMap* entry, int32_t raw_reg, int32_t raw_cl);
   void EmitShiftMemCl(const X86EncodingMap* entry, int32_t raw_base, int32_t disp, int32_t raw_cl);
+  void EmitShiftRegRegCl(const X86EncodingMap* entry, int32_t raw_reg1, int32_t raw_reg2,
+                         int32_t raw_cl);
   void EmitShiftMemImm(const X86EncodingMap* entry, int32_t raw_base, int32_t disp, int32_t imm);
   void EmitRegCond(const X86EncodingMap* entry, int32_t raw_reg, int32_t cc);
   void EmitMemCond(const X86EncodingMap* entry, int32_t raw_base, int32_t disp, int32_t cc);
@@ -479,11 +497,16 @@
   void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
                                 int64_t val, ConditionCode ccode);
   void GenConstWide(RegLocation rl_dest, int64_t value);
-  void GenMultiplyVectorSignedByte(BasicBlock *bb, MIR *mir);
+  void GenMultiplyVectorSignedByte(RegStorage rs_dest_src1, RegStorage rs_src2);
+  void GenMultiplyVectorLong(RegStorage rs_dest_src1, RegStorage rs_src2);
   void GenShiftByteVector(BasicBlock *bb, MIR *mir);
-  void AndMaskVectorRegister(RegStorage rs_src1, uint32_t m1, uint32_t m2, uint32_t m3, uint32_t m4);
-  void MaskVectorRegister(X86OpCode opcode, RegStorage rs_src1, uint32_t m1, uint32_t m2, uint32_t m3, uint32_t m4);
+  void AndMaskVectorRegister(RegStorage rs_src1, uint32_t m1, uint32_t m2, uint32_t m3,
+                             uint32_t m4);
+  void MaskVectorRegister(X86OpCode opcode, RegStorage rs_src1, uint32_t m1, uint32_t m2,
+                          uint32_t m3, uint32_t m4);
   void AppendOpcodeWithConst(X86OpCode opcode, int reg, MIR* mir);
+  virtual void LoadVectorRegister(RegStorage rs_dest, RegStorage rs_src, OpSize opsize,
+                                  int op_mov);
 
   static bool ProvidesFullMemoryBarrier(X86OpCode opcode);
 
@@ -519,20 +542,18 @@
   bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
 
   /**
-   * @brief Reserve a fixed number of vector  registers from the register pool
-   * @details The mir->dalvikInsn.vA specifies an N such that vector registers
-   * [0..N-1] are removed from the temporary pool. The caller must call
-   * ReturnVectorRegisters before calling ReserveVectorRegisters again.
-   * Also sets the num_reserved_vector_regs_ to the specified value
-   * @param mir whose vA specifies the number of registers to reserve
+   * @brief Used to reserve a range of vector registers.
+   * @see kMirOpReserveVectorRegisters
+   * @param mir The extended MIR for reservation.
    */
   void ReserveVectorRegisters(MIR* mir);
 
   /**
-   * @brief Return all the reserved vector registers to the temp pool
-   * @details Returns [0..num_reserved_vector_regs_]
+   * @brief Used to return a range of vector registers.
+   * @see kMirOpReturnVectorRegisters
+   * @param mir The extended MIR for returning vector regs.
    */
-  void ReturnVectorRegisters();
+  void ReturnVectorRegisters(MIR* mir);
 
   /*
    * @brief Load 128 bit constant into vector register.
@@ -554,7 +575,8 @@
   void GenMoveVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed multiply of units in two vector registers: vB = vB .* @note vC using vA to know the type of the vector.
+   * @brief Packed multiply of units in two vector registers: vB = vB .* @note vC using vA to know
+   * the type of the vector.
    * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -564,7 +586,8 @@
   void GenMultiplyVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed addition of units in two vector registers: vB = vB .+ vC using vA to know the type of the vector.
+   * @brief Packed addition of units in two vector registers: vB = vB .+ vC using vA to know the
+   * type of the vector.
    * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -574,7 +597,8 @@
   void GenAddVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed subtraction of units in two vector registers: vB = vB .- vC using vA to know the type of the vector.
+   * @brief Packed subtraction of units in two vector registers: vB = vB .- vC using vA to know the
+   * type of the vector.
    * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -584,7 +608,8 @@
   void GenSubtractVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed shift left of units in two vector registers: vB = vB .<< vC using vA to know the type of the vector.
+   * @brief Packed shift left of units in two vector registers: vB = vB .<< vC using vA to know the
+   * type of the vector.
    * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -594,7 +619,8 @@
   void GenShiftLeftVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed signed shift right of units in two vector registers: vB = vB .>> vC using vA to know the type of the vector.
+   * @brief Packed signed shift right of units in two vector registers: vB = vB .>> vC using vA to
+   * know the type of the vector.
    * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -604,7 +630,8 @@
   void GenSignedShiftRightVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed unsigned shift right of units in two vector registers: vB = vB .>>> vC using vA to know the type of the vector.
+   * @brief Packed unsigned shift right of units in two vector registers: vB = vB .>>> vC using vA
+   * to know the type of the vector.
    * @param bb The basic block in which the MIR is from..
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -614,7 +641,8 @@
   void GenUnsignedShiftRightVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed bitwise and of units in two vector registers: vB = vB .& vC using vA to know the type of the vector.
+   * @brief Packed bitwise and of units in two vector registers: vB = vB .& vC using vA to know the
+   * type of the vector.
    * @note vA: TypeSize
    * @note vB: destination and source
    * @note vC: source
@@ -622,7 +650,8 @@
   void GenAndVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed bitwise or of units in two vector registers: vB = vB .| vC using vA to know the type of the vector.
+   * @brief Packed bitwise or of units in two vector registers: vB = vB .| vC using vA to know the
+   * type of the vector.
    * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -632,7 +661,8 @@
   void GenOrVector(BasicBlock *bb, MIR *mir);
 
   /*
-   * @brief Packed bitwise xor of units in two vector registers: vB = vB .^ vC using vA to know the type of the vector.
+   * @brief Packed bitwise xor of units in two vector registers: vB = vB .^ vC using vA to know the
+   * type of the vector.
    * @param bb The basic block in which the MIR is from.
    * @param mir The MIR whose opcode is kMirConstVector.
    * @note vA: TypeSize
@@ -673,6 +703,20 @@
    */
   void GenSetVector(BasicBlock *bb, MIR *mir);
 
+  /**
+   * @brief Used to generate code for kMirOpPackedArrayGet.
+   * @param bb The basic block of MIR.
+   * @param mir The mir whose opcode is kMirOpPackedArrayGet.
+   */
+  void GenPackedArrayGet(BasicBlock *bb, MIR *mir);
+
+  /**
+   * @brief Used to generate code for kMirOpPackedArrayPut.
+   * @param bb The basic block of MIR.
+   * @param mir The mir whose opcode is kMirOpPackedArrayPut.
+   */
+  void GenPackedArrayPut(BasicBlock *bb, MIR *mir);
+
   /*
    * @brief Generate code for a vector opcode.
    * @param bb The basic block in which the MIR is from.
@@ -880,7 +924,7 @@
    * @param bb Basic block containing instruction.
    * @param mir Instruction to analyze.
    */
-  void AnalyzeFPInstruction(int opcode, BasicBlock * bb, MIR *mir);
+  virtual void AnalyzeFPInstruction(int opcode, BasicBlock * bb, MIR *mir);
 
   /*
    * @brief Analyze one use of a double operand.
@@ -911,13 +955,13 @@
   LIR* setup_method_address_[2];
 
   // Instructions needing patching with Method* values.
-  GrowableArray<LIR*> method_address_insns_;
+  ArenaVector<LIR*> method_address_insns_;
 
   // Instructions needing patching with Class Type* values.
-  GrowableArray<LIR*> class_type_address_insns_;
+  ArenaVector<LIR*> class_type_address_insns_;
 
   // Instructions needing patching with PC relative code addresses.
-  GrowableArray<LIR*> call_method_insns_;
+  ArenaVector<LIR*> call_method_insns_;
 
   // Prologue decrement of stack pointer.
   LIR* stack_decrement_;
@@ -926,20 +970,20 @@
   LIR* stack_increment_;
 
   // The list of const vector literals.
-  LIR *const_vectors_;
+  LIR* const_vectors_;
 
   /*
    * @brief Search for a matching vector literal
-   * @param mir A kMirOpConst128b MIR instruction to match.
+   * @param constants An array of size 4 which contains all of 32-bit constants.
    * @returns pointer to matching LIR constant, or nullptr if not found.
    */
-  LIR *ScanVectorLiteral(MIR *mir);
+  LIR* ScanVectorLiteral(int32_t* constants);
 
   /*
    * @brief Add a constant vector literal
-   * @param mir A kMirOpConst128b MIR instruction to match.
+   * @param constants An array of size 4 which contains all of 32-bit constants.
    */
-  LIR *AddVectorLiteral(MIR *mir);
+  LIR* AddVectorLiteral(int32_t* constants);
 
   InToRegStorageMapping in_to_reg_storage_mapping_;
 
@@ -959,8 +1003,8 @@
   static const X86EncodingMap EncodingMap[kX86Last];
 
  private:
-  // The number of vector registers [0..N] reserved by a call to ReserveVectorRegisters
-  int num_reserved_vector_regs_;
+  void SwapBits(RegStorage result_reg, int shift, int32_t value);
+  void SwapBits64(RegStorage result_reg, int shift, int64_t value);
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index 2920fb6..21d1a5c 100755
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -730,6 +730,25 @@
     // Handle NaN.
     branch_nan->target = NewLIR0(kPseudoTargetLabel);
     LoadConstantWide(rl_result.reg, INT64_C(0x7ff8000000000000));
+
+    // The base_of_code_ compiler temp is non-null when it is reserved
+    // for being able to do data accesses relative to method start.
+    if (base_of_code_ != nullptr) {
+      // Loading from the constant pool may have used base of code register.
+      // However, the code here generates logic in diamond shape and not all
+      // paths load base of code register. Therefore, we ensure it is clobbered so
+      // that the temp caching system does not believe it is live at merge point.
+      RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+      if (rl_method.wide) {
+        rl_method = UpdateLocWide(rl_method);
+      } else {
+        rl_method = UpdateLoc(rl_method);
+      }
+      if (rl_method.location == kLocPhysReg) {
+        Clobber(rl_method.reg);
+      }
+    }
+
     LIR* branch_exit_nan = NewLIR1(kX86Jmp8, 0);
     // Handle Min/Max. Copy greater/lesser value from src2.
     branch_cond1->target = NewLIR0(kPseudoTargetLabel);
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 00a2621..4357657 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -49,8 +49,8 @@
     return;
   }
 
-  FlushAllRegs();
-  LockCallTemps();  // Prepare for explicit register usage
+  // Prepare for explicit register usage
+  ExplicitTempRegisterLock(this, 4, &rs_r0, &rs_r1, &rs_r2, &rs_r3);
   RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_r0, rs_r1);
   RegStorage r_tmp2 = RegStorage::MakeRegPair(rs_r2, rs_r3);
   LoadValueDirectWideFixed(rl_src1, r_tmp1);
@@ -407,8 +407,8 @@
     return;
   }
 
-  FlushAllRegs();
-  LockCallTemps();  // Prepare for explicit register usage
+  // Prepare for explicit register usage
+  ExplicitTempRegisterLock(this, 4, &rs_r0, &rs_r1, &rs_r2, &rs_r3);
   RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_r0, rs_r1);
   RegStorage r_tmp2 = RegStorage::MakeRegPair(rs_r2, rs_r3);
   LoadValueDirectWideFixed(rl_src1, r_tmp1);
@@ -616,6 +616,12 @@
     rl_result = EvalLoc(rl_dest, kCoreReg, true);
     if (is_div) {
       LoadValueDirectFixed(rl_src, rl_result.reg);
+
+      // Check if numerator is 0
+      OpRegImm(kOpCmp, rl_result.reg, 0);
+      LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
+
+      // handle 0x80000000 / -1
       OpRegImm(kOpCmp, rl_result.reg, 0x80000000);
       LIR *minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
 
@@ -624,6 +630,7 @@
 
       // EAX already contains the right value (0x80000000),
       minint_branch->target = NewLIR0(kPseudoTargetLabel);
+      branch->target = NewLIR0(kPseudoTargetLabel);
     } else {
       // x % -1 == 0.
       LoadConstantNoClobber(rl_result.reg, 0);
@@ -636,6 +643,14 @@
       RegStorage rs_temp = AllocTypedTemp(false, kCoreReg);
       rl_result.reg.SetReg(rs_temp.GetReg());
     }
+
+    // Check if numerator is 0
+    OpRegImm(kOpCmp, rl_src.reg, 0);
+    LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+    LoadConstantNoClobber(rl_result.reg, 0);
+    LIR* done = NewLIR1(kX86Jmp8, 0);
+    branch->target = NewLIR0(kPseudoTargetLabel);
+
     NewLIR3(kX86Lea32RM, rl_result.reg.GetReg(), rl_src.reg.GetReg(), std::abs(imm) - 1);
     NewLIR2(kX86Test32RR, rl_src.reg.GetReg(), rl_src.reg.GetReg());
     OpCondRegReg(kOpCmov, kCondPl, rl_result.reg, rl_src.reg);
@@ -644,6 +659,7 @@
     if (imm < 0) {
       OpReg(kOpNeg, rl_result.reg);
     }
+    done->target = NewLIR0(kPseudoTargetLabel);
   } else {
     CHECK(imm <= -2 || imm >= 2);
 
@@ -676,26 +692,27 @@
     Clobber(rs_r2);
     LockTemp(rs_r2);
 
-    // Assume that the result will be in EDX.
-    rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r2, INVALID_SREG, INVALID_SREG};
+    // Assume that the result will be in EDX for divide, and EAX for remainder.
+    rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, is_div ? rs_r2 : rs_r0,
+                 INVALID_SREG, INVALID_SREG};
 
-    // Numerator into EAX.
-    RegStorage numerator_reg;
-    if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
-      // We will need the value later.
-      rl_src = LoadValue(rl_src, kCoreReg);
-      numerator_reg = rl_src.reg;
-      OpRegCopy(rs_r0, numerator_reg);
-    } else {
-      // Only need this once.  Just put it into EAX.
-      LoadValueDirectFixed(rl_src, rs_r0);
-    }
+    // We need the value at least twice.  Load into a temp.
+    rl_src = LoadValue(rl_src, kCoreReg);
+    RegStorage numerator_reg = rl_src.reg;
 
-    // EDX = magic.
-    LoadConstantNoClobber(rs_r2, magic);
+    // Check if numerator is 0.
+    OpRegImm(kOpCmp, numerator_reg, 0);
+    LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+    // Return result 0 if numerator was 0.
+    LoadConstantNoClobber(rl_result.reg, 0);
+    LIR* done = NewLIR1(kX86Jmp8, 0);
+    branch->target = NewLIR0(kPseudoTargetLabel);
 
-    // EDX:EAX = magic & dividend.
-    NewLIR1(kX86Imul32DaR, rs_r2.GetReg());
+    // EAX = magic.
+    LoadConstant(rs_r0, magic);
+
+    // EDX:EAX = magic * numerator.
+    NewLIR1(kX86Imul32DaR, numerator_reg.GetReg());
 
     if (imm > 0 && magic < 0) {
       // Add numerator to EDX.
@@ -733,12 +750,12 @@
       // EAX = numerator * imm.
       OpRegRegImm(kOpMul, rs_r2, rs_r2, imm);
 
-      // EDX -= EAX.
+      // EAX -= EDX.
       NewLIR2(kX86Sub32RR, rs_r0.GetReg(), rs_r2.GetReg());
 
       // For this case, return the result in EAX.
-      rl_result.reg.SetReg(r0);
     }
+    done->target = NewLIR0(kPseudoTargetLabel);
   }
 
   return rl_result;
@@ -753,8 +770,9 @@
 RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
                                   RegLocation rl_src2, bool is_div, bool check_zero) {
   // We have to use fixed registers, so flush all the temps.
-  FlushAllRegs();
-  LockCallTemps();  // Prepare for explicit register usage.
+
+  // Prepare for explicit register usage.
+  ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
 
   // Load LHS into EAX.
   LoadValueDirectFixed(rl_src1, rs_r0);
@@ -770,13 +788,19 @@
     GenDivZeroCheck(rs_r1);
   }
 
+  // Check if numerator is 0
+  OpRegImm(kOpCmp, rs_r0, 0);
+  LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
+
   // Have to catch 0x80000000/-1 case, or we will get an exception!
   OpRegImm(kOpCmp, rs_r1, -1);
-  LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+  LIR* minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
 
   // RHS is -1.
   OpRegImm(kOpCmp, rs_r0, 0x80000000);
-  LIR * minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+  LIR* minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+
+  branch->target = NewLIR0(kPseudoTargetLabel);
 
   // In 0x80000000/-1 case.
   if (!is_div) {
@@ -802,8 +826,115 @@
 bool X86Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
   DCHECK(cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64);
 
-  if (is_long && cu_->instruction_set == kX86) {
-    return false;
+  if (is_long && !cu_->target64) {
+   /*
+    * We want to implement the following algorithm
+    * mov eax, low part of arg1
+    * mov edx, high part of arg1
+    * mov ebx, low part of arg2
+    * mov ecx, high part of arg2
+    * mov edi, eax
+    * sub edi, ebx
+    * mov edi, edx
+    * sbb edi, ecx
+    * is_min ? "cmovgel eax, ebx" : "cmovll eax, ebx"
+    * is_min ? "cmovgel edx, ecx" : "cmovll edx, ecx"
+    *
+    * The algorithm above needs 5 registers: a pair for the first operand
+    * (which later will be used as result), a pair for the second operand
+    * and a temp register (e.g. 'edi') for intermediate calculations.
+    * Ideally we have 6 GP caller-save registers in 32-bit mode. They are:
+    * 'eax', 'ebx', 'ecx', 'edx', 'esi' and 'edi'. So there should be
+    * always enough registers to operate on. Practically, there is a pair
+    * of registers 'edi' and 'esi' which holds promoted values and
+    * sometimes should be treated as 'callee save'. If one of the operands
+    * is in the promoted registers then we have enough register to
+    * operate on. Otherwise there is lack of resources and we have to
+    * save 'edi' before calculations and restore after.
+    */
+
+    RegLocation rl_src1 = info->args[0];
+    RegLocation rl_src2 = info->args[2];
+    RegLocation rl_dest = InlineTargetWide(info);
+    int res_vreg, src1_vreg, src2_vreg;
+
+    /*
+     * If the result register is the same as the second element, then we
+     * need to be careful. The reason is that the first copy will
+     * inadvertently clobber the second element with the first one thus
+     * yielding the wrong result. Thus we do a swap in that case.
+     */
+    res_vreg = mir_graph_->SRegToVReg(rl_dest.s_reg_low);
+    src2_vreg = mir_graph_->SRegToVReg(rl_src2.s_reg_low);
+    if (res_vreg == src2_vreg) {
+      std::swap(rl_src1, rl_src2);
+    }
+
+    rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+    RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+
+    // Pick the first integer as min/max.
+    OpRegCopyWide(rl_result.reg, rl_src1.reg);
+
+    /*
+     * If the integers are both in the same register, then there is
+     * nothing else to do because they are equal and we have already
+     * moved one into the result.
+     */
+    src1_vreg = mir_graph_->SRegToVReg(rl_src1.s_reg_low);
+    src2_vreg = mir_graph_->SRegToVReg(rl_src2.s_reg_low);
+    if (src1_vreg == src2_vreg) {
+      StoreValueWide(rl_dest, rl_result);
+      return true;
+    }
+
+    // Free registers to make some room for the second operand.
+    // But don't try to free ourselves or promoted registers.
+    if (res_vreg != src1_vreg &&
+        IsTemp(rl_src1.reg.GetLow()) && IsTemp(rl_src1.reg.GetHigh())) {
+      FreeTemp(rl_src1.reg);
+    }
+    rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+
+    // Do we have a free register for intermediate calculations?
+    RegStorage tmp = AllocTemp(false);
+    if (tmp == RegStorage::InvalidReg()) {
+       /*
+        * No, will use 'edi'.
+        *
+        * As mentioned above we have 4 temporary and 2 promotable
+        * caller-save registers. Therefore, we assume that a free
+        * register can be allocated only if 'esi' and 'edi' are
+        * already used as operands. If number of promotable registers
+        * increases from 2 to 4 then our assumption fails and operand
+        * data is corrupted.
+        * Let's DCHECK it.
+        */
+       DCHECK(IsTemp(rl_src2.reg.GetLow()) &&
+              IsTemp(rl_src2.reg.GetHigh()) &&
+              IsTemp(rl_result.reg.GetLow()) &&
+              IsTemp(rl_result.reg.GetHigh()));
+       tmp = rs_rDI;
+       NewLIR1(kX86Push32R, tmp.GetReg());
+    }
+
+    // Now we are ready to do calculations.
+    OpRegReg(kOpMov, tmp, rl_result.reg.GetLow());
+    OpRegReg(kOpSub, tmp, rl_src2.reg.GetLow());
+    OpRegReg(kOpMov, tmp, rl_result.reg.GetHigh());
+    OpRegReg(kOpSbc, tmp, rl_src2.reg.GetHigh());
+
+    // Let's put pop 'edi' here to break a bit the dependency chain.
+    if (tmp == rs_rDI) {
+      NewLIR1(kX86Pop32R, tmp.GetReg());
+    }
+
+    // Conditionally move the other integer into the destination register.
+    ConditionCode cc = is_min ? kCondGe : kCondLt;
+    OpCondRegReg(kOpCmov, cc, rl_result.reg.GetLow(), rl_src2.reg.GetLow());
+    OpCondRegReg(kOpCmov, cc, rl_result.reg.GetHigh(), rl_src2.reg.GetHigh());
+    StoreValueWide(rl_dest, rl_result);
+    return true;
   }
 
   // Get the two arguments to the invoke and place them in GP registers.
@@ -1070,6 +1201,83 @@
   return true;
 }
 
+void X86Mir2Lir::SwapBits(RegStorage result_reg, int shift, int32_t value) {
+  RegStorage r_temp = AllocTemp();
+  OpRegCopy(r_temp, result_reg);
+  OpRegImm(kOpLsr, result_reg, shift);
+  OpRegImm(kOpAnd, r_temp, value);
+  OpRegImm(kOpAnd, result_reg, value);
+  OpRegImm(kOpLsl, r_temp, shift);
+  OpRegReg(kOpOr, result_reg, r_temp);
+  FreeTemp(r_temp);
+}
+
+void X86Mir2Lir::SwapBits64(RegStorage result_reg, int shift, int64_t value) {
+  RegStorage r_temp = AllocTempWide();
+  OpRegCopy(r_temp, result_reg);
+  OpRegImm(kOpLsr, result_reg, shift);
+  RegStorage r_value = AllocTempWide();
+  LoadConstantWide(r_value, value);
+  OpRegReg(kOpAnd, r_temp, r_value);
+  OpRegReg(kOpAnd, result_reg, r_value);
+  OpRegImm(kOpLsl, r_temp, shift);
+  OpRegReg(kOpOr, result_reg, r_temp);
+  FreeTemp(r_temp);
+  FreeTemp(r_value);
+}
+
+bool X86Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
+  RegLocation rl_src_i = info->args[0];
+  RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg)
+                                   : LoadValue(rl_src_i, kCoreReg);
+  RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
+  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+  if (size == k64) {
+    if (cu_->instruction_set == kX86_64) {
+      /* Use one bswap instruction to reverse byte order first and then use 3 rounds of
+         swapping bits to reverse bits in a long number x. Using bswap to save instructions
+         compared to generic luni implementation which has 5 rounds of swapping bits.
+         x = bswap x
+         x = (x & 0x5555555555555555) << 1 | (x >> 1) & 0x5555555555555555;
+         x = (x & 0x3333333333333333) << 2 | (x >> 2) & 0x3333333333333333;
+         x = (x & 0x0F0F0F0F0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F0F0F0F0F;
+      */
+      OpRegReg(kOpRev, rl_result.reg, rl_i.reg);
+      SwapBits64(rl_result.reg, 1, 0x5555555555555555);
+      SwapBits64(rl_result.reg, 2, 0x3333333333333333);
+      SwapBits64(rl_result.reg, 4, 0x0f0f0f0f0f0f0f0f);
+      StoreValueWide(rl_dest, rl_result);
+      return true;
+    }
+    RegStorage r_i_low = rl_i.reg.GetLow();
+    if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
+      // First REV shall clobber rl_result.reg.GetLowReg(), save the value in a temp for the second
+      // REV.
+      r_i_low = AllocTemp();
+      OpRegCopy(r_i_low, rl_i.reg);
+    }
+    OpRegReg(kOpRev, rl_result.reg.GetLow(), rl_i.reg.GetHigh());
+    OpRegReg(kOpRev, rl_result.reg.GetHigh(), r_i_low);
+    if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
+      FreeTemp(r_i_low);
+    }
+    SwapBits(rl_result.reg.GetLow(), 1, 0x55555555);
+    SwapBits(rl_result.reg.GetLow(), 2, 0x33333333);
+    SwapBits(rl_result.reg.GetLow(), 4, 0x0f0f0f0f);
+    SwapBits(rl_result.reg.GetHigh(), 1, 0x55555555);
+    SwapBits(rl_result.reg.GetHigh(), 2, 0x33333333);
+    SwapBits(rl_result.reg.GetHigh(), 4, 0x0f0f0f0f);
+    StoreValueWide(rl_dest, rl_result);
+  } else {
+    OpRegReg(kOpRev, rl_result.reg, rl_i.reg);
+    SwapBits(rl_result.reg, 1, 0x55555555);
+    SwapBits(rl_result.reg, 2, 0x33333333);
+    SwapBits(rl_result.reg, 4, 0x0f0f0f0f);
+    StoreValue(rl_dest, rl_result);
+  }
+  return true;
+}
+
 LIR* X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
   CHECK(base_of_code_ != nullptr);
 
@@ -1401,8 +1609,8 @@
   if (!cu_->target64) {
     int32_t val_lo = Low32Bits(val);
     int32_t val_hi = High32Bits(val);
-    FlushAllRegs();
-    LockCallTemps();  // Prepare for explicit register usage.
+    // Prepare for explicit register usage.
+    ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
     rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
     bool src1_in_reg = rl_src1.location == kLocPhysReg;
     int displacement = SRegOffset(rl_src1.s_reg_low);
@@ -1485,8 +1693,8 @@
   bool is_square = mir_graph_->SRegToVReg(rl_src1.s_reg_low) ==
                    mir_graph_->SRegToVReg(rl_src2.s_reg_low);
 
-  FlushAllRegs();
-  LockCallTemps();  // Prepare for explicit register usage.
+  // Prepare for explicit register usage.
+  ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
   rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
   rl_src2 = UpdateLocWideTyped(rl_src2, kCoreReg);
 
@@ -1509,7 +1717,7 @@
       NewLIR2(kX86Imul32RR, rs_r1.GetReg(), rl_src2.reg.GetLowReg());
     } else {
       int displacement = SRegOffset(rl_src2.s_reg_low);
-      LIR *m = NewLIR3(kX86Imul32RM, rs_r1.GetReg(), rs_rX86_SP.GetReg(),
+      LIR* m = NewLIR3(kX86Imul32RM, rs_r1.GetReg(), rs_rX86_SP.GetReg(),
                        displacement + LOWORD_OFFSET);
       AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
                               true /* is_load */, true /* is_64bit */);
@@ -1830,7 +2038,8 @@
     Clobber(rs_r2q);
     LockTemp(rs_r2q);
 
-    RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_r2q, INVALID_SREG, INVALID_SREG};
+    RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1,
+                             is_div ? rs_r2q : rs_r0q, INVALID_SREG, INVALID_SREG};
 
     // Use H.S.Warren's Hacker's Delight Chapter 10 and
     // T,Grablund, P.L.Montogomery's Division by invariant integers using multiplication.
@@ -1854,24 +2063,35 @@
      * 5. Thus, RDX is the quotient
      */
 
-    // Numerator into RAX.
+    // RAX = magic.
+    LoadConstantWide(rs_r0q, magic);
+
+    // Multiply by numerator.
     RegStorage numerator_reg;
     if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
       // We will need the value later.
       rl_src = LoadValueWide(rl_src, kCoreReg);
       numerator_reg = rl_src.reg;
-      OpRegCopyWide(rs_r0q, numerator_reg);
+
+      // RDX:RAX = magic * numerator.
+      NewLIR1(kX86Imul64DaR, numerator_reg.GetReg());
     } else {
-      // Only need this once.  Just put it into RAX.
-      LoadValueDirectWideFixed(rl_src, rs_r0q);
+      // Only need this once.  Multiply directly from the value.
+      rl_src = UpdateLocWideTyped(rl_src, kCoreReg);
+      if (rl_src.location != kLocPhysReg) {
+        // Okay, we can do this from memory.
+        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+        int displacement = SRegOffset(rl_src.s_reg_low);
+        // RDX:RAX = magic * numerator.
+        LIR *m = NewLIR2(kX86Imul64DaM, rs_rX86_SP.GetReg(), displacement);
+        AnnotateDalvikRegAccess(m, displacement >> 2,
+                                true /* is_load */, true /* is_64bit */);
+      } else {
+        // RDX:RAX = magic * numerator.
+        NewLIR1(kX86Imul64DaR, rl_src.reg.GetReg());
+      }
     }
 
-    // RDX = magic.
-    LoadConstantWide(rs_r2q, magic);
-
-    // RDX:RAX = magic & dividend.
-    NewLIR1(kX86Imul64DaR, rs_r2q.GetReg());
-
     if (imm > 0 && magic < 0) {
       // Add numerator to RDX.
       DCHECK(numerator_reg.Valid());
@@ -1919,14 +2139,12 @@
         NewLIR3(kX86Imul64RRI, rs_r2q.GetReg(), rs_r2q.GetReg(), short_imm);
       }
 
-      // RDX -= RAX.
+      // RAX -= RDX.
       OpRegReg(kOpSub, rs_r0q, rs_r2q);
 
-      // Store result.
-      OpRegCopyWide(rl_result.reg, rs_r0q);
+      // Result in RAX.
     } else {
-      // Store result.
-      OpRegCopyWide(rl_result.reg, rs_r2q);
+      // Result in RDX.
     }
     StoreValueWide(rl_dest, rl_result);
     FreeTemp(rs_r0q);
@@ -1949,8 +2167,8 @@
   }
 
   // We have to use fixed registers, so flush all the temps.
-  FlushAllRegs();
-  LockCallTemps();  // Prepare for explicit register usage.
+  // Prepare for explicit register usage.
+  ExplicitTempRegisterLock(this, 4, &rs_r0q, &rs_r1q, &rs_r2q, &rs_r6q);
 
   // Load LHS into RAX.
   LoadValueDirectWideFixed(rl_src1, rs_r0q);
@@ -1966,7 +2184,7 @@
 
   // Have to catch 0x8000000000000000/-1 case, or we will get an exception!
   NewLIR2(kX86Cmp64RI8, rs_r1q.GetReg(), -1);
-  LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+  LIR* minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
 
   // RHS is -1.
   LoadConstantWide(rs_r6q, 0x8000000000000000);
@@ -2149,9 +2367,9 @@
   if ((size == kSignedByte || size == kUnsignedByte) && !IsByteRegister(rl_src.reg)) {
     RegStorage temp = AllocTemp();
     OpRegCopy(temp, rl_src.reg);
-    StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, temp, size);
+    StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, temp, size, opt_flags);
   } else {
-    StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, rl_src.reg, size);
+    StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, rl_src.reg, size, opt_flags);
   }
   if (card_mark) {
     // Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
@@ -2813,7 +3031,6 @@
       LoadValueDirectFixed(rl_rhs, t_reg);
       if (is_two_addr) {
         // Can we do this directly into memory?
-        rl_rhs = LoadValue(rl_rhs, kCoreReg);
         rl_result = UpdateLocTyped(rl_dest, kCoreReg);
         if (rl_result.location != kLocPhysReg) {
           // Okay, we can do this into memory
@@ -2965,7 +3182,53 @@
 void X86Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
                         RegLocation rl_src1, RegLocation rl_shift) {
   if (!cu_->target64) {
-    Mir2Lir::GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
+    // Long shift operations in 32-bit. Use shld or shrd to create a 32-bit register filled from
+    // the other half, shift the other half, if the shift amount is less than 32 we're done,
+    // otherwise move one register to the other and place zero or sign bits in the other.
+    LIR* branch;
+    FlushAllRegs();
+    LockCallTemps();
+    LoadValueDirectFixed(rl_shift, rs_rCX);
+    RegStorage r_tmp = RegStorage::MakeRegPair(rs_rAX, rs_rDX);
+    LoadValueDirectWideFixed(rl_src1, r_tmp);
+    switch (opcode) {
+      case Instruction::SHL_LONG:
+      case Instruction::SHL_LONG_2ADDR:
+        NewLIR3(kX86Shld32RRC, r_tmp.GetHighReg(), r_tmp.GetLowReg(), rs_rCX.GetReg());
+        NewLIR2(kX86Sal32RC, r_tmp.GetLowReg(), rs_rCX.GetReg());
+        NewLIR2(kX86Test8RI, rs_rCX.GetReg(), 32);
+        branch = NewLIR2(kX86Jcc8, 0, kX86CondZ);
+        OpRegCopy(r_tmp.GetHigh(), r_tmp.GetLow());
+        LoadConstant(r_tmp.GetLow(), 0);
+        branch->target = NewLIR0(kPseudoTargetLabel);
+        break;
+      case Instruction::SHR_LONG:
+      case Instruction::SHR_LONG_2ADDR:
+        NewLIR3(kX86Shrd32RRC, r_tmp.GetLowReg(), r_tmp.GetHighReg(), rs_rCX.GetReg());
+        NewLIR2(kX86Sar32RC, r_tmp.GetHighReg(), rs_rCX.GetReg());
+        NewLIR2(kX86Test8RI, rs_rCX.GetReg(), 32);
+        branch = NewLIR2(kX86Jcc8, 0, kX86CondZ);
+        OpRegCopy(r_tmp.GetLow(), r_tmp.GetHigh());
+        NewLIR2(kX86Sar32RI, r_tmp.GetHighReg(), 31);
+        branch->target = NewLIR0(kPseudoTargetLabel);
+        break;
+      case Instruction::USHR_LONG:
+      case Instruction::USHR_LONG_2ADDR:
+        NewLIR3(kX86Shrd32RRC, r_tmp.GetLowReg(), r_tmp.GetHighReg(),
+               rs_rCX.GetReg());
+        NewLIR2(kX86Shr32RC, r_tmp.GetHighReg(), rs_rCX.GetReg());
+        NewLIR2(kX86Test8RI, rs_rCX.GetReg(), 32);
+        branch = NewLIR2(kX86Jcc8, 0, kX86CondZ);
+        OpRegCopy(r_tmp.GetLow(), r_tmp.GetHigh());
+        LoadConstant(r_tmp.GetHigh(), 0);
+        branch->target = NewLIR0(kPseudoTargetLabel);
+        break;
+      default:
+        LOG(FATAL) << "Unexpected case: " << opcode;
+        return;
+    }
+    RegLocation rl_result = LocCReturnWide();
+    StoreValueWide(rl_dest, rl_result);
     return;
   }
 
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index f7cb820..9aeaf60 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -14,16 +14,21 @@
  * limitations under the License.
  */
 
-#include <string>
+#include <cstdarg>
 #include <inttypes.h>
+#include <string>
 
+#include "backend_x86.h"
 #include "codegen_x86.h"
 #include "dex/compiler_internals.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 #include "mirror/array.h"
+#include "mirror/art_method.h"
 #include "mirror/string.h"
+#include "oat.h"
 #include "x86_lir.h"
+#include "utils/dwarf_cfi.h"
 
 namespace art {
 
@@ -362,6 +367,7 @@
              int64_t value = static_cast<int64_t>(static_cast<int64_t>(operand) << 32 |
                              static_cast<uint32_t>(lir->operands[operand_number+1]));
              buf +=StringPrintf("%" PRId64, value);
+             break;
           }
           case 'p': {
             EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(operand));
@@ -451,7 +457,7 @@
 }
 
 RegStorage X86Mir2Lir::Get128BitRegister(RegStorage reg) {
-  return GetRegInfo(reg)->FindMatchingView(RegisterInfo::k128SoloStorageMask)->GetReg();
+  return GetRegInfo(reg)->Master()->GetReg();
 }
 
 bool X86Mir2Lir::IsByteRegister(RegStorage reg) {
@@ -594,6 +600,9 @@
       mem_barrier = NewLIR0(kX86Mfence);
       ret = true;
     }
+  } else if (barrier_kind == kNTStoreStore) {
+      mem_barrier = NewLIR0(kX86Sfence);
+      ret = true;
   }
 
   // Now ensure that a scheduling barrier is in place.
@@ -612,13 +621,15 @@
 
 void X86Mir2Lir::CompilerInitializeRegAlloc() {
   if (cu_->target64) {
-    reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs_64, core_regs_64q, sp_regs_64,
-                                          dp_regs_64, reserved_regs_64, reserved_regs_64q,
-                                          core_temps_64, core_temps_64q, sp_temps_64, dp_temps_64);
+    reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs_64, core_regs_64q, sp_regs_64,
+                                              dp_regs_64, reserved_regs_64, reserved_regs_64q,
+                                              core_temps_64, core_temps_64q,
+                                              sp_temps_64, dp_temps_64));
   } else {
-    reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs_32, empty_pool, sp_regs_32,
-                                          dp_regs_32, reserved_regs_32, empty_pool,
-                                          core_temps_32, empty_pool, sp_temps_32, dp_temps_32);
+    reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs_32, empty_pool, sp_regs_32,
+                                              dp_regs_32, reserved_regs_32, empty_pool,
+                                              core_temps_32, empty_pool,
+                                              sp_temps_32, dp_temps_32));
   }
 
   // Target-specific adjustments.
@@ -627,7 +638,7 @@
   const ArrayRef<const RegStorage> *xp_regs = cu_->target64 ? &xp_regs_64 : &xp_regs_32;
   for (RegStorage reg : *xp_regs) {
     RegisterInfo* info = new (arena_) RegisterInfo(reg, GetRegMaskCommon(reg));
-    reginfo_map_.Put(reg.GetReg(), info);
+    reginfo_map_[reg.GetReg()] = info;
   }
   const ArrayRef<const RegStorage> *xp_temps = cu_->target64 ? &xp_temps_64 : &xp_temps_32;
   for (RegStorage reg : *xp_temps) {
@@ -637,8 +648,7 @@
 
   // Alias single precision xmm to double xmms.
   // TODO: as needed, add larger vector sizes - alias all to the largest.
-  GrowableArray<RegisterInfo*>::Iterator it(&reg_pool_->sp_regs_);
-  for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+  for (RegisterInfo* info : reg_pool_->sp_regs_) {
     int sp_reg_num = info->GetReg().GetRegNum();
     RegStorage xp_reg = RegStorage::Solo128(sp_reg_num);
     RegisterInfo* xp_reg_info = GetRegInfo(xp_reg);
@@ -658,8 +668,7 @@
 
   if (cu_->target64) {
     // Alias 32bit W registers to corresponding 64bit X registers.
-    GrowableArray<RegisterInfo*>::Iterator w_it(&reg_pool_->core_regs_);
-    for (RegisterInfo* info = w_it.Next(); info != nullptr; info = w_it.Next()) {
+    for (RegisterInfo* info : reg_pool_->core_regs_) {
       int x_reg_num = info->GetReg().GetRegNum();
       RegStorage x_reg = RegStorage::Solo64(x_reg_num);
       RegisterInfo* x_reg_info = GetRegInfo(x_reg);
@@ -683,8 +692,11 @@
   return 128;
 }
 
-int X86Mir2Lir::NumReservableVectorRegisters(bool fp_used) {
-  return fp_used ? 5 : 7;
+int X86Mir2Lir::NumReservableVectorRegisters(bool long_or_fp) {
+  int num_vector_temps = cu_->target64 ? xp_temps_64.size() : xp_temps_32.size();
+
+  // Leave a few temps for use by backend as scratch.
+  return long_or_fp ? num_vector_temps - 2 : num_vector_temps - 1;
 }
 
 void X86Mir2Lir::SpillCoreRegs() {
@@ -758,10 +770,7 @@
 RegisterClass X86Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) {
   // X86_64 can handle any size.
   if (cu_->target64) {
-    if (size == kReference) {
-      return kRefReg;
-    }
-    return kCoreReg;
+    return RegClassBySize(size);
   }
 
   if (UNLIKELY(is_volatile)) {
@@ -777,11 +786,14 @@
 X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
     : Mir2Lir(cu, mir_graph, arena),
       base_of_code_(nullptr), store_method_addr_(false), store_method_addr_used_(false),
-      method_address_insns_(arena, 100, kGrowableArrayMisc),
-      class_type_address_insns_(arena, 100, kGrowableArrayMisc),
-      call_method_insns_(arena, 100, kGrowableArrayMisc),
+      method_address_insns_(arena->Adapter()),
+      class_type_address_insns_(arena->Adapter()),
+      call_method_insns_(arena->Adapter()),
       stack_decrement_(nullptr), stack_increment_(nullptr),
       const_vectors_(nullptr) {
+  method_address_insns_.reserve(100);
+  class_type_address_insns_.reserve(100);
+  call_method_insns_.reserve(100);
   store_method_addr_used_ = false;
   if (kIsDebugBuild) {
     for (int i = 0; i < kX86Last; i++) {
@@ -861,9 +873,6 @@
   rX86_RET1 = rDX;
   rX86_INVOKE_TGT = rAX;
   rX86_COUNT = rCX;
-
-  // Initialize the number of reserved vector registers
-  num_reserved_vector_regs_ = -1;
 }
 
 Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
@@ -972,56 +981,85 @@
                      static_cast<int>(target_method_id_ptr), target_method_idx,
                      WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
   AppendLIR(move);
-  method_address_insns_.Insert(move);
+  method_address_insns_.push_back(move);
 }
 
-void X86Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) {
+void X86Mir2Lir::LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+                               SpecialTargetRegister symbolic_reg) {
   /*
    * For x86, just generate a 32 bit move immediate instruction, that will be filled
    * in at 'link time'.  For now, put a unique value based on target to ensure that
    * code deduplication works.
    */
-  const DexFile::TypeId& id = cu_->dex_file->GetTypeId(type_idx);
+  const DexFile::TypeId& id = dex_file.GetTypeId(type_idx);
   uintptr_t ptr = reinterpret_cast<uintptr_t>(&id);
 
   // Generate the move instruction with the unique pointer and save index and type.
   LIR *move = RawLIR(current_dalvik_offset_, kX86Mov32RI,
                      TargetReg(symbolic_reg, kNotWide).GetReg(),
-                     static_cast<int>(ptr), type_idx);
+                     static_cast<int>(ptr), type_idx,
+                     WrapPointer(const_cast<DexFile*>(&dex_file)));
   AppendLIR(move);
-  class_type_address_insns_.Insert(move);
+  class_type_address_insns_.push_back(move);
 }
 
-LIR *X86Mir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
+LIR* X86Mir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
   /*
    * For x86, just generate a 32 bit call relative instruction, that will be filled
-   * in at 'link time'.  For now, put a unique value based on target to ensure that
-   * code deduplication works.
+   * in at 'link time'.
    */
   int target_method_idx = target_method.dex_method_index;
   const DexFile* target_dex_file = target_method.dex_file;
-  const DexFile::MethodId& target_method_id = target_dex_file->GetMethodId(target_method_idx);
-  uintptr_t target_method_id_ptr = reinterpret_cast<uintptr_t>(&target_method_id);
 
   // Generate the call instruction with the unique pointer and save index, dex_file, and type.
-  LIR *call = RawLIR(current_dalvik_offset_, kX86CallI, static_cast<int>(target_method_id_ptr),
+  // NOTE: Method deduplication takes linker patches into account, so we can just pass 0
+  // as a placeholder for the offset.
+  LIR* call = RawLIR(current_dalvik_offset_, kX86CallI, 0,
                      target_method_idx, WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
   AppendLIR(call);
-  call_method_insns_.Insert(call);
+  call_method_insns_.push_back(call);
   return call;
 }
 
-/*
- * @brief Enter a 32 bit quantity into a buffer
- * @param buf buffer.
- * @param data Data value.
- */
+static LIR* GenInvokeNoInlineCall(Mir2Lir* mir_to_lir, InvokeType type) {
+  QuickEntrypointEnum trampoline;
+  switch (type) {
+    case kInterface:
+      trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
+      break;
+    case kDirect:
+      trampoline = kQuickInvokeDirectTrampolineWithAccessCheck;
+      break;
+    case kStatic:
+      trampoline = kQuickInvokeStaticTrampolineWithAccessCheck;
+      break;
+    case kSuper:
+      trampoline = kQuickInvokeSuperTrampolineWithAccessCheck;
+      break;
+    case kVirtual:
+      trampoline = kQuickInvokeVirtualTrampolineWithAccessCheck;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected invoke type";
+      trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
+  }
+  return mir_to_lir->InvokeTrampoline(kOpBlx, RegStorage::InvalidReg(), trampoline);
+}
 
-static void PushWord(std::vector<uint8_t>&buf, int32_t data) {
-  buf.push_back(data & 0xff);
-  buf.push_back((data >> 8) & 0xff);
-  buf.push_back((data >> 16) & 0xff);
-  buf.push_back((data >> 24) & 0xff);
+LIR* X86Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+  LIR* call_insn;
+  if (method_info.FastPath()) {
+    if (method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
+      // We can have the linker fixup a call relative.
+      call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType());
+    } else {
+      call_insn = OpMem(kOpBlx, TargetReg(kArg0, kRef),
+                        mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+    }
+  } else {
+    call_insn = GenInvokeNoInlineCall(this, method_info.GetSharpType());
+  }
+  return call_insn;
 }
 
 void X86Mir2Lir::InstallLiteralPools() {
@@ -1030,30 +1068,28 @@
   DCHECK(method_literal_list_ == nullptr);
   DCHECK(class_literal_list_ == nullptr);
 
-  // Align to 16 byte boundary.  We have implicit knowledge that the start of the method is
-  // on a 4 byte boundary.   How can I check this if it changes (other than aligned loads
-  // will fail at runtime)?
+
   if (const_vectors_ != nullptr) {
-    int align_size = (16-4) - (code_buffer_.size() & 0xF);
-    if (align_size < 0) {
-      align_size += 16;
+    // Vector literals must be 16-byte aligned. The header that is placed
+    // in the code section causes misalignment so we take it into account.
+    // Otherwise, we are sure that for x86 method is aligned to 16.
+    DCHECK_EQ(GetInstructionSetAlignment(cu_->instruction_set), 16u);
+    uint32_t bytes_to_fill = (0x10 - ((code_buffer_.size() + sizeof(OatQuickMethodHeader)) & 0xF)) & 0xF;
+    while (bytes_to_fill > 0) {
+      code_buffer_.push_back(0);
+      bytes_to_fill--;
     }
 
-    while (align_size > 0) {
-      code_buffer_.push_back(0);
-      align_size--;
-    }
     for (LIR *p = const_vectors_; p != nullptr; p = p->next) {
-      PushWord(code_buffer_, p->operands[0]);
-      PushWord(code_buffer_, p->operands[1]);
-      PushWord(code_buffer_, p->operands[2]);
-      PushWord(code_buffer_, p->operands[3]);
+      PushWord(&code_buffer_, p->operands[0]);
+      PushWord(&code_buffer_, p->operands[1]);
+      PushWord(&code_buffer_, p->operands[2]);
+      PushWord(&code_buffer_, p->operands[3]);
     }
   }
 
   // Handle the fixups for methods.
-  for (uint32_t i = 0; i < method_address_insns_.Size(); i++) {
-      LIR* p = method_address_insns_.Get(i);
+  for (LIR* p : method_address_insns_) {
       DCHECK_EQ(p->opcode, kX86Mov32RI);
       uint32_t target_method_idx = p->operands[2];
       const DexFile* target_dex_file =
@@ -1061,28 +1097,27 @@
 
       // The offset to patch is the last 4 bytes of the instruction.
       int patch_offset = p->offset + p->flags.size - 4;
-      cu_->compiler_driver->AddMethodPatch(cu_->dex_file, cu_->class_def_idx,
-                                           cu_->method_idx, cu_->invoke_type,
-                                           target_method_idx, target_dex_file,
-                                           static_cast<InvokeType>(p->operands[4]),
-                                           patch_offset);
+      patches_.push_back(LinkerPatch::MethodPatch(patch_offset,
+                                                  target_dex_file, target_method_idx));
   }
 
   // Handle the fixups for class types.
-  for (uint32_t i = 0; i < class_type_address_insns_.Size(); i++) {
-      LIR* p = class_type_address_insns_.Get(i);
+  for (LIR* p : class_type_address_insns_) {
       DCHECK_EQ(p->opcode, kX86Mov32RI);
-      uint32_t target_method_idx = p->operands[2];
+
+      const DexFile* class_dex_file =
+        reinterpret_cast<const DexFile*>(UnwrapPointer(p->operands[3]));
+      uint32_t target_type_idx = p->operands[2];
 
       // The offset to patch is the last 4 bytes of the instruction.
       int patch_offset = p->offset + p->flags.size - 4;
-      cu_->compiler_driver->AddClassPatch(cu_->dex_file, cu_->class_def_idx,
-                                          cu_->method_idx, target_method_idx, patch_offset);
+      patches_.push_back(LinkerPatch::TypePatch(patch_offset,
+                                                class_dex_file, target_type_idx));
   }
 
   // And now the PC-relative calls to methods.
-  for (uint32_t i = 0; i < call_method_insns_.Size(); i++) {
-      LIR* p = call_method_insns_.Get(i);
+  patches_.reserve(call_method_insns_.size());
+  for (LIR* p : call_method_insns_) {
       DCHECK_EQ(p->opcode, kX86CallI);
       uint32_t target_method_idx = p->operands[1];
       const DexFile* target_dex_file =
@@ -1090,11 +1125,8 @@
 
       // The offset to patch is the last 4 bytes of the instruction.
       int patch_offset = p->offset + p->flags.size - 4;
-      cu_->compiler_driver->AddRelativeCodePatch(cu_->dex_file, cu_->class_def_idx,
-                                                 cu_->method_idx, cu_->invoke_type,
-                                                 target_method_idx, target_dex_file,
-                                                 static_cast<InvokeType>(p->operands[3]),
-                                                 patch_offset, -4 /* offset */);
+      patches_.push_back(LinkerPatch::RelativeCodePatch(patch_offset,
+                                                        target_dex_file, target_method_idx));
   }
 
   // And do the normal processing.
@@ -1240,7 +1272,7 @@
   RegLocation rl_obj = info->args[0];
   RegLocation rl_char = info->args[1];
   RegLocation rl_start;  // Note: only present in III flavor or IndexOf.
-  // RBX is callee-save register in 64-bit mode.
+  // RBX is promotable in 64-bit mode.
   RegStorage rs_tmp = cu_->target64 ? rs_r11 : rs_rBX;
   int start_value = -1;
 
@@ -1260,23 +1292,7 @@
   // EBX or R11: temporary during execution (depending on mode).
   // REP SCASW: search instruction.
 
-  FlushReg(rs_rAX);
-  Clobber(rs_rAX);
-  LockTemp(rs_rAX);
-  FlushReg(rs_rCX);
-  Clobber(rs_rCX);
-  LockTemp(rs_rCX);
-  FlushReg(rs_rDX);
-  Clobber(rs_rDX);
-  LockTemp(rs_rDX);
-  FlushReg(rs_tmp);
-  Clobber(rs_tmp);
-  LockTemp(rs_tmp);
-  if (cu_->target64) {
-    FlushReg(rs_rDI);
-    Clobber(rs_rDI);
-    LockTemp(rs_rDI);
-  }
+  FlushAllRegs();
 
   RegLocation rl_return = GetReturn(kCoreReg);
   RegLocation rl_dest = InlineTarget(info);
@@ -1318,7 +1334,7 @@
   MarkPossibleNullPointerException(0);
 
   if (!cu_->target64) {
-    // EDI is callee-save register in 32-bit mode.
+    // EDI is promotable in 32-bit mode.
     NewLIR1(kX86Push32R, rs_rDI.GetReg());
   }
 
@@ -1426,142 +1442,9 @@
   }
 
   StoreValue(rl_dest, rl_return);
-
-  FreeTemp(rs_rAX);
-  FreeTemp(rs_rCX);
-  FreeTemp(rs_rDX);
-  FreeTemp(rs_tmp);
-  if (cu_->target64) {
-    FreeTemp(rs_rDI);
-  }
-
   return true;
 }
 
-/*
- * @brief Enter an 'advance LOC' into the FDE buffer
- * @param buf FDE buffer.
- * @param increment Amount by which to increase the current location.
- */
-static void AdvanceLoc(std::vector<uint8_t>&buf, uint32_t increment) {
-  if (increment < 64) {
-    // Encoding in opcode.
-    buf.push_back(0x1 << 6 | increment);
-  } else if (increment < 256) {
-    // Single byte delta.
-    buf.push_back(0x02);
-    buf.push_back(increment);
-  } else if (increment < 256 * 256) {
-    // Two byte delta.
-    buf.push_back(0x03);
-    buf.push_back(increment & 0xff);
-    buf.push_back((increment >> 8) & 0xff);
-  } else {
-    // Four byte delta.
-    buf.push_back(0x04);
-    PushWord(buf, increment);
-  }
-}
-
-
-std::vector<uint8_t>* X86CFIInitialization(bool is_x86_64) {
-  return X86Mir2Lir::ReturnCommonCallFrameInformation(is_x86_64);
-}
-
-static void EncodeUnsignedLeb128(std::vector<uint8_t>& buf, uint32_t value) {
-  uint8_t buffer[12];
-  uint8_t *ptr = EncodeUnsignedLeb128(buffer, value);
-  for (uint8_t *p = buffer; p < ptr; p++) {
-    buf.push_back(*p);
-  }
-}
-
-static void EncodeSignedLeb128(std::vector<uint8_t>& buf, int32_t value) {
-  uint8_t buffer[12];
-  uint8_t *ptr = EncodeSignedLeb128(buffer, value);
-  for (uint8_t *p = buffer; p < ptr; p++) {
-    buf.push_back(*p);
-  }
-}
-
-std::vector<uint8_t>* X86Mir2Lir::ReturnCommonCallFrameInformation(bool is_x86_64) {
-  std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
-
-  // Length (will be filled in later in this routine).
-  PushWord(*cfi_info, 0);
-
-  // CIE id: always 0.
-  PushWord(*cfi_info, 0);
-
-  // Version: always 1.
-  cfi_info->push_back(0x01);
-
-  // Augmentation: 'zR\0'
-  cfi_info->push_back(0x7a);
-  cfi_info->push_back(0x52);
-  cfi_info->push_back(0x0);
-
-  // Code alignment: 1.
-  EncodeUnsignedLeb128(*cfi_info, 1);
-
-  // Data alignment.
-  if (is_x86_64) {
-    EncodeSignedLeb128(*cfi_info, -8);
-  } else {
-    EncodeSignedLeb128(*cfi_info, -4);
-  }
-
-  // Return address register.
-  if (is_x86_64) {
-    // R16(RIP)
-    cfi_info->push_back(0x10);
-  } else {
-    // R8(EIP)
-    cfi_info->push_back(0x08);
-  }
-
-  // Augmentation length: 1.
-  cfi_info->push_back(1);
-
-  // Augmentation data: 0x03 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
-  cfi_info->push_back(0x03);
-
-  // Initial instructions.
-  if (is_x86_64) {
-    // DW_CFA_def_cfa R7(RSP) 8.
-    cfi_info->push_back(0x0c);
-    cfi_info->push_back(0x07);
-    cfi_info->push_back(0x08);
-
-    // DW_CFA_offset R16(RIP) 1 (* -8).
-    cfi_info->push_back(0x90);
-    cfi_info->push_back(0x01);
-  } else {
-    // DW_CFA_def_cfa R4(ESP) 4.
-    cfi_info->push_back(0x0c);
-    cfi_info->push_back(0x04);
-    cfi_info->push_back(0x04);
-
-    // DW_CFA_offset R8(EIP) 1 (* -4).
-    cfi_info->push_back(0x88);
-    cfi_info->push_back(0x01);
-  }
-
-  // Padding to a multiple of 4
-  while ((cfi_info->size() & 3) != 0) {
-    // DW_CFA_nop is encoded as 0.
-    cfi_info->push_back(0);
-  }
-
-  // Set the length of the CIE inside the generated bytes.
-  uint32_t length = cfi_info->size() - 4;
-  (*cfi_info)[0] = length;
-  (*cfi_info)[1] = length >> 8;
-  (*cfi_info)[2] = length >> 16;
-  (*cfi_info)[3] = length >> 24;
-  return cfi_info;
-}
-
 static bool ARTRegIDToDWARFRegID(bool is_x86_64, int art_reg_id, int* dwarf_reg_id) {
   if (is_x86_64) {
     switch (art_reg_id) {
@@ -1584,36 +1467,23 @@
   }
 }
 
-std::vector<uint8_t>* X86Mir2Lir::ReturnCallFrameInformation() {
-  std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
+std::vector<uint8_t>* X86Mir2Lir::ReturnFrameDescriptionEntry() {
+  std::vector<uint8_t>* cfi_info = new std::vector<uint8_t>;
 
   // Generate the FDE for the method.
   DCHECK_NE(data_offset_, 0U);
 
-  // Length (will be filled in later in this routine).
-  PushWord(*cfi_info, 0);
-
-  // 'CIE_pointer' (filled in by linker).
-  PushWord(*cfi_info, 0);
-
-  // 'initial_location' (filled in by linker).
-  PushWord(*cfi_info, 0);
-
-  // 'address_range' (number of bytes in the method).
-  PushWord(*cfi_info, data_offset_);
-
-  // Augmentation length: 0
-  cfi_info->push_back(0);
+  WriteFDEHeader(cfi_info, cu_->target64);
+  WriteFDEAddressRange(cfi_info, data_offset_, cu_->target64);
 
   // The instructions in the FDE.
   if (stack_decrement_ != nullptr) {
     // Advance LOC to just past the stack decrement.
     uint32_t pc = NEXT_LIR(stack_decrement_)->offset;
-    AdvanceLoc(*cfi_info, pc);
+    DW_CFA_advance_loc(cfi_info, pc);
 
     // Now update the offset to the call frame: DW_CFA_def_cfa_offset frame_size.
-    cfi_info->push_back(0x0e);
-    EncodeUnsignedLeb128(*cfi_info, frame_size_);
+    DW_CFA_def_cfa_offset(cfi_info, frame_size_);
 
     // Handle register spills
     const uint32_t kSpillInstLen = (cu_->target64) ? 5 : 4;
@@ -1625,14 +1495,12 @@
         pc += kSpillInstLen;
 
         // Advance LOC to pass this instruction
-        AdvanceLoc(*cfi_info, kSpillInstLen);
+        DW_CFA_advance_loc(cfi_info, kSpillInstLen);
 
         int dwarf_reg_id;
         if (ARTRegIDToDWARFRegID(cu_->target64, reg, &dwarf_reg_id)) {
-          // DW_CFA_offset_extended_sf reg_no offset
-          cfi_info->push_back(0x11);
-          EncodeUnsignedLeb128(*cfi_info, dwarf_reg_id);
-          EncodeSignedLeb128(*cfi_info, offset / kDataAlignmentFactor);
+          // DW_CFA_offset_extended_sf reg offset
+          DW_CFA_offset_extended_sf(cfi_info, dwarf_reg_id, offset / kDataAlignmentFactor);
         }
 
         offset += GetInstructionSetPointerSize(cu_->instruction_set);
@@ -1642,16 +1510,15 @@
     // We continue with that stack until the epilogue.
     if (stack_increment_ != nullptr) {
       uint32_t new_pc = NEXT_LIR(stack_increment_)->offset;
-      AdvanceLoc(*cfi_info, new_pc - pc);
+      DW_CFA_advance_loc(cfi_info, new_pc - pc);
 
       // We probably have code snippets after the epilogue, so save the
       // current state: DW_CFA_remember_state.
-      cfi_info->push_back(0x0a);
+      DW_CFA_remember_state(cfi_info);
 
       // We have now popped the stack: DW_CFA_def_cfa_offset 4/8.
       // There is only the return PC on the stack now.
-      cfi_info->push_back(0x0e);
-      EncodeUnsignedLeb128(*cfi_info, GetInstructionSetPointerSize(cu_->instruction_set));
+      DW_CFA_def_cfa_offset(cfi_info, GetInstructionSetPointerSize(cu_->instruction_set));
 
       // Everything after that is the same as before the epilogue.
       // Stack bump was followed by RET instruction.
@@ -1659,25 +1526,16 @@
       if (post_ret_insn != nullptr) {
         pc = new_pc;
         new_pc = post_ret_insn->offset;
-        AdvanceLoc(*cfi_info, new_pc - pc);
+        DW_CFA_advance_loc(cfi_info, new_pc - pc);
         // Restore the state: DW_CFA_restore_state.
-        cfi_info->push_back(0x0b);
+        DW_CFA_restore_state(cfi_info);
       }
     }
   }
 
-  // Padding to a multiple of 4
-  while ((cfi_info->size() & 3) != 0) {
-    // DW_CFA_nop is encoded as 0.
-    cfi_info->push_back(0);
-  }
+  PadCFI(cfi_info);
+  WriteCFILength(cfi_info, cu_->target64);
 
-  // Set the length of the FDE inside the generated bytes.
-  uint32_t length = cfi_info->size() - 4;
-  (*cfi_info)[0] = length;
-  (*cfi_info)[1] = length >> 8;
-  (*cfi_info)[2] = length >> 16;
-  (*cfi_info)[3] = length >> 24;
   return cfi_info;
 }
 
@@ -1687,7 +1545,7 @@
       ReserveVectorRegisters(mir);
       break;
     case kMirOpReturnVectorRegisters:
-      ReturnVectorRegisters();
+      ReturnVectorRegisters(mir);
       break;
     case kMirOpConstVector:
       GenConst128(bb, mir);
@@ -1731,17 +1589,22 @@
     case kMirOpPackedSet:
       GenSetVector(bb, mir);
       break;
+    case kMirOpMemBarrier:
+      GenMemBarrier(static_cast<MemBarrierKind>(mir->dalvikInsn.vA));
+      break;
+    case kMirOpPackedArrayGet:
+      GenPackedArrayGet(bb, mir);
+      break;
+    case kMirOpPackedArrayPut:
+      GenPackedArrayPut(bb, mir);
+      break;
     default:
       break;
   }
 }
 
 void X86Mir2Lir::ReserveVectorRegisters(MIR* mir) {
-  // We should not try to reserve twice without returning the registers
-  DCHECK_NE(num_reserved_vector_regs_, -1);
-
-  int num_vector_reg = mir->dalvikInsn.vA;
-  for (int i = 0; i < num_vector_reg; i++) {
+  for (uint32_t i = mir->dalvikInsn.vA; i <= mir->dalvikInsn.vB; i++) {
     RegStorage xp_reg = RegStorage::Solo128(i);
     RegisterInfo *xp_reg_info = GetRegInfo(xp_reg);
     Clobber(xp_reg);
@@ -1749,20 +1612,17 @@
     for (RegisterInfo *info = xp_reg_info->GetAliasChain();
                        info != nullptr;
                        info = info->GetAliasChain()) {
-      if (info->GetReg().IsSingle()) {
-        reg_pool_->sp_regs_.Delete(info);
-      } else {
-        reg_pool_->dp_regs_.Delete(info);
-      }
+      ArenaVector<RegisterInfo*>* regs =
+          info->GetReg().IsSingle() ? &reg_pool_->sp_regs_ : &reg_pool_->dp_regs_;
+      auto it = std::find(regs->begin(), regs->end(), info);
+      DCHECK(it != regs->end());
+      regs->erase(it);
     }
   }
-
-  num_reserved_vector_regs_ = num_vector_reg;
 }
 
-void X86Mir2Lir::ReturnVectorRegisters() {
-  // Return all the reserved registers
-  for (int i = 0; i < num_reserved_vector_regs_; i++) {
+void X86Mir2Lir::ReturnVectorRegisters(MIR* mir) {
+  for (uint32_t i = mir->dalvikInsn.vA; i <= mir->dalvikInsn.vB; i++) {
     RegStorage xp_reg = RegStorage::Solo128(i);
     RegisterInfo *xp_reg_info = GetRegInfo(xp_reg);
 
@@ -1770,23 +1630,18 @@
                        info != nullptr;
                        info = info->GetAliasChain()) {
       if (info->GetReg().IsSingle()) {
-        reg_pool_->sp_regs_.Insert(info);
+        reg_pool_->sp_regs_.push_back(info);
       } else {
-        reg_pool_->dp_regs_.Insert(info);
+        reg_pool_->dp_regs_.push_back(info);
       }
     }
   }
-
-  // We don't have anymore reserved vector registers
-  num_reserved_vector_regs_ = -1;
 }
 
 void X86Mir2Lir::GenConst128(BasicBlock* bb, MIR* mir) {
-  store_method_addr_used_ = true;
-  int type_size = mir->dalvikInsn.vB;
-  // We support 128 bit vectors.
-  DCHECK_EQ(type_size & 0xFFFF, 128);
   RegStorage rs_dest = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest);
+
   uint32_t *args = mir->dalvikInsn.arg;
   int reg = rs_dest.GetReg();
   // Check for all 0 case.
@@ -1796,14 +1651,24 @@
   }
 
   // Append the mov const vector to reg opcode.
-  AppendOpcodeWithConst(kX86MovupsRM, reg, mir);
+  AppendOpcodeWithConst(kX86MovdqaRM, reg, mir);
 }
 
 void X86Mir2Lir::AppendOpcodeWithConst(X86OpCode opcode, int reg, MIR* mir) {
-  // Okay, load it from the constant vector area.
-  LIR *data_target = ScanVectorLiteral(mir);
+  // The literal pool needs position independent logic.
+  store_method_addr_used_ = true;
+
+  // To deal with correct memory ordering, reverse order of constants.
+  int32_t constants[4];
+  constants[3] = mir->dalvikInsn.arg[0];
+  constants[2] = mir->dalvikInsn.arg[1];
+  constants[1] = mir->dalvikInsn.arg[2];
+  constants[0] = mir->dalvikInsn.arg[3];
+
+  // Search if there is already a constant in pool with this value.
+  LIR *data_target = ScanVectorLiteral(constants);
   if (data_target == nullptr) {
-    data_target = AddVectorLiteral(mir);
+    data_target = AddVectorLiteral(constants);
   }
 
   // Address the start of the method.
@@ -1819,7 +1684,7 @@
   // 4 byte offset.  We will fix this up in the assembler later to have the right
   // value.
   ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-  LIR *load = NewLIR2(opcode, reg, rl_method.reg.GetReg());
+  LIR *load = NewLIR3(opcode, reg, rl_method.reg.GetReg(), 256 /* bogus */);
   load->flags.fixup = kFixupLoad;
   load->target = data_target;
 }
@@ -1828,16 +1693,12 @@
   // We only support 128 bit registers.
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   RegStorage rs_dest = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest);
   RegStorage rs_src = RegStorage::Solo128(mir->dalvikInsn.vB);
-  NewLIR2(kX86Mova128RR, rs_dest.GetReg(), rs_src.GetReg());
+  NewLIR2(kX86MovdqaRR, rs_dest.GetReg(), rs_src.GetReg());
 }
 
-void X86Mir2Lir::GenMultiplyVectorSignedByte(BasicBlock *bb, MIR *mir) {
-  const int BYTE_SIZE = 8;
-  RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
-  RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
-  RegStorage rs_src1_high_tmp = Get128BitRegister(AllocTempWide());
-
+void X86Mir2Lir::GenMultiplyVectorSignedByte(RegStorage rs_dest_src1, RegStorage rs_src2) {
   /*
    * Emulate the behavior of a kSignedByte by separating out the 16 values in the two XMM
    * and multiplying 8 at a time before recombining back into one XMM register.
@@ -1855,29 +1716,100 @@
    */
 
   // Copy xmm1.
-  NewLIR2(kX86Mova128RR, rs_src1_high_tmp.GetReg(), rs_dest_src1.GetReg());
+  RegStorage rs_src1_high_tmp = Get128BitRegister(AllocTempDouble());
+  RegStorage rs_dest_high_tmp = Get128BitRegister(AllocTempDouble());
+  NewLIR2(kX86MovdqaRR, rs_src1_high_tmp.GetReg(), rs_src2.GetReg());
+  NewLIR2(kX86MovdqaRR, rs_dest_high_tmp.GetReg(), rs_dest_src1.GetReg());
 
   // Multiply low bits.
+  // x7 *= x3
   NewLIR2(kX86PmullwRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
 
   // xmm1 now has low bits.
   AndMaskVectorRegister(rs_dest_src1, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF);
 
   // Prepare high bits for multiplication.
-  NewLIR2(kX86PsrlwRI, rs_src1_high_tmp.GetReg(), BYTE_SIZE);
-  AndMaskVectorRegister(rs_src2, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00);
+  NewLIR2(kX86PsrlwRI, rs_src1_high_tmp.GetReg(), 0x8);
+  AndMaskVectorRegister(rs_dest_high_tmp,  0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00);
 
   // Multiply high bits and xmm2 now has high bits.
-  NewLIR2(kX86PmullwRR, rs_src2.GetReg(), rs_src1_high_tmp.GetReg());
+  NewLIR2(kX86PmullwRR, rs_src1_high_tmp.GetReg(), rs_dest_high_tmp.GetReg());
 
   // Combine back into dest XMM register.
-  NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
+  NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_src1_high_tmp.GetReg());
+}
+
+void X86Mir2Lir::GenMultiplyVectorLong(RegStorage rs_dest_src1, RegStorage rs_src2) {
+  /*
+   * We need to emulate the packed long multiply.
+   * For kMirOpPackedMultiply xmm1, xmm0:
+   * - xmm1 is src/dest
+   * - xmm0 is src
+   * - Get xmm2 and xmm3 as temp
+   * - Idea is to multiply the lower 32 of each operand with the higher 32 of the other.
+   * - Then add the two results.
+   * - Move it to the upper 32 of the destination
+   * - Then multiply the lower 32-bits of the operands and add the result to the destination.
+   *
+   * (op     dest   src )
+   * movdqa  %xmm2, %xmm1
+   * movdqa  %xmm3, %xmm0
+   * psrlq   %xmm3, $0x20
+   * pmuludq %xmm3, %xmm2
+   * psrlq   %xmm1, $0x20
+   * pmuludq %xmm1, %xmm0
+   * paddq   %xmm1, %xmm3
+   * psllq   %xmm1, $0x20
+   * pmuludq %xmm2, %xmm0
+   * paddq   %xmm1, %xmm2
+   *
+   * When both the operands are the same, then we need to calculate the lower-32 * higher-32
+   * calculation only once. Thus we don't need the xmm3 temp above. That sequence becomes:
+   *
+   * (op     dest   src )
+   * movdqa  %xmm2, %xmm1
+   * psrlq   %xmm1, $0x20
+   * pmuludq %xmm1, %xmm0
+   * paddq   %xmm1, %xmm1
+   * psllq   %xmm1, $0x20
+   * pmuludq %xmm2, %xmm0
+   * paddq   %xmm1, %xmm2
+   *
+   */
+
+  bool both_operands_same = (rs_dest_src1.GetReg() == rs_src2.GetReg());
+
+  RegStorage rs_tmp_vector_1;
+  RegStorage rs_tmp_vector_2;
+  rs_tmp_vector_1 = Get128BitRegister(AllocTempDouble());
+  NewLIR2(kX86MovdqaRR, rs_tmp_vector_1.GetReg(), rs_dest_src1.GetReg());
+
+  if (both_operands_same == false) {
+    rs_tmp_vector_2 = Get128BitRegister(AllocTempDouble());
+    NewLIR2(kX86MovdqaRR, rs_tmp_vector_2.GetReg(), rs_src2.GetReg());
+    NewLIR2(kX86PsrlqRI, rs_tmp_vector_2.GetReg(), 0x20);
+    NewLIR2(kX86PmuludqRR, rs_tmp_vector_2.GetReg(), rs_tmp_vector_1.GetReg());
+  }
+
+  NewLIR2(kX86PsrlqRI, rs_dest_src1.GetReg(), 0x20);
+  NewLIR2(kX86PmuludqRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
+
+  if (both_operands_same == false) {
+    NewLIR2(kX86PaddqRR, rs_dest_src1.GetReg(), rs_tmp_vector_2.GetReg());
+  } else {
+    NewLIR2(kX86PaddqRR, rs_dest_src1.GetReg(), rs_dest_src1.GetReg());
+  }
+
+  NewLIR2(kX86PsllqRI, rs_dest_src1.GetReg(), 0x20);
+  NewLIR2(kX86PmuludqRR, rs_tmp_vector_1.GetReg(), rs_src2.GetReg());
+  NewLIR2(kX86PaddqRR, rs_dest_src1.GetReg(), rs_tmp_vector_1.GetReg());
 }
 
 void X86Mir2Lir::GenMultiplyVector(BasicBlock *bb, MIR *mir) {
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
   int opcode = 0;
   switch (opsize) {
@@ -1895,7 +1827,10 @@
       break;
     case kSignedByte:
       // HW doesn't support 16x16 byte multiplication so emulate it.
-      GenMultiplyVectorSignedByte(bb, mir);
+      GenMultiplyVectorSignedByte(rs_dest_src1, rs_src2);
+      return;
+    case k64:
+      GenMultiplyVectorLong(rs_dest_src1, rs_src2);
       return;
     default:
       LOG(FATAL) << "Unsupported vector multiply " << opsize;
@@ -1908,12 +1843,16 @@
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
   int opcode = 0;
   switch (opsize) {
     case k32:
       opcode = kX86PadddRR;
       break;
+    case k64:
+      opcode = kX86PaddqRR;
+      break;
     case kSignedHalf:
     case kUnsignedHalf:
       opcode = kX86PaddwRR;
@@ -1939,12 +1878,16 @@
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
   int opcode = 0;
   switch (opsize) {
     case k32:
       opcode = kX86PsubdRR;
       break;
+    case k64:
+      opcode = kX86PsubqRR;
+      break;
     case kSignedHalf:
     case kUnsignedHalf:
       opcode = kX86PsubwRR;
@@ -1967,58 +1910,54 @@
 }
 
 void X86Mir2Lir::GenShiftByteVector(BasicBlock *bb, MIR *mir) {
+  // Destination does not need clobbered because it has already been as part
+  // of the general packed shift handler (caller of this method).
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
-  RegStorage rs_tmp = Get128BitRegister(AllocTempWide());
 
   int opcode = 0;
-  int imm = mir->dalvikInsn.vB;
-
   switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
     case kMirOpPackedShiftLeft:
       opcode = kX86PsllwRI;
       break;
     case kMirOpPackedSignedShiftRight:
-      opcode = kX86PsrawRI;
-      break;
     case kMirOpPackedUnsignedShiftRight:
-      opcode = kX86PsrlwRI;
-      break;
+      // TODO Add support for emulated byte shifts.
     default:
       LOG(FATAL) << "Unsupported shift operation on byte vector " << opcode;
       break;
   }
 
-  /*
-   * xmm1 will have low bits
-   * xmm2 will have high bits
-   *
-   * xmm2 = xmm1
-   * xmm1 = xmm1 .<< N
-   * xmm2 = xmm2 && 0xFF00FF00FF00FF00FF00FF00FF00FF00
-   * xmm2 = xmm2 .<< N
-   * xmm1 = xmm1 | xmm2
-   */
-
-  // Copy xmm1.
-  NewLIR2(kX86Mova128RR, rs_tmp.GetReg(), rs_dest_src1.GetReg());
+  // Clear xmm register and return if shift more than byte length.
+  int imm = mir->dalvikInsn.vB;
+  if (imm >= 8) {
+    NewLIR2(kX86PxorRR, rs_dest_src1.GetReg(), rs_dest_src1.GetReg());
+    return;
+  }
 
   // Shift lower values.
   NewLIR2(opcode, rs_dest_src1.GetReg(), imm);
 
-  // Mask bottom bits.
-  AndMaskVectorRegister(rs_tmp, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00);
+  /*
+   * The above shift will shift the whole word, but that means
+   * both the bytes will shift as well. To emulate a byte level
+   * shift, we can just throw away the lower (8 - N) bits of the
+   * upper byte, and we are done.
+   */
+  uint8_t byte_mask = 0xFF << imm;
+  uint32_t int_mask = byte_mask;
+  int_mask = int_mask << 8 | byte_mask;
+  int_mask = int_mask << 8 | byte_mask;
+  int_mask = int_mask << 8 | byte_mask;
 
-  // Shift higher values.
-  NewLIR2(opcode, rs_tmp.GetReg(), imm);
-
-  // Combine back into dest XMM register.
-  NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_tmp.GetReg());
+  // And the destination with the mask
+  AndMaskVectorRegister(rs_dest_src1, int_mask, int_mask, int_mask, int_mask);
 }
 
 void X86Mir2Lir::GenShiftLeftVector(BasicBlock *bb, MIR *mir) {
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   int imm = mir->dalvikInsn.vB;
   int opcode = 0;
   switch (opsize) {
@@ -2047,6 +1986,7 @@
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   int imm = mir->dalvikInsn.vB;
   int opcode = 0;
   switch (opsize) {
@@ -2061,6 +2001,8 @@
     case kUnsignedByte:
       GenShiftByteVector(bb, mir);
       return;
+    case k64:
+      // TODO Implement emulated shift algorithm.
     default:
       LOG(FATAL) << "Unsupported vector signed shift right " << opsize;
       break;
@@ -2072,6 +2014,7 @@
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   int imm = mir->dalvikInsn.vB;
   int opcode = 0;
   switch (opsize) {
@@ -2100,6 +2043,7 @@
   // We only support 128 bit registers.
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
   NewLIR2(kX86PandRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
 }
@@ -2108,6 +2052,7 @@
   // We only support 128 bit registers.
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
   NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
 }
@@ -2116,6 +2061,7 @@
   // We only support 128 bit registers.
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+  Clobber(rs_dest_src1);
   RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
   NewLIR2(kX86PxorRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
 }
@@ -2140,134 +2086,248 @@
 
 void X86Mir2Lir::GenAddReduceVector(BasicBlock *bb, MIR *mir) {
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
-  RegStorage rs_src1 = RegStorage::Solo128(mir->dalvikInsn.vB);
-  RegLocation rl_dest = mir_graph_->GetDest(mir);
-  RegStorage rs_tmp;
+  RegStorage vector_src = RegStorage::Solo128(mir->dalvikInsn.vB);
+  bool is_wide = opsize == k64 || opsize == kDouble;
 
-  int vec_bytes = (mir->dalvikInsn.vC & 0xFFFF) / 8;
-  int vec_unit_size = 0;
-  int opcode = 0;
-  int extr_opcode = 0;
-  RegLocation rl_result;
-
-  switch (opsize) {
-    case k32:
-      extr_opcode = kX86PextrdRRI;
-      opcode = kX86PhadddRR;
-      vec_unit_size = 4;
-      break;
-    case kSignedByte:
-    case kUnsignedByte:
-      extr_opcode = kX86PextrbRRI;
-      opcode = kX86PhaddwRR;
-      vec_unit_size = 2;
-      break;
-    case kSignedHalf:
-    case kUnsignedHalf:
-      extr_opcode = kX86PextrwRRI;
-      opcode = kX86PhaddwRR;
-      vec_unit_size = 2;
-      break;
-    case kSingle:
-      rl_result = EvalLoc(rl_dest, kFPReg, true);
-      vec_unit_size = 4;
-      for (int i = 0; i < 3; i++) {
-        NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), rs_src1.GetReg());
-        NewLIR3(kX86ShufpsRRI, rs_src1.GetReg(), rs_src1.GetReg(), 0x39);
-      }
-      NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), rs_src1.GetReg());
-      StoreValue(rl_dest, rl_result);
-
-      // For single-precision floats, we are done here
-      return;
-    default:
-      LOG(FATAL) << "Unsupported vector add reduce " << opsize;
-      break;
-  }
-
-  int elems = vec_bytes / vec_unit_size;
-
-  // Emulate horizontal add instruction by reducing 2 vectors with 8 values before adding them again
-  // TODO is overflow handled correctly?
-  if (opsize == kSignedByte || opsize == kUnsignedByte) {
-    rs_tmp = Get128BitRegister(AllocTempWide());
-
-    // tmp = xmm1 .>> 8.
-    NewLIR2(kX86Mova128RR, rs_tmp.GetReg(), rs_src1.GetReg());
-    NewLIR2(kX86PsrlwRI, rs_tmp.GetReg(), 8);
-
-    // Zero extend low bits in xmm1.
-    AndMaskVectorRegister(rs_src1, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF);
-  }
-
-  while (elems > 1) {
-    if (opsize == kSignedByte || opsize == kUnsignedByte) {
-      NewLIR2(opcode, rs_tmp.GetReg(), rs_tmp.GetReg());
-    }
-    NewLIR2(opcode, rs_src1.GetReg(), rs_src1.GetReg());
-    elems >>= 1;
-  }
-
-  // Combine the results if we separated them.
-  if (opsize == kSignedByte || opsize == kUnsignedByte) {
-    NewLIR2(kX86PaddbRR, rs_src1.GetReg(), rs_tmp.GetReg());
-  }
-
-  // We need to extract to a GPR.
-  RegStorage temp = AllocTemp();
-  NewLIR3(extr_opcode, temp.GetReg(), rs_src1.GetReg(), 0);
-
-  // Can we do this directly into memory?
-  rl_result = UpdateLocTyped(rl_dest, kCoreReg);
-  if (rl_result.location == kLocPhysReg) {
-    // Ensure res is in a core reg
-    rl_result = EvalLoc(rl_dest, kCoreReg, true);
-    OpRegReg(kOpAdd, rl_result.reg, temp);
-    StoreFinalValue(rl_dest, rl_result);
+  // Get the location of the virtual register. Since this bytecode is overloaded
+  // for different types (and sizes), we need different logic for each path.
+  // The design of bytecode uses same VR for source and destination.
+  RegLocation rl_src, rl_dest, rl_result;
+  if (is_wide) {
+    rl_src = mir_graph_->GetSrcWide(mir, 0);
+    rl_dest = mir_graph_->GetDestWide(mir);
   } else {
-    OpMemReg(kOpAdd, rl_result, temp.GetReg());
+    rl_src = mir_graph_->GetSrc(mir, 0);
+    rl_dest = mir_graph_->GetDest(mir);
   }
 
-  FreeTemp(temp);
+  // We need a temp for byte and short values
+  RegStorage temp;
+
+  // There is a different path depending on type and size.
+  if (opsize == kSingle) {
+    // Handle float case.
+    // TODO Add support for fast math (not value safe) and do horizontal add in that case.
+
+    rl_src = LoadValue(rl_src, kFPReg);
+    rl_result = EvalLoc(rl_dest, kFPReg, true);
+
+    // Since we are doing an add-reduce, we move the reg holding the VR
+    // into the result so we include it in result.
+    OpRegCopy(rl_result.reg, rl_src.reg);
+    NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+
+    // Since FP must keep order of operation for value safety, we shift to low
+    // 32-bits and add to result.
+    for (int i = 0; i < 3; i++) {
+      NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), 0x39);
+      NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+    }
+
+    StoreValue(rl_dest, rl_result);
+  } else if (opsize == kDouble) {
+    // Handle double case.
+    rl_src = LoadValueWide(rl_src, kFPReg);
+    rl_result = EvalLocWide(rl_dest, kFPReg, true);
+    LOG(FATAL) << "Unsupported vector add reduce for double.";
+  } else if (opsize == k64) {
+    /*
+     * Handle long case:
+     * 1) Reduce the vector register to lower half (with addition).
+     * 1-1) Get an xmm temp and fill it with vector register.
+     * 1-2) Shift the xmm temp by 8-bytes.
+     * 1-3) Add the xmm temp to vector register that is being reduced.
+     * 2) Allocate temp GP / GP pair.
+     * 2-1) In 64-bit case, use movq to move result to a 64-bit GP.
+     * 2-2) In 32-bit case, use movd twice to move to 32-bit GP pair.
+     * 3) Finish the add reduction by doing what add-long/2addr does,
+     * but instead of having a VR as one of the sources, we have our temp GP.
+     */
+    RegStorage rs_tmp_vector = Get128BitRegister(AllocTempDouble());
+    NewLIR2(kX86MovdqaRR, rs_tmp_vector.GetReg(), vector_src.GetReg());
+    NewLIR2(kX86PsrldqRI, rs_tmp_vector.GetReg(), 8);
+    NewLIR2(kX86PaddqRR, vector_src.GetReg(), rs_tmp_vector.GetReg());
+    FreeTemp(rs_tmp_vector);
+
+    // We would like to be able to reuse the add-long implementation, so set up a fake
+    // register location to pass it.
+    RegLocation temp_loc = mir_graph_->GetBadLoc();
+    temp_loc.core = 1;
+    temp_loc.wide = 1;
+    temp_loc.location = kLocPhysReg;
+    temp_loc.reg = AllocTempWide();
+
+    if (cu_->target64) {
+      DCHECK(!temp_loc.reg.IsPair());
+      NewLIR2(kX86MovqrxRR, temp_loc.reg.GetReg(), vector_src.GetReg());
+    } else {
+      NewLIR2(kX86MovdrxRR, temp_loc.reg.GetLowReg(), vector_src.GetReg());
+      NewLIR2(kX86PsrlqRI, vector_src.GetReg(), 0x20);
+      NewLIR2(kX86MovdrxRR, temp_loc.reg.GetHighReg(), vector_src.GetReg());
+    }
+
+    GenArithOpLong(Instruction::ADD_LONG_2ADDR, rl_dest, temp_loc, temp_loc);
+  } else if (opsize == kSignedByte || opsize == kUnsignedByte) {
+    RegStorage rs_tmp = Get128BitRegister(AllocTempDouble());
+    NewLIR2(kX86PxorRR, rs_tmp.GetReg(), rs_tmp.GetReg());
+    NewLIR2(kX86PsadbwRR, vector_src.GetReg(), rs_tmp.GetReg());
+    NewLIR3(kX86PshufdRRI, rs_tmp.GetReg(), vector_src.GetReg(), 0x4e);
+    NewLIR2(kX86PaddbRR, vector_src.GetReg(), rs_tmp.GetReg());
+    // Move to a GPR
+    temp = AllocTemp();
+    NewLIR2(kX86MovdrxRR, temp.GetReg(), vector_src.GetReg());
+  } else {
+    // Handle and the int and short cases together
+
+    // Initialize as if we were handling int case. Below we update
+    // the opcode if handling byte or short.
+    int vec_bytes = (mir->dalvikInsn.vC & 0xFFFF) / 8;
+    int vec_unit_size;
+    int horizontal_add_opcode;
+    int extract_opcode;
+
+    if (opsize == kSignedHalf || opsize == kUnsignedHalf) {
+      extract_opcode = kX86PextrwRRI;
+      horizontal_add_opcode = kX86PhaddwRR;
+      vec_unit_size = 2;
+    } else if (opsize == k32) {
+      vec_unit_size = 4;
+      horizontal_add_opcode = kX86PhadddRR;
+      extract_opcode = kX86PextrdRRI;
+    } else {
+      LOG(FATAL) << "Unsupported vector add reduce " << opsize;
+      return;
+    }
+
+    int elems = vec_bytes / vec_unit_size;
+
+    while (elems > 1) {
+      NewLIR2(horizontal_add_opcode, vector_src.GetReg(), vector_src.GetReg());
+      elems >>= 1;
+    }
+
+    // Handle this as arithmetic unary case.
+    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+
+    // Extract to a GP register because this is integral typed.
+    temp = AllocTemp();
+    NewLIR3(extract_opcode, temp.GetReg(), vector_src.GetReg(), 0);
+  }
+
+  if (opsize != k64 && opsize != kSingle && opsize != kDouble) {
+    // The logic below looks very similar to the handling of ADD_INT_2ADDR
+    // except the rhs is not a VR but a physical register allocated above.
+    // No load of source VR is done because it assumes that rl_result will
+    // share physical register / memory location.
+    rl_result = UpdateLocTyped(rl_dest, kCoreReg);
+    if (rl_result.location == kLocPhysReg) {
+      // Ensure res is in a core reg.
+      rl_result = EvalLoc(rl_dest, kCoreReg, true);
+      OpRegReg(kOpAdd, rl_result.reg, temp);
+      StoreFinalValue(rl_dest, rl_result);
+    } else {
+      // Do the addition directly to memory.
+      OpMemReg(kOpAdd, rl_result, temp.GetReg());
+    }
+  }
 }
 
 void X86Mir2Lir::GenReduceVector(BasicBlock *bb, MIR *mir) {
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegLocation rl_dest = mir_graph_->GetDest(mir);
-  RegStorage rs_src1 = RegStorage::Solo128(mir->dalvikInsn.vB);
-  int extract_index = mir->dalvikInsn.arg[0];
-  int extr_opcode = 0;
+  RegStorage vector_src = RegStorage::Solo128(mir->dalvikInsn.vB);
   RegLocation rl_result;
   bool is_wide = false;
 
-  switch (opsize) {
-    case k32:
-      rl_result = UpdateLocTyped(rl_dest, kCoreReg);
-      extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrdMRI : kX86PextrdRRI;
-      break;
-    case kSignedHalf:
-    case kUnsignedHalf:
-      rl_result= UpdateLocTyped(rl_dest, kCoreReg);
-      extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrwMRI : kX86PextrwRRI;
-      break;
-    default:
-      LOG(FATAL) << "Unsupported vector add reduce " << opsize;
-      return;
-      break;
-  }
+  // There is a different path depending on type and size.
+  if (opsize == kSingle) {
+    // Handle float case.
+    // TODO Add support for fast math (not value safe) and do horizontal add in that case.
 
-  if (rl_result.location == kLocPhysReg) {
-    NewLIR3(extr_opcode, rl_result.reg.GetReg(), rs_src1.GetReg(), extract_index);
-    if (is_wide == true) {
+    rl_result = EvalLoc(rl_dest, kFPReg, true);
+    NewLIR2(kX86PxorRR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
+    NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+
+    // Since FP must keep order of operation for value safety, we shift to low
+    // 32-bits and add to result.
+    for (int i = 0; i < 3; i++) {
+      NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), 0x39);
+      NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+    }
+
+    StoreValue(rl_dest, rl_result);
+  } else if (opsize == kDouble) {
+    // TODO Handle double case.
+    LOG(FATAL) << "Unsupported add reduce for double.";
+  } else if (opsize == k64) {
+    /*
+     * Handle long case:
+     * 1) Reduce the vector register to lower half (with addition).
+     * 1-1) Get an xmm temp and fill it with vector register.
+     * 1-2) Shift the xmm temp by 8-bytes.
+     * 1-3) Add the xmm temp to vector register that is being reduced.
+     * 2) Evaluate destination to a GP / GP pair.
+     * 2-1) In 64-bit case, use movq to move result to a 64-bit GP.
+     * 2-2) In 32-bit case, use movd twice to move to 32-bit GP pair.
+     * 3) Store the result to the final destination.
+     */
+    NewLIR2(kX86PsrldqRI, vector_src.GetReg(), 8);
+    rl_result = EvalLocWide(rl_dest, kCoreReg, true);
+    if (cu_->target64) {
+      DCHECK(!rl_result.reg.IsPair());
+      NewLIR2(kX86MovqrxRR, rl_result.reg.GetReg(), vector_src.GetReg());
+    } else {
+      NewLIR2(kX86MovdrxRR, rl_result.reg.GetLowReg(), vector_src.GetReg());
+      NewLIR2(kX86PsrlqRI, vector_src.GetReg(), 0x20);
+      NewLIR2(kX86MovdrxRR, rl_result.reg.GetHighReg(), vector_src.GetReg());
+    }
+
+    StoreValueWide(rl_dest, rl_result);
+  } else {
+    int extract_index = mir->dalvikInsn.arg[0];
+    int extr_opcode = 0;
+    rl_result = UpdateLocTyped(rl_dest, kCoreReg);
+
+    // Handle the rest of integral types now.
+    switch (opsize) {
+      case k32:
+        extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrdRRI : kX86PextrdMRI;
+        break;
+      case kSignedHalf:
+      case kUnsignedHalf:
+        extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrwRRI : kX86PextrwMRI;
+        break;
+      case kSignedByte:
+        extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrbRRI : kX86PextrbMRI;
+        break;
+      default:
+        LOG(FATAL) << "Unsupported vector reduce " << opsize;
+        return;
+    }
+
+    if (rl_result.location == kLocPhysReg) {
+      NewLIR3(extr_opcode, rl_result.reg.GetReg(), vector_src.GetReg(), extract_index);
       StoreFinalValue(rl_dest, rl_result);
     } else {
-      StoreFinalValueWide(rl_dest, rl_result);
+      int displacement = SRegOffset(rl_result.s_reg_low);
+      LIR *l = NewLIR3(extr_opcode, rs_rX86_SP.GetReg(), displacement, vector_src.GetReg());
+      AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
+      AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
     }
+  }
+}
+
+void X86Mir2Lir::LoadVectorRegister(RegStorage rs_dest, RegStorage rs_src,
+                                    OpSize opsize, int op_mov) {
+  if (!cu_->target64 && opsize == k64) {
+    // Logic assumes that longs are loaded in GP register pairs.
+    NewLIR2(kX86MovdxrRR, rs_dest.GetReg(), rs_src.GetLowReg());
+    RegStorage r_tmp = AllocTempDouble();
+    NewLIR2(kX86MovdxrRR, r_tmp.GetReg(), rs_src.GetHighReg());
+    NewLIR2(kX86PunpckldqRR, rs_dest.GetReg(), r_tmp.GetReg());
+    FreeTemp(r_tmp);
   } else {
-    int displacement = SRegOffset(rl_result.s_reg_low);
-    LIR *l = NewLIR3(extr_opcode, rs_rX86_SP.GetReg(), displacement, rs_src1.GetReg());
-    AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
-    AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
+    NewLIR2(op_mov, rs_dest.GetReg(), rs_src.GetReg());
   }
 }
 
@@ -2275,96 +2335,104 @@
   DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
   OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
   RegStorage rs_dest = RegStorage::Solo128(mir->dalvikInsn.vA);
-  int op_low = 0, op_high = 0, imm = 0, op_mov = kX86MovdxrRR;
+  Clobber(rs_dest);
+  int op_shuffle = 0, op_shuffle_high = 0, op_mov = kX86MovdxrRR;
   RegisterClass reg_type = kCoreReg;
+  bool is_wide = false;
 
   switch (opsize) {
     case k32:
-      op_low = kX86PshufdRRI;
+      op_shuffle = kX86PshufdRRI;
       break;
     case kSingle:
-      op_low = kX86PshufdRRI;
-      op_mov = kX86Mova128RR;
+      op_shuffle = kX86PshufdRRI;
+      op_mov = kX86MovdqaRR;
       reg_type = kFPReg;
       break;
     case k64:
-      op_low = kX86PshufdRRI;
-      imm = 0x44;
-      break;
-    case kDouble:
-      op_low = kX86PshufdRRI;
-      op_mov = kX86Mova128RR;
-      reg_type = kFPReg;
-      imm = 0x44;
+      op_shuffle = kX86PunpcklqdqRR;
+      op_mov = kX86MovqxrRR;
+      is_wide = true;
       break;
     case kSignedByte:
     case kUnsignedByte:
-      // Shuffle 8 bit value into 16 bit word.
-      // We set val = val + (val << 8) below and use 16 bit shuffle.
+      // We will have the source loaded up in a
+      // double-word before we use this shuffle
+      op_shuffle = kX86PshufdRRI;
+      break;
     case kSignedHalf:
     case kUnsignedHalf:
       // Handles low quadword.
-      op_low = kX86PshuflwRRI;
+      op_shuffle = kX86PshuflwRRI;
       // Handles upper quadword.
-      op_high = kX86PshufdRRI;
+      op_shuffle_high = kX86PshufdRRI;
       break;
     default:
       LOG(FATAL) << "Unsupported vector set " << opsize;
       break;
   }
 
-  RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
-
-  // Load the value from the VR into the reg.
-  if (rl_src.wide == 0) {
+  // Load the value from the VR into a physical register.
+  RegLocation rl_src;
+  if (!is_wide) {
+    rl_src = mir_graph_->GetSrc(mir, 0);
     rl_src = LoadValue(rl_src, reg_type);
   } else {
+    rl_src = mir_graph_->GetSrcWide(mir, 0);
     rl_src = LoadValueWide(rl_src, reg_type);
   }
-
-  // If opsize is 8 bits wide then double value and use 16 bit shuffle instead.
-  if (opsize == kSignedByte || opsize == kUnsignedByte) {
-    RegStorage temp = AllocTemp();
-    // val = val + (val << 8).
-    NewLIR2(kX86Mov32RR, temp.GetReg(), rl_src.reg.GetReg());
-    NewLIR2(kX86Sal32RI, temp.GetReg(), 8);
-    NewLIR2(kX86Or32RR, rl_src.reg.GetReg(), temp.GetReg());
-    FreeTemp(temp);
-  }
+  RegStorage reg_to_shuffle = rl_src.reg;
 
   // Load the value into the XMM register.
-  NewLIR2(op_mov, rs_dest.GetReg(), rl_src.reg.GetReg());
+  LoadVectorRegister(rs_dest, reg_to_shuffle, opsize, op_mov);
+
+  if (opsize == kSignedByte || opsize == kUnsignedByte) {
+    // In the byte case, first duplicate it to be a word
+    // Then duplicate it to be a double-word
+    NewLIR2(kX86PunpcklbwRR, rs_dest.GetReg(), rs_dest.GetReg());
+    NewLIR2(kX86PunpcklwdRR, rs_dest.GetReg(), rs_dest.GetReg());
+  }
 
   // Now shuffle the value across the destination.
-  NewLIR3(op_low, rs_dest.GetReg(), rs_dest.GetReg(), imm);
+  if (op_shuffle == kX86PunpcklqdqRR) {
+    NewLIR2(op_shuffle, rs_dest.GetReg(), rs_dest.GetReg());
+  } else {
+    NewLIR3(op_shuffle, rs_dest.GetReg(), rs_dest.GetReg(), 0);
+  }
 
   // And then repeat as needed.
-  if (op_high != 0) {
-    NewLIR3(op_high, rs_dest.GetReg(), rs_dest.GetReg(), imm);
+  if (op_shuffle_high != 0) {
+    NewLIR3(op_shuffle_high, rs_dest.GetReg(), rs_dest.GetReg(), 0);
   }
 }
 
-LIR *X86Mir2Lir::ScanVectorLiteral(MIR *mir) {
-  int *args = reinterpret_cast<int*>(mir->dalvikInsn.arg);
+void X86Mir2Lir::GenPackedArrayGet(BasicBlock *bb, MIR *mir) {
+  UNIMPLEMENTED(FATAL) << "Extended opcode kMirOpPackedArrayGet not supported.";
+}
+
+void X86Mir2Lir::GenPackedArrayPut(BasicBlock *bb, MIR *mir) {
+  UNIMPLEMENTED(FATAL) << "Extended opcode kMirOpPackedArrayPut not supported.";
+}
+
+LIR* X86Mir2Lir::ScanVectorLiteral(int32_t* constants) {
   for (LIR *p = const_vectors_; p != nullptr; p = p->next) {
-    if (args[0] == p->operands[0] && args[1] == p->operands[1] &&
-        args[2] == p->operands[2] && args[3] == p->operands[3]) {
+    if (constants[0] == p->operands[0] && constants[1] == p->operands[1] &&
+        constants[2] == p->operands[2] && constants[3] == p->operands[3]) {
       return p;
     }
   }
   return nullptr;
 }
 
-LIR *X86Mir2Lir::AddVectorLiteral(MIR *mir) {
+LIR* X86Mir2Lir::AddVectorLiteral(int32_t* constants) {
   LIR* new_value = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocData));
-  int *args = reinterpret_cast<int*>(mir->dalvikInsn.arg);
-  new_value->operands[0] = args[0];
-  new_value->operands[1] = args[1];
-  new_value->operands[2] = args[2];
-  new_value->operands[3] = args[3];
+  new_value->operands[0] = constants[0];
+  new_value->operands[1] = constants[1];
+  new_value->operands[2] = constants[2];
+  new_value->operands[3] = constants[3];
   new_value->next = const_vectors_;
   if (const_vectors_ == nullptr) {
-    estimated_native_code_size_ += 12;  // Amount needed to align to 16 byte boundary.
+    estimated_native_code_size_ += 12;  // Maximum needed to align to 16 byte boundary.
   }
   estimated_native_code_size_ += 16;  // Space for one vector.
   const_vectors_ = new_value;
@@ -2429,11 +2497,11 @@
   }
 
   if (!in_to_reg_storage_mapping_.IsInitialized()) {
-    int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+    int start_vreg = cu_->mir_graph->GetFirstInVR();
     RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
 
     InToRegStorageX86_64Mapper mapper(this);
-    in_to_reg_storage_mapping_.Initialize(arg_locs, cu_->num_ins, &mapper);
+    in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
   }
   return in_to_reg_storage_mapping_.Get(arg_num);
 }
@@ -2482,11 +2550,11 @@
     StoreRefDisp(rs_rX86_SP, 0, As32BitReg(TargetReg(kArg0, kRef)), kNotVolatile);
   }
 
-  if (cu_->num_ins == 0) {
+  if (mir_graph_->GetNumOfInVRs() == 0) {
     return;
   }
 
-  int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+  int start_vreg = cu_->mir_graph->GetFirstInVR();
   /*
    * Copy incoming arguments to their proper home locations.
    * NOTE: an older version of dx had an issue in which
@@ -2500,7 +2568,7 @@
    * half to memory as well.
    */
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-  for (int i = 0; i < cu_->num_ins; i++) {
+  for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
     // get reg corresponding to input
     RegStorage reg = GetArgMappingToPhysicalReg(i);
 
@@ -2632,9 +2700,6 @@
       }
     }
 
-    // Logic below assumes that Method pointer is at offset zero from SP.
-    DCHECK_EQ(VRegOffset(static_cast<int>(kVRegMethodPtrBaseReg)), 0);
-
     // The rest can be copied together
     int start_offset = SRegOffset(info->args[last_mapped_in + size_of_the_last_mapped].s_reg_low);
     int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + size_of_the_last_mapped,
@@ -2909,4 +2974,46 @@
   return true;
 }
 
+/**
+ * Lock temp registers for explicit usage. Registers will be freed in destructor.
+ */
+X86Mir2Lir::ExplicitTempRegisterLock::ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir,
+                                                               int n_regs, ...) :
+    temp_regs_(n_regs),
+    mir_to_lir_(mir_to_lir) {
+  va_list regs;
+  va_start(regs, n_regs);
+  for (int i = 0; i < n_regs; i++) {
+    RegStorage reg = *(va_arg(regs, RegStorage*));
+    RegisterInfo* info = mir_to_lir_->GetRegInfo(reg);
+
+    // Make sure we don't have promoted register here.
+    DCHECK(info->IsTemp());
+
+    temp_regs_.push_back(reg);
+    mir_to_lir_->FlushReg(reg);
+
+    if (reg.IsPair()) {
+      RegStorage partner = info->Partner();
+      temp_regs_.push_back(partner);
+      mir_to_lir_->FlushReg(partner);
+    }
+
+    mir_to_lir_->Clobber(reg);
+    mir_to_lir_->LockTemp(reg);
+  }
+
+  va_end(regs);
+}
+
+/*
+ * Free all locked registers.
+ */
+X86Mir2Lir::ExplicitTempRegisterLock::~ExplicitTempRegisterLock() {
+  // Free all locked temps.
+  for (auto it : temp_regs_) {
+    mir_to_lir_->FreeTemp(it);
+  }
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index a48613f..30384ec 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -592,7 +592,6 @@
                            kDouble, kNotVolatile);
         res->target = data_target;
         res->flags.fixup = kFixupLoad;
-        Clobber(rl_method.reg);
         store_method_addr_used_ = true;
       } else {
         if (r_dest.IsPair()) {
@@ -779,15 +778,20 @@
 }
 
 LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale,
-                                      int displacement, RegStorage r_src, OpSize size) {
+                                      int displacement, RegStorage r_src, OpSize size,
+                                      int opt_flags) {
   LIR *store = NULL;
   LIR *store2 = NULL;
   bool is_array = r_index.Valid();
   bool pair = r_src.IsPair();
   bool is64bit = (size == k64) || (size == kDouble);
+  bool consider_non_temporal = false;
+
   X86OpCode opcode = kX86Nop;
   switch (size) {
     case k64:
+      consider_non_temporal = true;
+      // Fall through!
     case kDouble:
       if (r_src.IsFloat()) {
         opcode = is_array ? kX86MovsdAR : kX86MovsdMR;
@@ -804,6 +808,7 @@
         opcode = is_array ? kX86Mov64AR  : kX86Mov64MR;
         CHECK_EQ(is_array, false);
         CHECK_EQ(r_src.IsFloat(), false);
+        consider_non_temporal = true;
         break;
       }  // else fall-through to k32 case
     case k32:
@@ -815,6 +820,7 @@
         DCHECK(r_src.IsSingle());
       }
       DCHECK_EQ((displacement & 0x3), 0);
+      consider_non_temporal = true;
       break;
     case kUnsignedHalf:
     case kSignedHalf:
@@ -829,6 +835,28 @@
       LOG(FATAL) << "Bad case in StoreBaseIndexedDispBody";
   }
 
+  // Handle non temporal hint here.
+  if (consider_non_temporal && ((opt_flags & MIR_STORE_NON_TEMPORAL) != 0)) {
+    switch (opcode) {
+      // We currently only handle 32/64 bit moves here.
+      case kX86Mov64AR:
+        opcode = kX86Movnti64AR;
+        break;
+      case kX86Mov64MR:
+        opcode = kX86Movnti64MR;
+        break;
+      case kX86Mov32AR:
+        opcode = kX86Movnti32AR;
+        break;
+      case kX86Mov32MR:
+        opcode = kX86Movnti32MR;
+        break;
+      default:
+        // Do nothing here.
+        break;
+    }
+  }
+
   if (!is_array) {
     if (!pair) {
       store = NewLIR3(opcode, r_base.GetReg(), displacement + LOWORD_OFFSET, r_src.GetReg());
@@ -875,6 +903,17 @@
 
   // StoreBaseDisp() will emit correct insn for atomic store on x86
   // assuming r_dest is correctly prepared using RegClassForFieldLoadStore().
+  // x86 only allows registers EAX-EDX to be used as byte registers, if the input src is not
+  // valid, allocate a temp.
+  bool allocated_temp = false;
+  if (size == kUnsignedByte || size == kSignedByte) {
+    if (!cu_->target64 && !r_src.Low4()) {
+      RegStorage r_input = r_src;
+      r_src = AllocateByteRegister();
+      OpRegCopy(r_src, r_input);
+      allocated_temp = true;
+    }
+  }
 
   LIR* store = StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, size);
 
@@ -884,6 +923,10 @@
     GenMemBarrier(kAnyAny);
   }
 
+  if (allocated_temp) {
+    FreeTemp(r_src);
+  }
+
   return store;
 }
 
@@ -913,7 +956,8 @@
 
   // Did we need a pointer to the method code?
   if (store_method_addr_) {
-    base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempVR, cu_->target64 == true);
+    base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, cu_->target64 == true);
+    DCHECK(base_of_code_ != nullptr);
   } else {
     base_of_code_ = nullptr;
   }
@@ -946,6 +990,17 @@
     case kMirOpConstVector:
       store_method_addr_ = true;
       break;
+    case kMirOpPackedMultiply:
+    case kMirOpPackedShiftLeft:
+    case kMirOpPackedSignedShiftRight:
+    case kMirOpPackedUnsignedShiftRight: {
+      // Byte emulation requires constants from the literal pool.
+      OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
+      if (opsize == kSignedByte || opsize == kUnsignedByte) {
+        store_method_addr_ = true;
+      }
+      break;
+    }
     default:
       // Ignore the rest.
       break;
@@ -980,6 +1035,7 @@
       store_method_addr_ = true;
       break;
     case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_STATIC_RANGE:
       AnalyzeInvokeStatic(opcode, bb, mir);
       break;
     default:
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 500c6b8..22a2f30 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -75,33 +75,36 @@
  *  ST1 .. ST7: caller save
  *
  *  Stack frame diagram (stack grows down, higher addresses at top):
+ *  For a more detailed view of each region see stack.h.
  *
- * +------------------------+
- * | IN[ins-1]              |  {Note: resides in caller's frame}
- * |       .                |
- * | IN[0]                  |
- * | caller's Method*       |
- * +========================+  {Note: start of callee's frame}
- * | return address         |  {pushed by call}
- * | spill region           |  {variable sized}
- * +------------------------+
- * | ...filler word...      |  {Note: used as 2nd word of V[locals-1] if long]
- * +------------------------+
- * | V[locals-1]            |
- * | V[locals-2]            |
- * |      .                 |
- * |      .                 |
- * | V[1]                   |
- * | V[0]                   |
- * +------------------------+
- * |  0 to 3 words padding  |
- * +------------------------+
- * | OUT[outs-1]            |
- * | OUT[outs-2]            |
- * |       .                |
- * | OUT[0]                 |
- * | cur_method*            | <<== sp w/ 16-byte alignment
- * +========================+
+ * +---------------------------+
+ * | IN[ins-1]                 |  {Note: resides in caller's frame}
+ * |       .                   |
+ * | IN[0]                     |
+ * | caller's Method*          |
+ * +===========================+  {Note: start of callee's frame}
+ * | return address            |  {pushed by call}
+ * | spill region              |  {variable sized}
+ * +---------------------------+
+ * | ...filler 4-bytes...      |  {Note: used as 2nd word of V[locals-1] if long]
+ * +---------------------------+
+ * | V[locals-1]               |
+ * | V[locals-2]               |
+ * |      .                    |
+ * |      .                    |
+ * | V[1]                      |
+ * | V[0]                      |
+ * +---------------------------+
+ * | 0 to 12-bytes padding     |
+ * +---------------------------+
+ * | compiler temp region      |
+ * +---------------------------+
+ * | OUT[outs-1]               |
+ * | OUT[outs-2]               |
+ * |       .                   |
+ * | OUT[0]                    |
+ * | StackReference<ArtMethod> | <<== sp w/ 16-byte alignment
+ * +===========================+
  */
 
 enum X86ResourceEncodingPos {
@@ -440,12 +443,12 @@
   kX86Mov16MR, kX86Mov16AR, kX86Mov16TR,
   kX86Mov16RR, kX86Mov16RM, kX86Mov16RA, kX86Mov16RT,
   kX86Mov16RI, kX86Mov16MI, kX86Mov16AI, kX86Mov16TI,
-  kX86Mov32MR, kX86Mov32AR, kX86Mov32TR,
+  kX86Mov32MR, kX86Mov32AR, kX86Movnti32MR, kX86Movnti32AR, kX86Mov32TR,
   kX86Mov32RR, kX86Mov32RM, kX86Mov32RA, kX86Mov32RT,
   kX86Mov32RI, kX86Mov32MI, kX86Mov32AI, kX86Mov32TI,
   kX86Lea32RM,
   kX86Lea32RA,
-  kX86Mov64MR, kX86Mov64AR, kX86Mov64TR,
+  kX86Mov64MR, kX86Mov64AR, kX86Movnti64MR, kX86Movnti64AR, kX86Mov64TR,
   kX86Mov64RR, kX86Mov64RM, kX86Mov64RA, kX86Mov64RT,
   kX86Mov64RI32, kX86Mov64RI64, kX86Mov64MI, kX86Mov64AI, kX86Mov64TI,
   kX86Lea64RM,
@@ -484,8 +487,10 @@
 #undef BinaryShiftOpcode
   kX86Cmc,
   kX86Shld32RRI,
+  kX86Shld32RRC,
   kX86Shld32MRI,
   kX86Shrd32RRI,
+  kX86Shrd32RRC,
   kX86Shrd32MRI,
   kX86Shld64RRI,
   kX86Shld64MRI,
@@ -550,20 +555,27 @@
   Binary0fOpCode(kX86Subss),    // float subtract
   Binary0fOpCode(kX86Divsd),    // double divide
   Binary0fOpCode(kX86Divss),    // float divide
-  Binary0fOpCode(kX86Punpckldq),  // Interleave low-order double words
+  Binary0fOpCode(kX86Punpcklbw),  // Interleave low-order bytes
+  Binary0fOpCode(kX86Punpcklwd),  // Interleave low-order single words (16-bits)
+  Binary0fOpCode(kX86Punpckldq),  // Interleave low-order double words (32-bit)
+  Binary0fOpCode(kX86Punpcklqdq),  // Interleave low-order quad word
   Binary0fOpCode(kX86Sqrtsd),   // square root
   Binary0fOpCode(kX86Pmulld),   // parallel integer multiply 32 bits x 4
   Binary0fOpCode(kX86Pmullw),   // parallel integer multiply 16 bits x 8
+  Binary0fOpCode(kX86Pmuludq),   // parallel unsigned 32 integer and stores result as 64
   Binary0fOpCode(kX86Mulps),    // parallel FP multiply 32 bits x 4
   Binary0fOpCode(kX86Mulpd),    // parallel FP multiply 64 bits x 2
   Binary0fOpCode(kX86Paddb),    // parallel integer addition 8 bits x 16
   Binary0fOpCode(kX86Paddw),    // parallel integer addition 16 bits x 8
   Binary0fOpCode(kX86Paddd),    // parallel integer addition 32 bits x 4
+  Binary0fOpCode(kX86Paddq),    // parallel integer addition 64 bits x 2
+  Binary0fOpCode(kX86Psadbw),   // computes sum of absolute differences for unsigned byte integers
   Binary0fOpCode(kX86Addps),    // parallel FP addition 32 bits x 4
   Binary0fOpCode(kX86Addpd),    // parallel FP addition 64 bits x 2
   Binary0fOpCode(kX86Psubb),    // parallel integer subtraction 8 bits x 16
   Binary0fOpCode(kX86Psubw),    // parallel integer subtraction 16 bits x 8
   Binary0fOpCode(kX86Psubd),    // parallel integer subtraction 32 bits x 4
+  Binary0fOpCode(kX86Psubq),    // parallel integer subtraction 32 bits x 4
   Binary0fOpCode(kX86Subps),    // parallel FP subtraction 32 bits x 4
   Binary0fOpCode(kX86Subpd),    // parallel FP subtraction 64 bits x 2
   Binary0fOpCode(kX86Pand),     // parallel AND 128 bits x 1
@@ -588,6 +600,7 @@
   kX86PsrlwRI,                  // logical right shift of floating point registers 16 bits x 8
   kX86PsrldRI,                  // logical right shift of floating point registers 32 bits x 4
   kX86PsrlqRI,                  // logical right shift of floating point registers 64 bits x 2
+  kX86PsrldqRI,                 // logical shift of 128-bit vector register, immediate in bytes
   kX86PsllwRI,                  // left shift of floating point registers 16 bits x 8
   kX86PslldRI,                  // left shift of floating point registers 32 bits x 4
   kX86PsllqRI,                  // left shift of floating point registers 64 bits x 2
@@ -602,8 +615,8 @@
   kX86Fprem,                    // remainder from dividing of two floating point values
   kX86Fucompp,                  // compare floating point values and pop x87 fp stack twice
   kX86Fstsw16R,                 // store FPU status word
-  Binary0fOpCode(kX86Mova128),  // move 128 bits aligned
-  kX86Mova128MR, kX86Mova128AR,  // store 128 bit aligned from xmm1 to m128
+  Binary0fOpCode(kX86Movdqa),   // move 128 bits aligned
+  kX86MovdqaMR, kX86MovdqaAR,   // store 128 bit aligned from xmm1 to m128
   Binary0fOpCode(kX86Movups),   // load unaligned packed single FP values from xmm2/m128 to xmm1
   kX86MovupsMR, kX86MovupsAR,   // store unaligned packed single FP values from xmm1 to m128
   Binary0fOpCode(kX86Movaps),   // load aligned packed single FP values from xmm2/m128 to xmm1
@@ -618,7 +631,12 @@
   kX86MovdrxRR, kX86MovdrxMR, kX86MovdrxAR,  // move into reg from xmm
   kX86MovsxdRR, kX86MovsxdRM, kX86MovsxdRA,  // move 32 bit to 64 bit with sign extension
   kX86Set8R, kX86Set8M, kX86Set8A,  // set byte depending on condition operand
-  kX86Mfence,                   // memory barrier
+  kX86Lfence,                   // memory barrier to serialize all previous
+                                // load-from-memory instructions
+  kX86Mfence,                   // memory barrier to serialize all previous
+                                // load-from-memory and store-to-memory instructions
+  kX86Sfence,                   // memory barrier to serialize all previous
+                                // store-to-memory instructions
   Binary0fOpCode(kX86Imul16),   // 16bit multiply
   Binary0fOpCode(kX86Imul32),   // 32bit multiply
   Binary0fOpCode(kX86Imul64),   // 64bit multiply
@@ -675,6 +693,7 @@
   kMemRegImm,                               // MRI instruction kinds.
   kShiftRegImm, kShiftMemImm, kShiftArrayImm,  // Shift opcode with immediate.
   kShiftRegCl, kShiftMemCl, kShiftArrayCl,     // Shift opcode with register CL.
+  kShiftRegRegCl,
   // kRegRegReg, kRegRegMem, kRegRegArray,    // RRR, RRM, RRA instruction kinds.
   kRegCond, kMemCond, kArrayCond,          // R, M, A instruction kinds following by a condition.
   kRegRegCond,                             // RR instruction kind followed by a condition.
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index e26745a..3cc573b 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "base/bit_vector-inl.h"
 #include "compiler_internals.h"
 #include "dataflow_iterator-inl.h"
 #include "utils/scoped_arena_containers.h"
@@ -44,12 +45,7 @@
     res = NeedsVisit(GetBasicBlock(bb->taken));
     if (res == NULL) {
       if (bb->successor_block_list_type != kNotUsed) {
-        GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
-        while (true) {
-          SuccessorBlockInfo *sbi = iterator.Next();
-          if (sbi == NULL) {
-            break;
-          }
+        for (SuccessorBlockInfo* sbi : bb->successor_blocks) {
           res = NeedsVisit(GetBasicBlock(sbi->block));
           if (res != NULL) {
             break;
@@ -65,7 +61,7 @@
   block->visited = true;
   /* Enqueue the pre_order block id */
   if (block->id != NullBasicBlockId) {
-    dfs_order_->Insert(block->id);
+    dfs_order_.push_back(block->id);
   }
 }
 
@@ -82,9 +78,9 @@
       succ.push_back(next_successor);
       continue;
     }
-    curr->dfs_id = dfs_post_order_->Size();
+    curr->dfs_id = dfs_post_order_.size();
     if (curr->id != NullBasicBlockId) {
-      dfs_post_order_->Insert(curr->id);
+      dfs_post_order_.push_back(curr->id);
     }
     succ.pop_back();
   }
@@ -92,23 +88,11 @@
 
 /* Sort the blocks by the Depth-First-Search */
 void MIRGraph::ComputeDFSOrders() {
-  /* Initialize or reset the DFS pre_order list */
-  if (dfs_order_ == NULL) {
-    dfs_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
-                                                          kGrowableArrayDfsOrder);
-  } else {
-    /* Just reset the used length on the counter */
-    dfs_order_->Reset();
-  }
-
-  /* Initialize or reset the DFS post_order list */
-  if (dfs_post_order_ == NULL) {
-    dfs_post_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
-                                                               kGrowableArrayDfsPostOrder);
-  } else {
-    /* Just reset the used length on the counter */
-    dfs_post_order_->Reset();
-  }
+  /* Clear the DFS pre-order and post-order lists. */
+  dfs_order_.clear();
+  dfs_order_.reserve(GetNumBlocks());
+  dfs_post_order_.clear();
+  dfs_post_order_.reserve(GetNumBlocks());
 
   // Reset visited flags from all nodes
   ClearAllVisitedFlags();
@@ -116,7 +100,7 @@
   // Record dfs orders
   RecordDFSOrders(GetEntryBlock());
 
-  num_reachable_blocks_ = dfs_order_->Size();
+  num_reachable_blocks_ = dfs_order_.size();
 
   if (num_reachable_blocks_ != num_blocks_) {
     // Hide all unreachable blocks.
@@ -146,8 +130,8 @@
 }
 
 void MIRGraph::ComputeDefBlockMatrix() {
-  int num_registers = cu_->num_dalvik_registers;
-  /* Allocate num_dalvik_registers bit vector pointers */
+  int num_registers = GetNumOfCodeAndTempVRs();
+  /* Allocate num_registers bit vector pointers */
   def_block_matrix_ = static_cast<ArenaBitVector**>
       (arena_->Alloc(sizeof(ArenaBitVector *) * num_registers,
                      kArenaAllocDFInfo));
@@ -158,6 +142,7 @@
     def_block_matrix_[i] =
         new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapBMatrix);
   }
+
   AllNodesIterator iter(this);
   for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
     FindLocalLiveIn(bb);
@@ -171,22 +156,18 @@
    * Also set the incoming parameters as defs in the entry block.
    * Only need to handle the parameters for the outer method.
    */
-  int num_regs = cu_->num_dalvik_registers;
-  int in_reg = num_regs - cu_->num_ins;
+  int num_regs = GetNumOfCodeVRs();
+  int in_reg = GetFirstInVR();
   for (; in_reg < num_regs; in_reg++) {
     def_block_matrix_[in_reg]->SetBit(GetEntryBlock()->id);
   }
 }
 
 void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) {
-  if (dom_post_order_traversal_ == NULL || max_num_reachable_blocks_ < num_reachable_blocks_) {
-    // First time or too small - create the array.
-    dom_post_order_traversal_ =
-        new (arena_) GrowableArray<BasicBlockId>(arena_, num_reachable_blocks_,
-                                        kGrowableArrayDomPostOrderTraversal);
-  } else {
-    dom_post_order_traversal_->Reset();
-  }
+  // Clear the dominator post-order list.
+  dom_post_order_traversal_.clear();
+  dom_post_order_traversal_.reserve(num_reachable_blocks_);
+
   ClearAllVisitedFlags();
   DCHECK(temp_scoped_alloc_.get() != nullptr);
   ScopedArenaVector<std::pair<BasicBlock*, ArenaBitVector::IndexIterator>> work_stack(
@@ -209,7 +190,7 @@
     } else {
       // no successor/next
       if (curr_bb->id != NullBasicBlockId) {
-        dom_post_order_traversal_->Insert(curr_bb->id);
+        dom_post_order_traversal_.push_back(curr_bb->id);
       }
       work_stack.pop_back();
 
@@ -245,15 +226,10 @@
     CheckForDominanceFrontier(bb, GetBasicBlock(bb->fall_through));
   }
   if (bb->successor_block_list_type != kNotUsed) {
-    GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
-      while (true) {
-        SuccessorBlockInfo *successor_block_info = iterator.Next();
-        if (successor_block_info == NULL) {
-          break;
-        }
-        BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
-        CheckForDominanceFrontier(bb, succ_bb);
-      }
+    for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
+      BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
+      CheckForDominanceFrontier(bb, succ_bb);
+    }
   }
 
   /* Calculate DF_up */
@@ -274,11 +250,11 @@
 
   if (bb->dominators == NULL) {
     bb->dominators = new (arena_) ArenaBitVector(arena_, num_total_blocks,
-                                                 false /* expandable */, kBitMapDominators);
+                                                 true /* expandable */, kBitMapDominators);
     bb->i_dominated = new (arena_) ArenaBitVector(arena_, num_total_blocks,
-                                                  false /* expandable */, kBitMapIDominated);
+                                                  true /* expandable */, kBitMapIDominated);
     bb->dom_frontier = new (arena_) ArenaBitVector(arena_, num_total_blocks,
-                                                   false /* expandable */, kBitMapDomFrontier);
+                                                   true /* expandable */, kBitMapDomFrontier);
   } else {
     bb->dominators->ClearAllBits();
     bb->i_dominated->ClearAllBits();
@@ -317,13 +293,14 @@
   }
 
   /* Iterate through the predecessors */
-  GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
+  auto it = bb->predecessors.begin(), end = bb->predecessors.end();
 
   /* Find the first processed predecessor */
   int idom = -1;
-  while (true) {
-    BasicBlock* pred_bb = GetBasicBlock(iter.Next());
-    CHECK(pred_bb != NULL);
+  for ( ; ; ++it) {
+    CHECK(it != end);
+    BasicBlock* pred_bb = GetBasicBlock(*it);
+    DCHECK(pred_bb != nullptr);
     if (i_dom_list_[pred_bb->dfs_id] != NOTVISITED) {
       idom = pred_bb->dfs_id;
       break;
@@ -331,11 +308,9 @@
   }
 
   /* Scan the rest of the predecessors */
-  while (true) {
-      BasicBlock* pred_bb = GetBasicBlock(iter.Next());
-      if (!pred_bb) {
-        break;
-      }
+  for ( ; it != end; ++it) {
+      BasicBlock* pred_bb = GetBasicBlock(*it);
+      DCHECK(pred_bb != nullptr);
       if (i_dom_list_[pred_bb->dfs_id] == NOTVISITED) {
         continue;
       } else {
@@ -368,7 +343,7 @@
   if (bb != GetEntryBlock()) {
     int idom_dfs_idx = i_dom_list_[bb->dfs_id];
     DCHECK_NE(idom_dfs_idx, NOTVISITED);
-    int i_dom_idx = dfs_post_order_->Get(idom_dfs_idx);
+    int i_dom_idx = dfs_post_order_[idom_dfs_idx];
     BasicBlock* i_dom = GetBasicBlock(i_dom_idx);
     bb->i_dom = i_dom->id;
     /* Add bb to the i_dominated set of the immediate dominator block */
@@ -456,7 +431,7 @@
  * insert a phi node if the variable is live-in to the block.
  */
 bool MIRGraph::ComputeBlockLiveIns(BasicBlock* bb) {
-  DCHECK_EQ(temp_bit_vector_size_, cu_->num_dalvik_registers);
+  DCHECK_EQ(temp_bit_vector_size_, cu_->mir_graph.get()->GetNumOfCodeAndTempVRs());
   ArenaBitVector* temp_dalvik_register_v = temp_bit_vector_;
 
   if (bb->data_flow_info == NULL) {
@@ -472,12 +447,7 @@
     ComputeSuccLineIn(temp_dalvik_register_v, bb_fall_through->data_flow_info->live_in_v,
                       bb->data_flow_info->def_v);
   if (bb->successor_block_list_type != kNotUsed) {
-    GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
-    while (true) {
-      SuccessorBlockInfo *successor_block_info = iterator.Next();
-      if (successor_block_info == NULL) {
-        break;
-      }
+    for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
       BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
       if (succ_bb->data_flow_info) {
         ComputeSuccLineIn(temp_dalvik_register_v, succ_bb->data_flow_info->live_in_v,
@@ -507,7 +477,7 @@
   }
 
   /* Iterate through each Dalvik register */
-  for (dalvik_reg = cu_->num_dalvik_registers - 1; dalvik_reg >= 0; dalvik_reg--) {
+  for (dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
     input_blocks->Copy(def_block_matrix_[dalvik_reg]);
     phi_blocks->ClearAllBits();
     do {
@@ -554,8 +524,7 @@
     int v_reg = SRegToVReg(ssa_reg);
 
     /* Iterate through the predecessors */
-    GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
-    size_t num_uses = bb->predecessors->Size();
+    size_t num_uses = bb->predecessors.size();
     AllocateSSAUseData(mir, num_uses);
     int* uses = mir->ssa_rep->uses;
     BasicBlockId* incoming =
@@ -563,14 +532,12 @@
                                                  kArenaAllocDFInfo));
     mir->meta.phi_incoming = incoming;
     int idx = 0;
-    while (true) {
-      BasicBlock* pred_bb = GetBasicBlock(iter.Next());
-      if (!pred_bb) {
-       break;
-      }
+    for (BasicBlockId pred_id : bb->predecessors) {
+      BasicBlock* pred_bb = GetBasicBlock(pred_id);
+      DCHECK(pred_bb != nullptr);
       int ssa_reg = pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg];
       uses[idx] = ssa_reg;
-      incoming[idx] = pred_bb->id;
+      incoming[idx] = pred_id;
       idx++;
     }
   }
@@ -586,7 +553,7 @@
 
   /* Process this block */
   DoSSAConversion(block);
-  int map_size = sizeof(int) * cu_->num_dalvik_registers;
+  int map_size = sizeof(int) * GetNumOfCodeAndTempVRs();
 
   /* Save SSA map snapshot */
   ScopedArenaAllocator allocator(&cu_->arena_stack);
@@ -605,12 +572,7 @@
     memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
   }
   if (block->successor_block_list_type != kNotUsed) {
-    GrowableArray<SuccessorBlockInfo*>::Iterator iterator(block->successor_blocks);
-    while (true) {
-      SuccessorBlockInfo *successor_block_info = iterator.Next();
-      if (successor_block_info == NULL) {
-        break;
-      }
+    for (SuccessorBlockInfo* successor_block_info : block->successor_blocks) {
       BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
       DoDFSPreOrderSSARename(succ_bb);
       /* Restore SSA map snapshot */
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 1b3f2a1..9f0a696 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -38,7 +38,7 @@
 #include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
-#include "verifier/register_line.h"
+#include "verifier/reg_type-inl.h"
 #include "verifier/register_line-inl.h"
 
 namespace art {
@@ -127,7 +127,7 @@
         dex_gc_map_.push_back((i >> 8) & 0xFF);
       }
       verifier::RegisterLine* line = method_verifier->GetRegLine(i);
-      line->WriteReferenceBitMap(dex_gc_map_, ref_bitmap_bytes);
+      line->WriteReferenceBitMap(method_verifier, &dex_gc_map_, ref_bitmap_bytes);
     }
   }
   DCHECK_EQ(dex_gc_map_.size(), table_size);
@@ -151,7 +151,7 @@
       map_index++;
       verifier::RegisterLine* line = method_verifier->GetRegLine(i);
       for (size_t j = 0; j < code_item->registers_size_; j++) {
-        if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) {
+        if (line->GetRegisterType(method_verifier, j).IsNonZeroReferenceTypes()) {
           DCHECK_LT(j / 8, map.RegWidth());
           DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
         } else if ((j / 8) < map.RegWidth()) {
@@ -178,7 +178,7 @@
       local_gc_points++;
       max_insn = i;
       verifier::RegisterLine* line = method_verifier->GetRegLine(i);
-      max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg);
+      max_ref_reg = line->GetMaxNonZeroReferenceReg(method_verifier, max_ref_reg);
     }
   }
   *gc_points = local_gc_points;
@@ -216,8 +216,9 @@
     verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
     bool is_range = (inst->Opcode() ==  Instruction::INVOKE_VIRTUAL_RANGE) ||
         (inst->Opcode() ==  Instruction::INVOKE_INTERFACE_RANGE);
-    verifier::RegType&
-        reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
+    const verifier::RegType&
+        reg_type(line->GetRegisterType(method_verifier,
+                                       is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
 
     if (!reg_type.HasClass()) {
       // We will compute devirtualization information only when we know the Class of the reg type.
@@ -284,18 +285,21 @@
       const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
       bool is_safe_cast = false;
       if (code == Instruction::CHECK_CAST) {
-        verifier::RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
-        verifier::RegType& cast_type =
+        const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
+                                                                inst->VRegA_21c()));
+        const verifier::RegType& cast_type =
             method_verifier->ResolveCheckedClass(inst->VRegB_21c());
         is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type);
       } else {
-        verifier::RegType& array_type(line->GetRegisterType(inst->VRegB_23x()));
+        const verifier::RegType& array_type(line->GetRegisterType(method_verifier,
+                                                                  inst->VRegB_23x()));
         // We only know its safe to assign to an array if the array type is precise. For example,
         // an Object[] can have any type of object stored in it, but it may also be assigned a
         // String[] in which case the stores need to be of Strings.
         if (array_type.IsPreciseReference()) {
-          verifier::RegType& value_type(line->GetRegisterType(inst->VRegA_23x()));
-          verifier::RegType& component_type = method_verifier->GetRegTypeCache()
+          const verifier::RegType& value_type(line->GetRegisterType(method_verifier,
+                                                                    inst->VRegA_23x()));
+          const verifier::RegType& component_type = method_verifier->GetRegTypeCache()
               ->GetComponentType(array_type, method_verifier->GetClassLoader());
           is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
         }
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index 4a3e071..bdfab13 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -252,7 +252,7 @@
     // Special-case handling for format 35c/3rc invokes
     Instruction::Code opcode = mir->dalvikInsn.opcode;
     int flags = MIR::DecodedInstruction::IsPseudoMirOp(opcode) ?
-                  0 : Instruction::FlagsOf(mir->dalvikInsn.opcode);
+                  0 : mir->dalvikInsn.FlagsOf();
     if ((flags & Instruction::kInvoke) &&
         (attrs & (DF_FORMAT_35C | DF_FORMAT_3RC))) {
       DCHECK_EQ(next, 0);
@@ -401,33 +401,18 @@
   return changed;
 }
 
-static const char* storage_name[] = {" Frame ", "PhysReg", " Spill "};
+static const char* storage_name[] = {" Frame ", "PhysReg", " CompilerTemp "};
 
 void MIRGraph::DumpRegLocTable(RegLocation* table, int count) {
-  // FIXME: Quick-specific.  Move to Quick (and make a generic version for MIRGraph?
-  Mir2Lir* cg = static_cast<Mir2Lir*>(cu_->cg.get());
-  if (cg != NULL) {
-    for (int i = 0; i < count; i++) {
-      LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c 0x%04x S%d",
-          table[i].orig_sreg, storage_name[table[i].location],
-          table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
-          table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
-          table[i].is_const ? 'c' : 'n',
-          table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
-          table[i].reg.GetRawBits(),
-          table[i].s_reg_low);
-    }
-  } else {
-    // Either pre-regalloc or Portable.
-    for (int i = 0; i < count; i++) {
-      LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c S%d",
-          table[i].orig_sreg, storage_name[table[i].location],
-          table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
-          table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
-          table[i].is_const ? 'c' : 'n',
-          table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
-          table[i].s_reg_low);
-    }
+  for (int i = 0; i < count; i++) {
+    LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c 0x%04x S%d",
+                              table[i].orig_sreg, storage_name[table[i].location],
+                              table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
+                              table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
+                              table[i].is_const ? 'c' : 'n',
+                              table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
+                              table[i].reg.GetRawBits(),
+                              table[i].s_reg_low);
   }
 }
 
@@ -436,7 +421,9 @@
                                       RegStorage(), INVALID_SREG, INVALID_SREG};
 
 void MIRGraph::InitRegLocations() {
-  /* Allocate the location map */
+  // Allocate the location map. We also include the maximum possible temps because
+  // the temp allocation initializes reg location as well (in order to deal with
+  // case when it will be called after this pass).
   int max_regs = GetNumSSARegs() + GetMaxPossibleCompilerTemps();
   RegLocation* loc = static_cast<RegLocation*>(arena_->Alloc(max_regs * sizeof(*loc),
                                                              kArenaAllocRegAlloc));
@@ -447,22 +434,18 @@
     loc[i].wide = false;
   }
 
-  /* Patch up the locations for the compiler temps */
-  GrowableArray<CompilerTemp*>::Iterator iter(&compiler_temps_);
-  for (CompilerTemp* ct = iter.Next(); ct != NULL; ct = iter.Next()) {
-    loc[ct->s_reg_low].location = kLocCompilerTemp;
-    loc[ct->s_reg_low].defined = true;
-  }
-
   /* Treat Method* as a normal reference */
-  loc[GetMethodSReg()].ref = true;
+  int method_sreg = GetMethodSReg();
+  loc[method_sreg].ref = true;
+  loc[method_sreg].location = kLocCompilerTemp;
+  loc[method_sreg].defined = true;
 
   reg_location_ = loc;
 
-  int num_regs = cu_->num_dalvik_registers;
+  int num_regs = GetNumOfCodeVRs();
 
   /* Add types of incoming arguments based on signature */
-  int num_ins = cu_->num_ins;
+  int num_ins = GetNumOfInVRs();
   if (num_ins > 0) {
     int s_reg = num_regs - num_ins;
     if ((cu_->access_flags & kAccStatic) == 0) {
@@ -517,11 +500,9 @@
  */
 void MIRGraph::RemapRegLocations() {
   for (int i = 0; i < GetNumSSARegs(); i++) {
-    if (reg_location_[i].location != kLocCompilerTemp) {
-      int orig_sreg = reg_location_[i].s_reg_low;
-      reg_location_[i].orig_sreg = orig_sreg;
-      reg_location_[i].s_reg_low = SRegToVReg(orig_sreg);
-    }
+    int orig_sreg = reg_location_[i].s_reg_low;
+    reg_location_[i].orig_sreg = orig_sreg;
+    reg_location_[i].s_reg_low = SRegToVReg(orig_sreg);
   }
 }
 
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 022ec6b..4d5d253 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -20,6 +20,7 @@
 #include "compiler_driver.h"
 
 #include "dex/compiler_ir.h"
+#include "dex_compilation_unit.h"
 #include "field_helper.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index fa2a560..cdb816d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -342,6 +342,8 @@
       freezing_constructor_lock_("freezing constructor lock"),
       compiled_classes_lock_("compiled classes lock"),
       compiled_methods_lock_("compiled method lock"),
+      compiled_methods_(),
+      non_relative_linker_patch_count_(0u),
       image_(image),
       image_classes_(image_classes),
       thread_count_(thread_count),
@@ -355,8 +357,8 @@
       compiler_enable_auto_elf_loading_(nullptr),
       compiler_get_method_code_addr_(nullptr),
       support_boot_image_fixup_(instruction_set != kMips),
-      cfi_info_(nullptr),
       dedupe_code_("dedupe code"),
+      dedupe_src_mapping_table_("dedupe source mapping table"),
       dedupe_mapping_table_("dedupe mapping table"),
       dedupe_vmap_table_("dedupe vmap table"),
       dedupe_gc_map_("dedupe gc map"),
@@ -378,11 +380,6 @@
     CHECK(image_classes_.get() == nullptr);
   }
 
-  // Are we generating CFI information?
-  if (compiler_options->GetGenerateGDBInformation()) {
-    cfi_info_.reset(compiler_->GetCallFrameInformationInitialization(*this));
-  }
-
   // Read the profile file if one is provided.
   if (!profile_file.empty()) {
     profile_present_ = profile_file_.LoadFile(profile_file);
@@ -398,6 +395,10 @@
   return dedupe_code_.Add(Thread::Current(), code);
 }
 
+SrcMap* CompilerDriver::DeduplicateSrcMappingTable(const SrcMap& src_map) {
+  return dedupe_src_mapping_table_.Add(Thread::Current(), src_map);
+}
+
 std::vector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const std::vector<uint8_t>& code) {
   return dedupe_mapping_table_.Add(Thread::Current(), code);
 }
@@ -427,18 +428,6 @@
     MutexLock mu(self, compiled_methods_lock_);
     STLDeleteValues(&compiled_methods_);
   }
-  {
-    MutexLock mu(self, compiled_methods_lock_);
-    STLDeleteElements(&code_to_patch_);
-  }
-  {
-    MutexLock mu(self, compiled_methods_lock_);
-    STLDeleteElements(&methods_to_patch_);
-  }
-  {
-    MutexLock mu(self, compiled_methods_lock_);
-    STLDeleteElements(&classes_to_patch_);
-  }
   CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key");
   compiler_->UnInit();
 }
@@ -447,7 +436,7 @@
   // Lazily create thread-local storage
   CompilerTls* res = static_cast<CompilerTls*>(pthread_getspecific(tls_key_));
   if (res == nullptr) {
-    res = new CompilerTls();
+    res = compiler_->CreateNewCompilerTls();
     CHECK_PTHREAD_CALL(pthread_setspecific, (tls_key_, res), "compiler tls");
   }
   return res;
@@ -628,7 +617,7 @@
   }
 }
 
-static void ResolveExceptionsForMethod(MethodHelper* mh,
+static void ResolveExceptionsForMethod(MutableMethodHelper* mh,
     std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   const DexFile::CodeItem* code_item = mh->GetMethod()->GetCodeItem();
@@ -671,7 +660,7 @@
   std::set<std::pair<uint16_t, const DexFile*>>* exceptions_to_resolve =
       reinterpret_cast<std::set<std::pair<uint16_t, const DexFile*>>*>(arg);
   StackHandleScope<1> hs(Thread::Current());
-  MethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+  MutableMethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
   for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
     mh.ChangeMethod(c->GetVirtualMethod(i));
     ResolveExceptionsForMethod(&mh, *exceptions_to_resolve);
@@ -762,7 +751,7 @@
   Thread* self = Thread::Current();
   StackHandleScope<1> hs(self);
   // Make a copy of the handle so that we don't clobber it doing Assign.
-  Handle<mirror::Class> klass(hs.NewHandle(c.Get()));
+  MutableHandle<mirror::Class> klass(hs.NewHandle(c.Get()));
   std::string temp;
   while (!klass->IsObjectClass()) {
     const char* descriptor = klass->GetDescriptor(&temp);
@@ -796,14 +785,11 @@
   if (IsImage()) {
     TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
     // Update image_classes_ with classes for objects created by <clinit> methods.
-    Thread* self = Thread::Current();
-    const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
     gc::Heap* heap = Runtime::Current()->GetHeap();
     // TODO: Image spaces only?
     ScopedObjectAccess soa(Thread::Current());
-    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    WriterMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_);
     heap->VisitObjects(FindClinitImageClassesCallback, this);
-    self->EndAssertNoThreadSuspension(old_cause);
   }
 }
 
@@ -972,6 +958,43 @@
   }
 }
 
+bool CompilerDriver::CanEmbedReferenceTypeInCode(ClassReference* ref,
+                                                 bool* use_direct_ptr,
+                                                 uintptr_t* direct_type_ptr) {
+  CHECK(ref != nullptr);
+  CHECK(use_direct_ptr != nullptr);
+  CHECK(direct_type_ptr != nullptr);
+
+  ScopedObjectAccess soa(Thread::Current());
+  mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference();
+  bool is_initialized = false;
+  bool unused_finalizable;
+  // Make sure we have a finished Reference class object before attempting to use it.
+  if (!CanEmbedTypeInCode(*reference_class->GetDexCache()->GetDexFile(),
+                          reference_class->GetDexTypeIndex(), &is_initialized,
+                          use_direct_ptr, direct_type_ptr, &unused_finalizable) ||
+      !is_initialized) {
+    return false;
+  }
+  ref->first = &reference_class->GetDexFile();
+  ref->second = reference_class->GetDexClassDefIndex();
+  return true;
+}
+
+uint32_t CompilerDriver::GetReferenceSlowFlagOffset() const {
+  ScopedObjectAccess soa(Thread::Current());
+  mirror::Class* klass = mirror::Reference::GetJavaLangRefReference();
+  DCHECK(klass->IsInitialized());
+  return klass->GetSlowPathFlagOffset().Uint32Value();
+}
+
+uint32_t CompilerDriver::GetReferenceDisableFlagOffset() const {
+  ScopedObjectAccess soa(Thread::Current());
+  mirror::Class* klass = mirror::Reference::GetJavaLangRefReference();
+  DCHECK(klass->IsInitialized());
+  return klass->GetDisableIntrinsicFlagOffset().Uint32Value();
+}
+
 void CompilerDriver::ProcessedInstanceField(bool resolved) {
   if (!resolved) {
     stats_->UnresolvedInstanceField();
@@ -1287,75 +1310,6 @@
   return result;
 }
 
-void CompilerDriver::AddCodePatch(const DexFile* dex_file,
-                                  uint16_t referrer_class_def_idx,
-                                  uint32_t referrer_method_idx,
-                                  InvokeType referrer_invoke_type,
-                                  uint32_t target_method_idx,
-                                  const DexFile* target_dex_file,
-                                  InvokeType target_invoke_type,
-                                  size_t literal_offset) {
-  MutexLock mu(Thread::Current(), compiled_methods_lock_);
-  code_to_patch_.push_back(new CallPatchInformation(dex_file,
-                                                    referrer_class_def_idx,
-                                                    referrer_method_idx,
-                                                    referrer_invoke_type,
-                                                    target_method_idx,
-                                                    target_dex_file,
-                                                    target_invoke_type,
-                                                    literal_offset));
-}
-void CompilerDriver::AddRelativeCodePatch(const DexFile* dex_file,
-                                          uint16_t referrer_class_def_idx,
-                                          uint32_t referrer_method_idx,
-                                          InvokeType referrer_invoke_type,
-                                          uint32_t target_method_idx,
-                                          const DexFile* target_dex_file,
-                                          InvokeType target_invoke_type,
-                                          size_t literal_offset,
-                                          int32_t pc_relative_offset) {
-  MutexLock mu(Thread::Current(), compiled_methods_lock_);
-  code_to_patch_.push_back(new RelativeCallPatchInformation(dex_file,
-                                                            referrer_class_def_idx,
-                                                            referrer_method_idx,
-                                                            referrer_invoke_type,
-                                                            target_method_idx,
-                                                            target_dex_file,
-                                                            target_invoke_type,
-                                                            literal_offset,
-                                                            pc_relative_offset));
-}
-void CompilerDriver::AddMethodPatch(const DexFile* dex_file,
-                                    uint16_t referrer_class_def_idx,
-                                    uint32_t referrer_method_idx,
-                                    InvokeType referrer_invoke_type,
-                                    uint32_t target_method_idx,
-                                    const DexFile* target_dex_file,
-                                    InvokeType target_invoke_type,
-                                    size_t literal_offset) {
-  MutexLock mu(Thread::Current(), compiled_methods_lock_);
-  methods_to_patch_.push_back(new CallPatchInformation(dex_file,
-                                                       referrer_class_def_idx,
-                                                       referrer_method_idx,
-                                                       referrer_invoke_type,
-                                                       target_method_idx,
-                                                       target_dex_file,
-                                                       target_invoke_type,
-                                                       literal_offset));
-}
-void CompilerDriver::AddClassPatch(const DexFile* dex_file,
-                                    uint16_t referrer_class_def_idx,
-                                    uint32_t referrer_method_idx,
-                                    uint32_t target_type_idx,
-                                    size_t literal_offset) {
-  MutexLock mu(Thread::Current(), compiled_methods_lock_);
-  classes_to_patch_.push_back(new TypePatchInformation(dex_file,
-                                                       referrer_class_def_idx,
-                                                       referrer_method_idx,
-                                                       target_type_idx,
-                                                       literal_offset));
-}
-
 class ParallelCompilationManager {
  public:
   typedef void Callback(const ParallelCompilationManager* manager, size_t index);
@@ -1699,15 +1653,15 @@
      */
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
     std::string error_msg;
-    if (verifier::MethodVerifier::VerifyClass(&dex_file, dex_cache, class_loader, &class_def, true,
-                                              &error_msg) ==
+    if (verifier::MethodVerifier::VerifyClass(soa.Self(), &dex_file, dex_cache, class_loader,
+                                              &class_def, true, &error_msg) ==
                                                   verifier::MethodVerifier::kHardFailure) {
       LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
                  << " because: " << error_msg;
     }
   } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
     CHECK(klass->IsResolved()) << PrettyClass(klass.Get());
-    class_linker->VerifyClass(klass);
+    class_linker->VerifyClass(soa.Self(), klass);
 
     if (klass->IsErroneous()) {
       // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
@@ -1796,7 +1750,7 @@
     if (klass->IsVerified()) {
       // Attempt to initialize the class but bail if we either need to initialize the super-class
       // or static fields.
-      manager->GetClassLinker()->EnsureInitialized(klass, false, false);
+      manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false);
       if (!klass->IsInitialized()) {
         // We don't want non-trivial class initialization occurring on multiple threads due to
         // deadlock problems. For example, a parent class is initialized (holding its lock) that
@@ -1810,7 +1764,7 @@
         ObjectLock<mirror::Class> lock(soa.Self(), h_klass);
         // Attempt to initialize allowing initialization of parent classes but still not static
         // fields.
-        manager->GetClassLinker()->EnsureInitialized(klass, false, true);
+        manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
         if (!klass->IsInitialized()) {
           // We need to initialize static fields, we only do this for image classes that aren't
           // marked with the $NoPreloadHolder (which implies this should not be initialized early).
@@ -1829,11 +1783,13 @@
             // Run the class initializer in transaction mode.
             runtime->EnterTransactionMode(&transaction);
             const mirror::Class::Status old_status = klass->GetStatus();
-            bool success = manager->GetClassLinker()->EnsureInitialized(klass, true, true);
+            bool success = manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
+                                                                        true);
             // TODO we detach transaction from runtime to indicate we quit the transactional
             // mode which prevents the GC from visiting objects modified during the transaction.
             // Ensure GC is not run so don't access freed objects when aborting transaction.
-            const char* old_casue = soa.Self()->StartAssertNoThreadSuspension("Transaction end");
+
+            ScopedAssertNoThreadSuspension ants(soa.Self(), "Transaction end");
             runtime->ExitTransactionMode();
 
             if (!success) {
@@ -1846,7 +1802,6 @@
               transaction.Abort();
               CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
             }
-            soa.Self()->EndAssertNoThreadSuspension(old_casue);
           }
         }
         soa.Self()->AssertNoPendingException();
@@ -2040,11 +1995,19 @@
 
   Thread* self = Thread::Current();
   if (compiled_method != nullptr) {
+    // Count non-relative linker patches.
+    size_t non_relative_linker_patch_count = 0u;
+    for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+      if (patch.Type() != kLinkerPatchCallRelative) {
+        ++non_relative_linker_patch_count;
+      }
+    }
     MethodReference ref(&dex_file, method_idx);
     DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file);
     {
       MutexLock mu(self, compiled_methods_lock_);
       compiled_methods_.Put(ref, compiled_method);
+      non_relative_linker_patch_count_ += non_relative_linker_patch_count;
     }
     DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file);
   }
@@ -2102,6 +2065,11 @@
   return it->second;
 }
 
+size_t CompilerDriver::GetNonRelativeLinkerPatchCount() const {
+  MutexLock mu(Thread::Current(), compiled_methods_lock_);
+  return non_relative_linker_patch_count_;
+}
+
 void CompilerDriver::AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
                                                    uint16_t class_def_index) {
   WriterMutexLock mu(self, freezing_constructor_lock_);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index a165901..c445683 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -76,20 +76,6 @@
   kOptimize               // Perform required transformation and peep-hole optimizations.
 };
 
-// Thread-local storage compiler worker threads
-class CompilerTls {
-  public:
-    CompilerTls() : llvm_info_(nullptr) {}
-    ~CompilerTls() {}
-
-    void* GetLLVMInfo() { return llvm_info_; }
-
-    void SetLLVMInfo(void* llvm_info) { llvm_info_ = llvm_info; }
-
-  private:
-    void* llvm_info_;
-};
-
 class CompilerDriver {
  public:
   // Create a compiler targeting the requested "instruction_set".
@@ -183,6 +169,8 @@
 
   CompiledMethod* GetCompiledMethod(MethodReference ref) const
       LOCKS_EXCLUDED(compiled_methods_lock_);
+  size_t GetNonRelativeLinkerPatchCount() const
+      LOCKS_EXCLUDED(compiled_methods_lock_);
 
   void AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
                                      uint16_t class_def_index);
@@ -211,6 +199,12 @@
                           bool* is_type_initialized, bool* use_direct_type_ptr,
                           uintptr_t* direct_type_ptr, bool* out_is_finalizable);
 
+  // Query methods for the java.lang.ref.Reference class.
+  bool CanEmbedReferenceTypeInCode(ClassReference* ref,
+                                   bool* use_direct_type_ptr, uintptr_t* direct_type_ptr);
+  uint32_t GetReferenceSlowFlagOffset() const;
+  uint32_t GetReferenceDisableFlagOffset() const;
+
   // Get the DexCache for the
   mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -268,7 +262,7 @@
       uint16_t* declaring_class_idx, uint16_t* declaring_method_idx)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Get declaration location of a resolved field.
+  // Get the index in the vtable of the method.
   uint16_t GetResolvedMethodVTableIndex(
       mirror::ArtMethod* resolved_method, InvokeType type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -321,42 +315,6 @@
   const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
   bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
 
-  // Record patch information for later fix up.
-  void AddCodePatch(const DexFile* dex_file,
-                    uint16_t referrer_class_def_idx,
-                    uint32_t referrer_method_idx,
-                    InvokeType referrer_invoke_type,
-                    uint32_t target_method_idx,
-                    const DexFile* target_dex_file,
-                    InvokeType target_invoke_type,
-                    size_t literal_offset)
-      LOCKS_EXCLUDED(compiled_methods_lock_);
-  void AddRelativeCodePatch(const DexFile* dex_file,
-                            uint16_t referrer_class_def_idx,
-                            uint32_t referrer_method_idx,
-                            InvokeType referrer_invoke_type,
-                            uint32_t target_method_idx,
-                            const DexFile* target_dex_file,
-                            InvokeType target_invoke_type,
-                            size_t literal_offset,
-                            int32_t pc_relative_offset)
-      LOCKS_EXCLUDED(compiled_methods_lock_);
-  void AddMethodPatch(const DexFile* dex_file,
-                      uint16_t referrer_class_def_idx,
-                      uint32_t referrer_method_idx,
-                      InvokeType referrer_invoke_type,
-                      uint32_t target_method_idx,
-                      const DexFile* target_dex_file,
-                      InvokeType target_invoke_type,
-                      size_t literal_offset)
-      LOCKS_EXCLUDED(compiled_methods_lock_);
-  void AddClassPatch(const DexFile* dex_file,
-                     uint16_t referrer_class_def_idx,
-                     uint32_t referrer_method_idx,
-                     uint32_t target_method_idx,
-                     size_t literal_offset)
-      LOCKS_EXCLUDED(compiled_methods_lock_);
-
   bool GetSupportBootImageFixup() const {
     return support_boot_image_fixup_;
   }
@@ -393,198 +351,14 @@
     return thread_count_;
   }
 
-  class CallPatchInformation;
-  class TypePatchInformation;
-
   bool GetDumpPasses() const {
     return dump_passes_;
   }
 
-  bool DidIncludeDebugSymbols() const {
-    return compiler_options_->GetIncludeDebugSymbols();
-  }
-
   CumulativeLogger* GetTimingsLogger() const {
     return timings_logger_;
   }
 
-  class PatchInformation {
-   public:
-    const DexFile& GetDexFile() const {
-      return *dex_file_;
-    }
-    uint16_t GetReferrerClassDefIdx() const {
-      return referrer_class_def_idx_;
-    }
-    uint32_t GetReferrerMethodIdx() const {
-      return referrer_method_idx_;
-    }
-    size_t GetLiteralOffset() const {
-      return literal_offset_;
-    }
-
-    virtual bool IsCall() const {
-      return false;
-    }
-    virtual bool IsType() const {
-      return false;
-    }
-    virtual const CallPatchInformation* AsCall() const {
-      LOG(FATAL) << "Unreachable";
-      return nullptr;
-    }
-    virtual const TypePatchInformation* AsType() const {
-      LOG(FATAL) << "Unreachable";
-      return nullptr;
-    }
-
-   protected:
-    PatchInformation(const DexFile* dex_file,
-                     uint16_t referrer_class_def_idx,
-                     uint32_t referrer_method_idx,
-                     size_t literal_offset)
-      : dex_file_(dex_file),
-        referrer_class_def_idx_(referrer_class_def_idx),
-        referrer_method_idx_(referrer_method_idx),
-        literal_offset_(literal_offset) {
-      CHECK(dex_file_ != nullptr);
-    }
-    virtual ~PatchInformation() {}
-
-    const DexFile* const dex_file_;
-    const uint16_t referrer_class_def_idx_;
-    const uint32_t referrer_method_idx_;
-    const size_t literal_offset_;
-
-    friend class CompilerDriver;
-  };
-
-  class CallPatchInformation : public PatchInformation {
-   public:
-    InvokeType GetReferrerInvokeType() const {
-      return referrer_invoke_type_;
-    }
-    uint32_t GetTargetMethodIdx() const {
-      return target_method_idx_;
-    }
-    const DexFile* GetTargetDexFile() const {
-      return target_dex_file_;
-    }
-    InvokeType GetTargetInvokeType() const {
-      return target_invoke_type_;
-    }
-
-    const CallPatchInformation* AsCall() const {
-      return this;
-    }
-    bool IsCall() const {
-      return true;
-    }
-    virtual bool IsRelative() const {
-      return false;
-    }
-    virtual int RelativeOffset() const {
-      return 0;
-    }
-
-   protected:
-    CallPatchInformation(const DexFile* dex_file,
-                         uint16_t referrer_class_def_idx,
-                         uint32_t referrer_method_idx,
-                         InvokeType referrer_invoke_type,
-                         uint32_t target_method_idx,
-                         const DexFile* target_dex_file,
-                         InvokeType target_invoke_type,
-                         size_t literal_offset)
-        : PatchInformation(dex_file, referrer_class_def_idx,
-                           referrer_method_idx, literal_offset),
-          referrer_invoke_type_(referrer_invoke_type),
-          target_method_idx_(target_method_idx),
-          target_dex_file_(target_dex_file),
-          target_invoke_type_(target_invoke_type) {
-    }
-
-   private:
-    const InvokeType referrer_invoke_type_;
-    const uint32_t target_method_idx_;
-    const DexFile* target_dex_file_;
-    const InvokeType target_invoke_type_;
-
-    friend class CompilerDriver;
-    DISALLOW_COPY_AND_ASSIGN(CallPatchInformation);
-  };
-
-  class RelativeCallPatchInformation : public CallPatchInformation {
-   public:
-    bool IsRelative() const {
-      return true;
-    }
-    int RelativeOffset() const {
-      return offset_;
-    }
-
-   private:
-    RelativeCallPatchInformation(const DexFile* dex_file,
-                                 uint16_t referrer_class_def_idx,
-                                 uint32_t referrer_method_idx,
-                                 InvokeType referrer_invoke_type,
-                                 uint32_t target_method_idx,
-                                 const DexFile* target_dex_file,
-                                 InvokeType target_invoke_type,
-                                 size_t literal_offset,
-                                 int32_t pc_relative_offset)
-        : CallPatchInformation(dex_file, referrer_class_def_idx,
-                           referrer_method_idx, referrer_invoke_type, target_method_idx,
-                           target_dex_file, target_invoke_type, literal_offset),
-          offset_(pc_relative_offset) {
-    }
-
-    const int offset_;
-
-    friend class CompilerDriver;
-    DISALLOW_COPY_AND_ASSIGN(RelativeCallPatchInformation);
-  };
-
-  class TypePatchInformation : public PatchInformation {
-   public:
-    uint32_t GetTargetTypeIdx() const {
-      return target_type_idx_;
-    }
-
-    bool IsType() const {
-      return true;
-    }
-    const TypePatchInformation* AsType() const {
-      return this;
-    }
-
-   private:
-    TypePatchInformation(const DexFile* dex_file,
-                         uint16_t referrer_class_def_idx,
-                         uint32_t referrer_method_idx,
-                         uint32_t target_type_idx,
-                         size_t literal_offset)
-        : PatchInformation(dex_file, referrer_class_def_idx,
-                           referrer_method_idx, literal_offset),
-          target_type_idx_(target_type_idx) {
-    }
-
-    const uint32_t target_type_idx_;
-
-    friend class CompilerDriver;
-    DISALLOW_COPY_AND_ASSIGN(TypePatchInformation);
-  };
-
-  const std::vector<const CallPatchInformation*>& GetCodeToPatch() const {
-    return code_to_patch_;
-  }
-  const std::vector<const CallPatchInformation*>& GetMethodsToPatch() const {
-    return methods_to_patch_;
-  }
-  const std::vector<const TypePatchInformation*>& GetClassesToPatch() const {
-    return classes_to_patch_;
-  }
-
   // Checks if class specified by type_idx is one of the image_classes_
   bool IsImageClass(const char* descriptor) const;
 
@@ -592,19 +366,12 @@
       LOCKS_EXCLUDED(compiled_classes_lock_);
 
   std::vector<uint8_t>* DeduplicateCode(const std::vector<uint8_t>& code);
+  SrcMap* DeduplicateSrcMappingTable(const SrcMap& src_map);
   std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code);
   std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code);
   std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code);
   std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info);
 
-  /*
-   * @brief return the pointer to the Call Frame Information.
-   * @return pointer to call frame information for this compilation.
-   */
-  std::vector<uint8_t>* GetCallFrameInformation() const {
-    return cfi_info_.get();
-  }
-
   ProfileFile profile_file_;
   bool profile_present_;
 
@@ -701,10 +468,6 @@
   static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
-  std::vector<const CallPatchInformation*> code_to_patch_;
-  std::vector<const CallPatchInformation*> methods_to_patch_;
-  std::vector<const TypePatchInformation*> classes_to_patch_;
-
   const CompilerOptions* const compiler_options_;
   VerificationResults* const verification_results_;
   DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -727,6 +490,9 @@
   // All method references that this compiler has compiled.
   mutable Mutex compiled_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   MethodTable compiled_methods_ GUARDED_BY(compiled_methods_lock_);
+  // Number of non-relative patches in all compiled methods. These patches need space
+  // in the .oat_patches ELF section if requested in the compiler options.
+  size_t non_relative_linker_patch_count_ GUARDED_BY(compiled_methods_lock_);
 
   const bool image_;
 
@@ -775,19 +541,17 @@
 
   bool support_boot_image_fixup_;
 
-  // Call Frame Information, which might be generated to help stack tracebacks.
-  std::unique_ptr<std::vector<uint8_t>> cfi_info_;
-
   // DeDuplication data structures, these own the corresponding byte arrays.
+  template <typename ByteArray>
   class DedupeHashFunc {
    public:
-    size_t operator()(const std::vector<uint8_t>& array) const {
+    size_t operator()(const ByteArray& array) const {
       // For small arrays compute a hash using every byte.
       static const size_t kSmallArrayThreshold = 16;
       size_t hash = 0x811c9dc5;
       if (array.size() <= kSmallArrayThreshold) {
-        for (uint8_t b : array) {
-          hash = (hash * 16777619) ^ b;
+        for (auto b : array) {
+          hash = (hash * 16777619) ^ static_cast<uint8_t>(b);
         }
       } else {
         // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
@@ -795,12 +559,12 @@
         // values at random.
         static const size_t kRandomHashCount = 16;
         for (size_t i = 0; i < 2; ++i) {
-          uint8_t b = array[i + 6];
+          uint8_t b = static_cast<uint8_t>(array[i + 6]);
           hash = (hash * 16777619) ^ b;
         }
         for (size_t i = 2; i < kRandomHashCount; ++i) {
           size_t r = i * 1103515245 + 12345;
-          uint8_t b = array[r % array.size()];
+          uint8_t b = static_cast<uint8_t>(array[r % array.size()]);
           hash = (hash * 16777619) ^ b;
         }
       }
@@ -812,11 +576,13 @@
       return hash;
     }
   };
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_code_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_;
+
+  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_code_;
+  DedupeSet<SrcMap, size_t, DedupeHashFunc<SrcMap>, 4> dedupe_src_mapping_table_;
+  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_mapping_table_;
+  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_vmap_table_;
+  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_gc_map_;
+  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_cfi_info_;
 
   DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
 };
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 9ba6645..9ae9bd4 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -129,12 +129,10 @@
                                            << " "
                                            << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
                                            << " " << dex->GetMethodName(dex->GetMethodId(i));
-#if defined(ART_USE_PORTABLE_COMPILER)
     EXPECT_TRUE(method->GetEntryPointFromPortableCompiledCode() != NULL) << "method_idx=" << i
                                            << " "
                                            << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
                                            << " " << dex->GetMethodName(dex->GetMethodId(i));
-#endif
   }
   EXPECT_EQ(dex->NumFieldIds(), dex_cache->NumResolvedFields());
   for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index c0f91d1..eb3de97 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -27,7 +27,8 @@
     kSpace,               // Maximize space savings.
     kBalanced,            // Try to get the best performance return on compilation investment.
     kSpeed,               // Maximize runtime performance.
-    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
+    kEverything,          // Force compilation (Note: excludes compilation of class initializers).
+    kTime                 // Compile methods, but minimize compilation time.
   };
 
   // Guide heuristics to determine whether to compile method if profile data not available.
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 840b0ad..986fc71 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -23,18 +23,6 @@
 
 namespace art {
 
-DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu)
-    : cu_(cu),
-      class_loader_(cu->class_loader),
-      class_linker_(cu->class_linker),
-      dex_file_(cu->dex_file),
-      code_item_(cu->code_item),
-      class_def_idx_(cu->class_def_idx),
-      dex_method_idx_(cu->method_idx),
-      access_flags_(cu->access_flags),
-      verified_method_(cu_->compiler_driver->GetVerifiedMethod(cu->dex_file, cu->method_idx)) {
-}
-
 DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu,
                                        jobject class_loader,
                                        ClassLinker* class_linker,
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
new file mode 100644
index 0000000..3be2478
--- /dev/null
+++ b/compiler/elf_builder.h
@@ -0,0 +1,1170 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_ELF_BUILDER_H_
+#define ART_COMPILER_ELF_BUILDER_H_
+
+#include "base/stl_util.h"
+#include "buffered_output_stream.h"
+#include "elf_utils.h"
+#include "file_output_stream.h"
+#include "instruction_set.h"
+
+namespace art {
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfSectionBuilder {
+ public:
+  ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+                    const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link, Elf_Word info,
+                    Elf_Word align, Elf_Word entsize) : name_(sec_name), link_(link) {
+    memset(&section_, 0, sizeof(section_));
+    section_.sh_type = type;
+    section_.sh_flags = flags;
+    section_.sh_info = info;
+    section_.sh_addralign = align;
+    section_.sh_entsize = entsize;
+  }
+
+  virtual ~ElfSectionBuilder() {}
+
+  Elf_Shdr section_;
+  Elf_Word section_index_ = 0;
+
+  Elf_Word GetLink() {
+    return (link_) ? link_->section_index_ : 0;
+  }
+
+  const std::string name_;
+
+ protected:
+  const ElfSectionBuilder* link_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Dyn, typename Elf_Shdr>
+class ElfDynamicBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  void AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
+    if (tag == DT_NULL) {
+      return;
+    }
+    dynamics_.push_back({nullptr, tag, d_un});
+  }
+
+  void AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
+                     ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section) {
+    if (tag == DT_NULL) {
+      return;
+    }
+    dynamics_.push_back({section, tag, d_un});
+  }
+
+  ElfDynamicBuilder(const std::string& sec_name,
+                    ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link)
+  : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC,
+                                                     link, 0, kPageSize, sizeof(Elf_Dyn)) {}
+  ~ElfDynamicBuilder() {}
+
+  Elf_Word GetSize() {
+    // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
+    // these must be added when we actually put the file together because
+    // their values are very dependent on state.
+    return dynamics_.size() + 3;
+  }
+
+  // Create the actual dynamic vector. strsz should be the size of the .dynstr
+  // table and soname_off should be the offset of the soname in .dynstr.
+  // Since niether can be found prior to final layout we will wait until here
+  // to add them.
+  std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname) {
+    std::vector<Elf_Dyn> ret;
+    for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
+      if (it->section_) {
+        // We are adding an address relative to a section.
+        ret.push_back(
+            {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
+      } else {
+        ret.push_back({it->tag_, {it->off_}});
+      }
+    }
+    ret.push_back({DT_STRSZ, {strsz}});
+    ret.push_back({DT_SONAME, {soname}});
+    ret.push_back({DT_NULL, {0}});
+    return ret;
+  }
+
+ protected:
+  struct ElfDynamicState {
+    ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+    Elf_Sword tag_;
+    Elf_Word off_;
+  };
+  std::vector<ElfDynamicState> dynamics_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfRawSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+                       const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* link, Elf_Word info,
+                       Elf_Word align, Elf_Word entsize)
+    : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, link, info, align,
+                                                       entsize) {}
+  ~ElfRawSectionBuilder() {}
+  std::vector<uint8_t>* GetBuffer() { return &buf_; }
+  void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
+
+ protected:
+  std::vector<uint8_t> buf_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfOatSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
+                       Elf_Word type, Elf_Word flags)
+    : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, nullptr, 0, kPageSize,
+                                                       0), offset_(offset), size_(size) {}
+  ~ElfOatSectionBuilder() {}
+
+  Elf_Word GetOffset() {
+    return offset_;
+  }
+
+  Elf_Word GetSize() {
+    return size_;
+  }
+
+ protected:
+  // Offset of the content within the file.
+  Elf_Word offset_;
+  // Size of the content within the file.
+  Elf_Word size_;
+};
+
+static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
+  return ((binding) << 4) + ((type) & 0xf);
+}
+
+// from bionic
+static inline unsigned elfhash(const char *_name) {
+  const unsigned char *name = (const unsigned char *) _name;
+  unsigned h = 0, g;
+
+  while (*name) {
+    h = (h << 4) + *name++;
+    g = h & 0xf0000000;
+    h ^= g;
+    h ^= g >> 24;
+  }
+  return h;
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Sym,
+          typename Elf_Shdr>
+class ElfSymtabBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  // Add a symbol with given name to this symtab. The symbol refers to
+  // 'relative_addr' within the given section and has the given attributes.
+  void AddSymbol(const std::string& name,
+                 const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section,
+                 Elf_Addr addr,
+                 bool is_relative,
+                 Elf_Word size,
+                 uint8_t binding,
+                 uint8_t type,
+                 uint8_t other = 0) {
+    CHECK(section);
+    ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
+                                            MakeStInfo(binding, type), other, 0};
+    symbols_.push_back(state);
+  }
+
+  ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
+                   const std::string& str_name, Elf_Word str_type, bool alloc)
+  : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, ((alloc) ? SHF_ALLOC : 0U),
+                                                     &strtab_, 0, sizeof(Elf_Word),
+                                                     sizeof(Elf_Sym)), str_name_(str_name),
+                                                     str_type_(str_type),
+                                                     strtab_(str_name,
+                                                             str_type,
+                                                             ((alloc) ? SHF_ALLOC : 0U),
+                                                             nullptr, 0, 1, 1) {}
+  ~ElfSymtabBuilder() {}
+
+  std::vector<Elf_Word> GenerateHashContents() {
+    // Here is how The ELF hash table works.
+    // There are 3 arrays to worry about.
+    // * The symbol table where the symbol information is.
+    // * The bucket array which is an array of indexes into the symtab and chain.
+    // * The chain array which is also an array of indexes into the symtab and chain.
+    //
+    // Lets say the state is something like this.
+    // +--------+       +--------+      +-----------+
+    // | symtab |       | bucket |      |   chain   |
+    // |  null  |       | 1      |      | STN_UNDEF |
+    // | <sym1> |       | 4      |      | 2         |
+    // | <sym2> |       |        |      | 5         |
+    // | <sym3> |       |        |      | STN_UNDEF |
+    // | <sym4> |       |        |      | 3         |
+    // | <sym5> |       |        |      | STN_UNDEF |
+    // +--------+       +--------+      +-----------+
+    //
+    // The lookup process (in python psudocode) is
+    //
+    // def GetSym(name):
+    //     # NB STN_UNDEF == 0
+    //     indx = bucket[elfhash(name) % num_buckets]
+    //     while indx != STN_UNDEF:
+    //         if GetSymbolName(symtab[indx]) == name:
+    //             return symtab[indx]
+    //         indx = chain[indx]
+    //     return SYMBOL_NOT_FOUND
+    //
+    // Between bucket and chain arrays every symtab index must be present exactly
+    // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
+
+    // Select number of buckets.
+    // This is essentially arbitrary.
+    Elf_Word nbuckets;
+    Elf_Word chain_size = GetSize();
+    if (symbols_.size() < 8) {
+      nbuckets = 2;
+    } else if (symbols_.size() < 32) {
+      nbuckets = 4;
+    } else if (symbols_.size() < 256) {
+      nbuckets = 16;
+    } else {
+      // Have about 32 ids per bucket.
+      nbuckets = RoundUp(symbols_.size()/32, 2);
+    }
+    std::vector<Elf_Word> hash;
+    hash.push_back(nbuckets);
+    hash.push_back(chain_size);
+    uint32_t bucket_offset = hash.size();
+    uint32_t chain_offset = bucket_offset + nbuckets;
+    hash.resize(hash.size() + nbuckets + chain_size, 0);
+
+    Elf_Word* buckets = hash.data() + bucket_offset;
+    Elf_Word* chain   = hash.data() + chain_offset;
+
+    // Set up the actual hash table.
+    for (Elf_Word i = 0; i < symbols_.size(); i++) {
+      // Add 1 since we need to have the null symbol that is not in the symbols
+      // list.
+      Elf_Word index = i + 1;
+      Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
+      if (buckets[hash_val] == 0) {
+        buckets[hash_val] = index;
+      } else {
+        hash_val = buckets[hash_val];
+        CHECK_LT(hash_val, chain_size);
+        while (chain[hash_val] != 0) {
+          hash_val = chain[hash_val];
+          CHECK_LT(hash_val, chain_size);
+        }
+        chain[hash_val] = index;
+        // Check for loops. Works because if this is non-empty then there must be
+        // another cell which already contains the same symbol index as this one,
+        // which means some symbol has more then one name, which isn't allowed.
+        CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
+      }
+    }
+
+    return hash;
+  }
+
+  std::string GenerateStrtab() {
+    std::string tab;
+    tab += '\0';
+    for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
+      it->name_idx_ = tab.size();
+      tab += it->name_;
+      tab += '\0';
+    }
+    strtab_.section_.sh_size = tab.size();
+    return tab;
+  }
+
+  std::vector<Elf_Sym> GenerateSymtab() {
+    std::vector<Elf_Sym> ret;
+    Elf_Sym undef_sym;
+    memset(&undef_sym, 0, sizeof(undef_sym));
+    undef_sym.st_shndx = SHN_UNDEF;
+    ret.push_back(undef_sym);
+
+    for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
+      Elf_Sym sym;
+      memset(&sym, 0, sizeof(sym));
+      sym.st_name = it->name_idx_;
+      if (it->is_relative_) {
+        sym.st_value = it->addr_ + it->section_->section_.sh_offset;
+      } else {
+        sym.st_value = it->addr_;
+      }
+      sym.st_size = it->size_;
+      sym.st_other = it->other_;
+      sym.st_shndx = it->section_->section_index_;
+      sym.st_info = it->info_;
+
+      ret.push_back(sym);
+    }
+    return ret;
+  }
+
+  Elf_Word GetSize() {
+    // 1 is for the implicit NULL symbol.
+    return symbols_.size() + 1;
+  }
+
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* GetStrTab() {
+    return &strtab_;
+  }
+
+ protected:
+  struct ElfSymbolState {
+    const std::string name_;
+    const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+    Elf_Addr addr_;
+    Elf_Word size_;
+    bool is_relative_;
+    uint8_t info_;
+    uint8_t other_;
+    // Used during Write() to temporarially hold name index in the strtab.
+    Elf_Word name_idx_;
+  };
+
+  // Information for the strsym for dynstr sections.
+  const std::string str_name_;
+  Elf_Word str_type_;
+  // The symbols in the same order they will be in the symbol table.
+  std::vector<ElfSymbolState> symbols_;
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> strtab_;
+};
+
+template <typename Elf_Word>
+class ElfFilePiece {
+ public:
+  virtual ~ElfFilePiece() {}
+
+  virtual bool Write(File* elf_file) {
+    if (static_cast<off_t>(offset_) != lseek(elf_file->Fd(), offset_, SEEK_SET)) {
+      PLOG(ERROR) << "Failed to seek to " << GetDescription() << " offset " << offset_ << " for "
+          << elf_file->GetPath();
+      return false;
+    }
+
+    return DoActualWrite(elf_file);
+  }
+
+  static bool Compare(ElfFilePiece* a, ElfFilePiece* b) {
+    return a->offset_ < b->offset_;
+  }
+
+ protected:
+  explicit ElfFilePiece(Elf_Word offset) : offset_(offset) {}
+
+  virtual std::string GetDescription() = 0;
+  virtual bool DoActualWrite(File* elf_file) = 0;
+
+  Elf_Word offset_;
+};
+
+template <typename Elf_Word>
+class ElfFileMemoryPiece : public ElfFilePiece<Elf_Word> {
+ public:
+  ElfFileMemoryPiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
+      : ElfFilePiece<Elf_Word>(offset), dbg_name_(name), data_(data), size_(size) {}
+
+  bool DoActualWrite(File* elf_file) OVERRIDE {
+    DCHECK(data_ != nullptr || size_ == 0U) << dbg_name_ << " " << size_;
+
+    if (!elf_file->WriteFully(data_, size_)) {
+      PLOG(ERROR) << "Failed to write " << dbg_name_ << " for " << elf_file->GetPath();
+      return false;
+    }
+
+    return true;
+  }
+
+  std::string GetDescription() OVERRIDE {
+    return dbg_name_;
+  }
+
+ private:
+  const std::string& dbg_name_;
+  const void *data_;
+  Elf_Word size_;
+};
+
+class CodeOutput {
+ public:
+  virtual void SetCodeOffset(size_t offset) = 0;
+  virtual bool Write(OutputStream* out) = 0;
+  virtual ~CodeOutput() {}
+};
+
+template <typename Elf_Word>
+class ElfFileRodataPiece : public ElfFilePiece<Elf_Word> {
+ public:
+  ElfFileRodataPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+      output_(output) {}
+
+  bool DoActualWrite(File* elf_file) OVERRIDE {
+    output_->SetCodeOffset(this->offset_);
+    std::unique_ptr<BufferedOutputStream> output_stream(
+        new BufferedOutputStream(new FileOutputStream(elf_file)));
+    if (!output_->Write(output_stream.get())) {
+      PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file->GetPath();
+      return false;
+    }
+
+    return true;
+  }
+
+  std::string GetDescription() OVERRIDE {
+    return ".rodata";
+  }
+
+ private:
+  CodeOutput* output_;
+};
+
+template <typename Elf_Word>
+class ElfFileOatTextPiece : public ElfFilePiece<Elf_Word> {
+ public:
+  ElfFileOatTextPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+      output_(output) {}
+
+  bool DoActualWrite(File* elf_file) OVERRIDE {
+    // All data is written by the ElfFileRodataPiece right now, as the oat writer writes in one
+    // piece. This is for future flexibility.
+    UNUSED(output_);
+    return true;
+  }
+
+  std::string GetDescription() OVERRIDE {
+    return ".text";
+  }
+
+ private:
+  CodeOutput* output_;
+};
+
+template <typename Elf_Word>
+static bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces, File* elf_file) {
+  // TODO It would be nice if this checked for overlap.
+  for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+    if (!(*it)->Write(elf_file)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename Elf_Word, typename Elf_Shdr>
+static inline constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
+  return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn,
+          typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr>
+class ElfBuilder FINAL {
+ public:
+  ElfBuilder(CodeOutput* oat_writer,
+             File* elf_file,
+             InstructionSet isa,
+             Elf_Word rodata_relative_offset,
+             Elf_Word rodata_size,
+             Elf_Word text_relative_offset,
+             Elf_Word text_size,
+             const bool add_symbols,
+             bool debug = false)
+    : oat_writer_(oat_writer),
+      elf_file_(elf_file),
+      add_symbols_(add_symbols),
+      debug_logging_(debug),
+      text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
+                    SHF_ALLOC | SHF_EXECINSTR),
+      rodata_builder_(".rodata", rodata_size, rodata_relative_offset, SHT_PROGBITS, SHF_ALLOC),
+      dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
+      symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
+      hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0, sizeof(Elf_Word),
+                    sizeof(Elf_Word)),
+      dynamic_builder_(".dynamic", &dynsym_builder_),
+      shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
+    SetupEhdr();
+    SetupDynamic();
+    SetupRequiredSymbols();
+    SetISA(isa);
+  }
+  ~ElfBuilder() {}
+
+  bool Init() {
+    // The basic layout of the elf file. Order may be different in final output.
+    // +-------------------------+
+    // | Elf_Ehdr                |
+    // +-------------------------+
+    // | Elf_Phdr PHDR           |
+    // | Elf_Phdr LOAD R         | .dynsym .dynstr .hash .rodata
+    // | Elf_Phdr LOAD R X       | .text
+    // | Elf_Phdr LOAD RW        | .dynamic
+    // | Elf_Phdr DYNAMIC        | .dynamic
+    // +-------------------------+
+    // | .dynsym                 |
+    // | Elf_Sym  STN_UNDEF      |
+    // | Elf_Sym  oatdata        |
+    // | Elf_Sym  oatexec        |
+    // | Elf_Sym  oatlastword    |
+    // +-------------------------+
+    // | .dynstr                 |
+    // | \0                      |
+    // | oatdata\0               |
+    // | oatexec\0               |
+    // | oatlastword\0           |
+    // | boot.oat\0              |
+    // +-------------------------+
+    // | .hash                   |
+    // | Elf_Word nbucket = b    |
+    // | Elf_Word nchain  = c    |
+    // | Elf_Word bucket[0]      |
+    // |         ...             |
+    // | Elf_Word bucket[b - 1]  |
+    // | Elf_Word chain[0]       |
+    // |         ...             |
+    // | Elf_Word chain[c - 1]   |
+    // +-------------------------+
+    // | .rodata                 |
+    // | oatdata..oatexec-4      |
+    // +-------------------------+
+    // | .text                   |
+    // | oatexec..oatlastword    |
+    // +-------------------------+
+    // | .dynamic                |
+    // | Elf_Dyn DT_SONAME       |
+    // | Elf_Dyn DT_HASH         |
+    // | Elf_Dyn DT_SYMTAB       |
+    // | Elf_Dyn DT_SYMENT       |
+    // | Elf_Dyn DT_STRTAB       |
+    // | Elf_Dyn DT_STRSZ        |
+    // | Elf_Dyn DT_NULL         |
+    // +-------------------------+  (Optional)
+    // | .strtab                 |  (Optional)
+    // | program symbol names    |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .symtab                 |  (Optional)
+    // | program symbols         |  (Optional)
+    // +-------------------------+
+    // | .shstrtab               |
+    // | \0                      |
+    // | .dynamic\0              |
+    // | .dynsym\0               |
+    // | .dynstr\0               |
+    // | .hash\0                 |
+    // | .rodata\0               |
+    // | .text\0                 |
+    // | .shstrtab\0             |
+    // | .symtab\0               |  (Optional)
+    // | .strtab\0               |  (Optional)
+    // | .debug_str\0            |  (Optional)
+    // | .debug_info\0           |  (Optional)
+    // | .eh_frame\0             |  (Optional)
+    // | .debug_line\0           |  (Optional)
+    // | .debug_abbrev\0         |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_info             |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_abbrev           |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .eh_frame               |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_line             |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_str              |  (Optional)
+    // +-------------------------+  (Optional)
+    // | Elf_Shdr NULL           |
+    // | Elf_Shdr .dynsym        |
+    // | Elf_Shdr .dynstr        |
+    // | Elf_Shdr .hash          |
+    // | Elf_Shdr .text          |
+    // | Elf_Shdr .rodata        |
+    // | Elf_Shdr .dynamic       |
+    // | Elf_Shdr .shstrtab      |
+    // | Elf_Shdr .debug_info    |  (Optional)
+    // | Elf_Shdr .debug_abbrev  |  (Optional)
+    // | Elf_Shdr .eh_frame      |  (Optional)
+    // | Elf_Shdr .debug_line    |  (Optional)
+    // | Elf_Shdr .debug_str     |  (Optional)
+    // +-------------------------+
+
+    if (fatal_error_) {
+      return false;
+    }
+    // Step 1. Figure out all the offsets.
+
+    if (debug_logging_) {
+      LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
+      LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
+    }
+
+    memset(&program_headers_, 0, sizeof(program_headers_));
+    program_headers_[PH_PHDR].p_type    = PT_PHDR;
+    program_headers_[PH_PHDR].p_offset  = PHDR_OFFSET;
+    program_headers_[PH_PHDR].p_vaddr   = PHDR_OFFSET;
+    program_headers_[PH_PHDR].p_paddr   = PHDR_OFFSET;
+    program_headers_[PH_PHDR].p_filesz  = sizeof(program_headers_);
+    program_headers_[PH_PHDR].p_memsz   = sizeof(program_headers_);
+    program_headers_[PH_PHDR].p_flags   = PF_R;
+    program_headers_[PH_PHDR].p_align   = sizeof(Elf_Word);
+
+    program_headers_[PH_LOAD_R__].p_type    = PT_LOAD;
+    program_headers_[PH_LOAD_R__].p_offset  = 0;
+    program_headers_[PH_LOAD_R__].p_vaddr   = 0;
+    program_headers_[PH_LOAD_R__].p_paddr   = 0;
+    program_headers_[PH_LOAD_R__].p_flags   = PF_R;
+
+    program_headers_[PH_LOAD_R_X].p_type    = PT_LOAD;
+    program_headers_[PH_LOAD_R_X].p_flags   = PF_R | PF_X;
+
+    program_headers_[PH_LOAD_RW_].p_type    = PT_LOAD;
+    program_headers_[PH_LOAD_RW_].p_flags   = PF_R | PF_W;
+
+    program_headers_[PH_DYNAMIC].p_type    = PT_DYNAMIC;
+    program_headers_[PH_DYNAMIC].p_flags   = PF_R | PF_W;
+
+    // Get the dynstr string.
+    dynstr_ = dynsym_builder_.GenerateStrtab();
+
+    // Add the SONAME to the dynstr.
+    dynstr_soname_offset_ = dynstr_.size();
+    std::string file_name(elf_file_->GetPath());
+    size_t directory_separator_pos = file_name.rfind('/');
+    if (directory_separator_pos != std::string::npos) {
+      file_name = file_name.substr(directory_separator_pos + 1);
+    }
+    dynstr_ += file_name;
+    dynstr_ += '\0';
+    if (debug_logging_) {
+      LOG(INFO) << "dynstr size (bytes)   =" << dynstr_.size()
+                << std::hex << " " << dynstr_.size();
+      LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
+                << std::hex << " " << dynsym_builder_.GetSize();
+    }
+
+    // Get the section header string table.
+    shstrtab_ += '\0';
+
+    // Setup sym_undef
+    memset(&null_hdr_, 0, sizeof(null_hdr_));
+    null_hdr_.sh_type = SHT_NULL;
+    null_hdr_.sh_link = SHN_UNDEF;
+    section_ptrs_.push_back(&null_hdr_);
+
+    section_index_ = 1;
+
+    // setup .dynsym
+    section_ptrs_.push_back(&dynsym_builder_.section_);
+    AssignSectionStr(&dynsym_builder_, &shstrtab_);
+    dynsym_builder_.section_index_ = section_index_++;
+
+    // Setup .dynstr
+    section_ptrs_.push_back(&dynsym_builder_.GetStrTab()->section_);
+    AssignSectionStr(dynsym_builder_.GetStrTab(), &shstrtab_);
+    dynsym_builder_.GetStrTab()->section_index_ = section_index_++;
+
+    // Setup .hash
+    section_ptrs_.push_back(&hash_builder_.section_);
+    AssignSectionStr(&hash_builder_, &shstrtab_);
+    hash_builder_.section_index_ = section_index_++;
+
+    // Setup .rodata
+    section_ptrs_.push_back(&rodata_builder_.section_);
+    AssignSectionStr(&rodata_builder_, &shstrtab_);
+    rodata_builder_.section_index_ = section_index_++;
+
+    // Setup .text
+    section_ptrs_.push_back(&text_builder_.section_);
+    AssignSectionStr(&text_builder_, &shstrtab_);
+    text_builder_.section_index_ = section_index_++;
+
+    // Setup .dynamic
+    section_ptrs_.push_back(&dynamic_builder_.section_);
+    AssignSectionStr(&dynamic_builder_, &shstrtab_);
+    dynamic_builder_.section_index_ = section_index_++;
+
+    // Fill in the hash section.
+    hash_ = dynsym_builder_.GenerateHashContents();
+
+    if (debug_logging_) {
+      LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
+                << std::hex << " " << hash_.size() * sizeof(Elf_Word);
+    }
+
+    Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
+
+    // Get the layout in the sections.
+    //
+    // Get the layout of the dynsym section.
+    dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
+    dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
+    dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
+    dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
+
+    // Get the layout of the dynstr section.
+    dynsym_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                                 (dynsym_builder_.GetStrTab()->section_,
+                                                  dynsym_builder_.section_);
+    dynsym_builder_.GetStrTab()->section_.sh_addr = dynsym_builder_.GetStrTab()->section_.sh_offset;
+    dynsym_builder_.GetStrTab()->section_.sh_size = dynstr_.size();
+    dynsym_builder_.GetStrTab()->section_.sh_link = dynsym_builder_.GetStrTab()->GetLink();
+
+    // Get the layout of the hash section
+    hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                       (hash_builder_.section_,
+                                        dynsym_builder_.GetStrTab()->section_);
+    hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
+    hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
+    hash_builder_.section_.sh_link = hash_builder_.GetLink();
+
+    // Get the layout of the rodata section.
+    rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                         (rodata_builder_.section_,
+                                          hash_builder_.section_);
+    rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
+    rodata_builder_.section_.sh_size = rodata_builder_.GetSize();
+    rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
+
+    // Get the layout of the text section.
+    text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                       (text_builder_.section_, rodata_builder_.section_);
+    text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
+    text_builder_.section_.sh_size = text_builder_.GetSize();
+    text_builder_.section_.sh_link = text_builder_.GetLink();
+    CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
+
+    // Get the layout of the dynamic section.
+    dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                          (dynamic_builder_.section_,
+                                           text_builder_.section_);
+    dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
+    dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
+    dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
+
+    if (debug_logging_) {
+      LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
+                << " dynsym size=" << dynsym_builder_.section_.sh_size;
+      LOG(INFO) << "dynstr off=" << dynsym_builder_.GetStrTab()->section_.sh_offset
+                << " dynstr size=" << dynsym_builder_.GetStrTab()->section_.sh_size;
+      LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
+                << " hash size=" << hash_builder_.section_.sh_size;
+      LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
+                << " rodata size=" << rodata_builder_.section_.sh_size;
+      LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
+                << " text size=" << text_builder_.section_.sh_size;
+      LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
+                << " dynamic size=" << dynamic_builder_.section_.sh_size;
+    }
+
+    return true;
+  }
+
+  bool Write() {
+    std::vector<ElfFilePiece<Elf_Word>*> pieces;
+    Elf_Shdr prev = dynamic_builder_.section_;
+    std::string strtab;
+
+    if (IncludingDebugSymbols()) {
+      // Setup .symtab
+      section_ptrs_.push_back(&symtab_builder_.section_);
+      AssignSectionStr(&symtab_builder_, &shstrtab_);
+      symtab_builder_.section_index_ = section_index_++;
+
+      // Setup .strtab
+      section_ptrs_.push_back(&symtab_builder_.GetStrTab()->section_);
+      AssignSectionStr(symtab_builder_.GetStrTab(), &shstrtab_);
+      symtab_builder_.GetStrTab()->section_index_ = section_index_++;
+
+      strtab = symtab_builder_.GenerateStrtab();
+      if (debug_logging_) {
+        LOG(INFO) << "strtab size (bytes)    =" << strtab.size()
+                  << std::hex << " " << strtab.size();
+        LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
+                  << std::hex << " " << symtab_builder_.GetSize();
+      }
+    }
+
+    // Setup all the other sections.
+    for (ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder = other_builders_.data(),
+         *end = builder + other_builders_.size();
+         builder != end; ++builder) {
+      section_ptrs_.push_back(&builder->section_);
+      AssignSectionStr(builder, &shstrtab_);
+      builder->section_index_ = section_index_++;
+    }
+
+    // Setup shstrtab
+    section_ptrs_.push_back(&shstrtab_builder_.section_);
+    AssignSectionStr(&shstrtab_builder_, &shstrtab_);
+    shstrtab_builder_.section_index_ = section_index_++;
+
+    if (debug_logging_) {
+      LOG(INFO) << ".shstrtab size    (bytes)   =" << shstrtab_.size()
+                << std::hex << " " << shstrtab_.size();
+      LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
+                << std::hex << " " << section_ptrs_.size();
+    }
+
+    if (IncludingDebugSymbols()) {
+      // Get the layout of the symtab section.
+      symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                           (symtab_builder_.section_,
+                                            dynamic_builder_.section_);
+      symtab_builder_.section_.sh_addr = 0;
+      // Add to leave space for the null symbol.
+      symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
+      symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
+
+      // Get the layout of the dynstr section.
+      symtab_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                                   (symtab_builder_.GetStrTab()->section_,
+                                                    symtab_builder_.section_);
+      symtab_builder_.GetStrTab()->section_.sh_addr = 0;
+      symtab_builder_.GetStrTab()->section_.sh_size = strtab.size();
+      symtab_builder_.GetStrTab()->section_.sh_link = symtab_builder_.GetStrTab()->GetLink();
+
+      prev = symtab_builder_.GetStrTab()->section_;
+      if (debug_logging_) {
+        LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
+                  << " symtab size=" << symtab_builder_.section_.sh_size;
+        LOG(INFO) << "strtab off=" << symtab_builder_.GetStrTab()->section_.sh_offset
+                  << " strtab size=" << symtab_builder_.GetStrTab()->section_.sh_size;
+      }
+    }
+
+    // Get the layout of the extra sections. (This will deal with the debug
+    // sections if they are there)
+    for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+      it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
+      it->section_.sh_addr = 0;
+      it->section_.sh_size = it->GetBuffer()->size();
+      it->section_.sh_link = it->GetLink();
+
+      // We postpone adding an ElfFilePiece to keep the order in "pieces."
+
+      prev = it->section_;
+      if (debug_logging_) {
+        LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
+                  << " size=" << it->section_.sh_size;
+      }
+    }
+
+    // Get the layout of the shstrtab section
+    shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                           (shstrtab_builder_.section_, prev);
+    shstrtab_builder_.section_.sh_addr = 0;
+    shstrtab_builder_.section_.sh_size = shstrtab_.size();
+    shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
+    if (debug_logging_) {
+        LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
+                  << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
+    }
+
+    // The section list comes after come after.
+    Elf_Word sections_offset = RoundUp(
+        shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
+        sizeof(Elf_Word));
+
+    // Setup the actual symbol arrays.
+    std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
+    CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
+    std::vector<Elf_Sym> symtab;
+    if (IncludingDebugSymbols()) {
+      symtab = symtab_builder_.GenerateSymtab();
+      CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
+    }
+
+    // Setup the dynamic section.
+    // This will add the 2 values we cannot know until now time, namely the size
+    // and the soname_offset.
+    std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
+                                                                  dynstr_soname_offset_);
+    CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
+
+    // Finish setup of the program headers now that we know the layout of the
+    // whole file.
+    Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
+    program_headers_[PH_LOAD_R__].p_memsz =  load_r_size;
+    program_headers_[PH_LOAD_R__].p_align =  rodata_builder_.section_.sh_addralign;
+
+    Elf_Word load_rx_size = text_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_R_X].p_vaddr  = text_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_R_X].p_paddr  = text_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
+    program_headers_[PH_LOAD_R_X].p_memsz  = load_rx_size;
+    program_headers_[PH_LOAD_R_X].p_align  = text_builder_.section_.sh_addralign;
+
+    program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_RW_].p_vaddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_RW_].p_paddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_RW_].p_memsz  = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_RW_].p_align  = dynamic_builder_.section_.sh_addralign;
+
+    program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_DYNAMIC].p_vaddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_DYNAMIC].p_paddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_DYNAMIC].p_memsz  = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_DYNAMIC].p_align  = dynamic_builder_.section_.sh_addralign;
+
+    // Finish setup of the Ehdr values.
+    elf_header_.e_phoff = PHDR_OFFSET;
+    elf_header_.e_shoff = sections_offset;
+    elf_header_.e_phnum = PH_NUM;
+    elf_header_.e_shnum = section_ptrs_.size();
+    elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
+
+    // Add the rest of the pieces to the list.
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Elf Header", 0, &elf_header_,
+                                                      sizeof(elf_header_)));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Program headers", PHDR_OFFSET,
+                                                      &program_headers_, sizeof(program_headers_)));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynamic",
+                                                      dynamic_builder_.section_.sh_offset,
+                                                      dynamic.data(),
+                                                      dynamic_builder_.section_.sh_size));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynsym", dynsym_builder_.section_.sh_offset,
+                                                      dynsym.data(),
+                                                      dynsym.size() * sizeof(Elf_Sym)));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynstr",
+                                                    dynsym_builder_.GetStrTab()->section_.sh_offset,
+                                                    dynstr_.c_str(), dynstr_.size()));
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".hash", hash_builder_.section_.sh_offset,
+                                                      hash_.data(),
+                                                      hash_.size() * sizeof(Elf_Word)));
+    pieces.push_back(new ElfFileRodataPiece<Elf_Word>(rodata_builder_.section_.sh_offset,
+                                                      oat_writer_));
+    pieces.push_back(new ElfFileOatTextPiece<Elf_Word>(text_builder_.section_.sh_offset,
+                                                       oat_writer_));
+    if (IncludingDebugSymbols()) {
+      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".symtab",
+                                                        symtab_builder_.section_.sh_offset,
+                                                        symtab.data(),
+                                                        symtab.size() * sizeof(Elf_Sym)));
+      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".strtab",
+                                                    symtab_builder_.GetStrTab()->section_.sh_offset,
+                                                    strtab.c_str(), strtab.size()));
+    }
+    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".shstrtab",
+                                                      shstrtab_builder_.section_.sh_offset,
+                                                      &shstrtab_[0], shstrtab_.size()));
+    for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
+      // Just add all the sections in induvidually since they are all over the
+      // place on the heap/stack.
+      Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
+      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("section table piece", cur_off,
+                                                        section_ptrs_[i], sizeof(Elf_Shdr)));
+    }
+
+    // Postponed debug info.
+    for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(it->name_, it->section_.sh_offset,
+                                                        it->GetBuffer()->data(),
+                                                        it->GetBuffer()->size()));
+    }
+
+    if (!WriteOutFile(pieces)) {
+      LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
+
+      STLDeleteElements(&pieces);  // Have to manually clean pieces.
+      return false;
+    }
+
+    STLDeleteElements(&pieces);  // Have to manually clean pieces.
+    return true;
+  }
+
+  // Adds the given raw section to the builder. This will copy it. The caller
+  // is responsible for deallocating their copy.
+  void RegisterRawSection(ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> bld) {
+    other_builders_.push_back(bld);
+  }
+
+ private:
+  CodeOutput* oat_writer_;
+  File* elf_file_;
+  const bool add_symbols_;
+  const bool debug_logging_;
+
+  bool fatal_error_ = false;
+
+  // What phdr is.
+  static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
+  enum : uint8_t {
+    PH_PHDR     = 0,
+        PH_LOAD_R__ = 1,
+        PH_LOAD_R_X = 2,
+        PH_LOAD_RW_ = 3,
+        PH_DYNAMIC  = 4,
+        PH_NUM      = 5,
+  };
+  static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
+  Elf_Phdr program_headers_[PH_NUM];
+
+  Elf_Ehdr elf_header_;
+
+  Elf_Shdr null_hdr_;
+  std::string shstrtab_;
+  uint32_t section_index_;
+  std::string dynstr_;
+  uint32_t dynstr_soname_offset_;
+  std::vector<Elf_Shdr*> section_ptrs_;
+  std::vector<Elf_Word> hash_;
+
+ public:
+  ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> text_builder_;
+  ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> rodata_builder_;
+  ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> dynsym_builder_;
+  ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> symtab_builder_;
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> hash_builder_;
+  ElfDynamicBuilder<Elf_Word, Elf_Sword, Elf_Dyn, Elf_Shdr> dynamic_builder_;
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> shstrtab_builder_;
+  std::vector<ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>> other_builders_;
+
+ private:
+  void SetISA(InstructionSet isa) {
+    switch (isa) {
+      case kArm:
+        // Fall through.
+      case kThumb2: {
+        elf_header_.e_machine = EM_ARM;
+        elf_header_.e_flags = EF_ARM_EABI_VER5;
+        break;
+      }
+      case kArm64: {
+        elf_header_.e_machine = EM_AARCH64;
+        elf_header_.e_flags = 0;
+        break;
+      }
+      case kX86: {
+        elf_header_.e_machine = EM_386;
+        elf_header_.e_flags = 0;
+        break;
+      }
+      case kX86_64: {
+        elf_header_.e_machine = EM_X86_64;
+        elf_header_.e_flags = 0;
+        break;
+      }
+      case kMips: {
+        elf_header_.e_machine = EM_MIPS;
+        elf_header_.e_flags = (EF_MIPS_NOREORDER |
+                               EF_MIPS_PIC       |
+                               EF_MIPS_CPIC      |
+                               EF_MIPS_ABI_O32   |
+                               EF_MIPS_ARCH_32R2);
+        break;
+      }
+      default: {
+        fatal_error_ = true;
+        LOG(FATAL) << "Unknown instruction set: " << isa;
+        break;
+      }
+    }
+  }
+
+  void SetupEhdr() {
+    memset(&elf_header_, 0, sizeof(elf_header_));
+    elf_header_.e_ident[EI_MAG0]       = ELFMAG0;
+    elf_header_.e_ident[EI_MAG1]       = ELFMAG1;
+    elf_header_.e_ident[EI_MAG2]       = ELFMAG2;
+    elf_header_.e_ident[EI_MAG3]       = ELFMAG3;
+    elf_header_.e_ident[EI_CLASS]      = ELFCLASS32;
+    elf_header_.e_ident[EI_DATA]       = ELFDATA2LSB;
+    elf_header_.e_ident[EI_VERSION]    = EV_CURRENT;
+    elf_header_.e_ident[EI_OSABI]      = ELFOSABI_LINUX;
+    elf_header_.e_ident[EI_ABIVERSION] = 0;
+    elf_header_.e_type = ET_DYN;
+    elf_header_.e_version = 1;
+    elf_header_.e_entry = 0;
+    elf_header_.e_ehsize = sizeof(Elf_Ehdr);
+    elf_header_.e_phentsize = sizeof(Elf_Phdr);
+    elf_header_.e_shentsize = sizeof(Elf_Shdr);
+    elf_header_.e_phoff = sizeof(Elf_Ehdr);
+  }
+
+  // Sets up a bunch of the required Dynamic Section entries.
+  // Namely it will initialize all the mandatory ones that it can.
+  // Specifically:
+  // DT_HASH
+  // DT_STRTAB
+  // DT_SYMTAB
+  // DT_SYMENT
+  //
+  // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
+  void SetupDynamic() {
+    dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
+    dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, dynsym_builder_.GetStrTab());
+    dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
+    dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
+  }
+
+  // Sets up the basic dynamic symbols that are needed, namely all those we
+  // can know already.
+  //
+  // Specifically adds:
+  // oatdata
+  // oatexec
+  // oatlastword
+  void SetupRequiredSymbols() {
+    dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
+                              rodata_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+    dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
+                              text_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+    dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.GetSize() - 4,
+                              true, 4, STB_GLOBAL, STT_OBJECT);
+  }
+
+  void AssignSectionStr(ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder,
+                        std::string* strtab) {
+    builder->section_.sh_name = strtab->size();
+    *strtab += builder->name_;
+    *strtab += '\0';
+    if (debug_logging_) {
+      LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
+                << "to shstrtab at offset " << builder->section_.sh_name;
+    }
+  }
+
+
+  // Write each of the pieces out to the file.
+  bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces) {
+    for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+      if (!(*it)->Write(elf_file_)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_ELF_BUILDER_H_
diff --git a/compiler/elf_fixup.cc b/compiler/elf_fixup.cc
index 0155c82..0d34879 100644
--- a/compiler/elf_fixup.cc
+++ b/compiler/elf_fixup.cc
@@ -38,28 +38,32 @@
   Elf32_Off base_address = oat_data_begin - oatdata_address;
 
   if (!FixupDynamic(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup .dynamic in " << file->GetPath();
-      return false;
+    LOG(WARNING) << "Failed to fixup .dynamic in " << file->GetPath();
+    return false;
   }
   if (!FixupSectionHeaders(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup section headers in " << file->GetPath();
-      return false;
+    LOG(WARNING) << "Failed to fixup section headers in " << file->GetPath();
+    return false;
   }
   if (!FixupProgramHeaders(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup program headers in " << file->GetPath();
-      return false;
+    LOG(WARNING) << "Failed to fixup program headers in " << file->GetPath();
+    return false;
   }
   if (!FixupSymbols(*elf_file.get(), base_address, true)) {
-      LOG(WARNING) << "Failed fo fixup .dynsym in " << file->GetPath();
-      return false;
+    LOG(WARNING) << "Failed to fixup .dynsym in " << file->GetPath();
+    return false;
   }
   if (!FixupSymbols(*elf_file.get(), base_address, false)) {
-      LOG(WARNING) << "Failed fo fixup .symtab in " << file->GetPath();
-      return false;
+    LOG(WARNING) << "Failed to fixup .symtab in " << file->GetPath();
+    return false;
   }
   if (!FixupRelocations(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup .rel.dyn in " << file->GetPath();
-      return false;
+    LOG(WARNING) << "Failed to fixup .rel.dyn in " << file->GetPath();
+    return false;
+  }
+  if (!elf_file->FixupDebugSections(base_address)) {
+    LOG(WARNING) << "Failed to fixup debug sections in " << file->GetPath();
+    return false;
   }
   return true;
 }
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
deleted file mode 100644
index e4c957a..0000000
--- a/compiler/elf_patcher.cc
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "elf_patcher.h"
-
-#include <vector>
-#include <set>
-
-#include "elf_file.h"
-#include "elf_utils.h"
-#include "mirror/art_field-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/array-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/class_loader.h"
-#include "mirror/dex_cache-inl.h"
-#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/string-inl.h"
-#include "oat.h"
-#include "os.h"
-#include "utils.h"
-
-namespace art {
-
-bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf_file,
-                       const std::string& oat_location,
-                       ImageAddressCallback cb, void* cb_data,
-                       std::string* error_msg) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
-  if (oat_file == nullptr) {
-    CHECK(Runtime::Current()->IsCompiler());
-    oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
-    if (oat_file == nullptr) {
-      *error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
-                                error_msg->c_str());
-      return false;
-    }
-    CHECK_EQ(class_linker->RegisterOatFile(oat_file), oat_file);
-  }
-  return ElfPatcher::Patch(driver, elf_file, oat_file,
-                           reinterpret_cast<uintptr_t>(oat_file->Begin()), cb, cb_data, error_msg);
-}
-
-bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf, const OatFile* oat_file,
-                       uintptr_t oat_data_start, ImageAddressCallback cb, void* cb_data,
-                       std::string* error_msg) {
-  Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
-  if (data_sec == nullptr) {
-    *error_msg = "Unable to find .rodata section and oat header";
-    return false;
-  }
-  OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
-  if (!oat_header->IsValid()) {
-    *error_msg = "Oat header was not valid";
-    return false;
-  }
-
-  ElfPatcher p(driver, elf, oat_file, oat_header, oat_data_start, cb, cb_data, error_msg);
-  return p.PatchElf();
-}
-
-mirror::ArtMethod* ElfPatcher::GetTargetMethod(const CompilerDriver::CallPatchInformation* patch) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  StackHandleScope<1> hs(Thread::Current());
-  Handle<mirror::DexCache> dex_cache(
-      hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
-  mirror::ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
-                                                          patch->GetTargetMethodIdx(),
-                                                          dex_cache,
-                                                          NullHandle<mirror::ClassLoader>(),
-                                                          NullHandle<mirror::ArtMethod>(),
-                                                          patch->GetTargetInvokeType());
-  CHECK(method != NULL)
-    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
-  CHECK(!method->IsRuntimeMethod())
-    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
-  CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
-    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
-    << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
-    << PrettyMethod(method);
-  return method;
-}
-
-mirror::Class* ElfPatcher::GetTargetType(const CompilerDriver::TypePatchInformation* patch) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  StackHandleScope<2> hs(Thread::Current());
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
-  mirror::Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
-                                                   dex_cache, NullHandle<mirror::ClassLoader>());
-  CHECK(klass != NULL)
-    << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
-  CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
-    << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
-    << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
-    << PrettyClass(klass);
-  return klass;
-}
-
-void ElfPatcher::AddPatch(uintptr_t p) {
-  if (write_patches_ && patches_set_.find(p) == patches_set_.end()) {
-    patches_set_.insert(p);
-    patches_.push_back(p);
-  }
-}
-
-uint32_t* ElfPatcher::GetPatchLocation(uintptr_t patch_ptr) {
-  CHECK_GE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()));
-  CHECK_LE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->End()));
-  uintptr_t off = patch_ptr - reinterpret_cast<uintptr_t>(oat_file_->Begin());
-  uintptr_t ret = reinterpret_cast<uintptr_t>(oat_header_) + off;
-
-  CHECK_GE(ret, reinterpret_cast<uintptr_t>(elf_file_->Begin()));
-  CHECK_LT(ret, reinterpret_cast<uintptr_t>(elf_file_->End()));
-  return reinterpret_cast<uint32_t*>(ret);
-}
-
-void ElfPatcher::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
-                                                                patch->GetReferrerClassDefIdx(),
-                                                                patch->GetReferrerMethodIdx());
-  // TODO: make this Thumb2 specific
-  uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
-  uintptr_t patch_ptr = reinterpret_cast<uintptr_t>(base + patch->GetLiteralOffset());
-  uint32_t* patch_location = GetPatchLocation(patch_ptr);
-  if (kIsDebugBuild) {
-    if (patch->IsCall()) {
-      const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
-      const DexFile::MethodId& id =
-          cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
-      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
-      uint32_t actual = *patch_location;
-      CHECK(actual == expected || actual == value) << "Patching call failed: " << std::hex
-          << " actual=" << actual
-          << " expected=" << expected
-          << " value=" << value;
-    }
-    if (patch->IsType()) {
-      const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
-      const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
-      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
-      uint32_t actual = *patch_location;
-      CHECK(actual == expected || actual == value) << "Patching type failed: " << std::hex
-          << " actual=" << actual
-          << " expected=" << expected
-          << " value=" << value;
-    }
-  }
-  *patch_location = value;
-  oat_header_->UpdateChecksum(patch_location, sizeof(value));
-
-  if (patch->IsCall() && patch->AsCall()->IsRelative()) {
-    // We never record relative patches.
-    return;
-  }
-  uintptr_t loc = patch_ptr - (reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
-                               oat_header_->GetExecutableOffset());
-  CHECK_GT(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
-                      oat_header_->GetExecutableOffset());
-  CHECK_LT(loc, oat_file_->Size() - oat_header_->GetExecutableOffset());
-  AddPatch(loc);
-}
-
-bool ElfPatcher::PatchElf() {
-  // TODO if we are adding patches the resulting ELF file might have a
-  // potentially rather large amount of free space where patches might have been
-  // placed. We should adjust the ELF file to get rid of this excess space.
-  if (write_patches_) {
-    patches_.reserve(compiler_driver_->GetCodeToPatch().size() +
-                     compiler_driver_->GetMethodsToPatch().size() +
-                     compiler_driver_->GetClassesToPatch().size());
-  }
-  Thread* self = Thread::Current();
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  const char* old_cause = self->StartAssertNoThreadSuspension("ElfPatcher");
-
-  typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
-  const CallPatches& code_to_patch = compiler_driver_->GetCodeToPatch();
-  for (size_t i = 0; i < code_to_patch.size(); i++) {
-    const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
-
-    mirror::ArtMethod* target = GetTargetMethod(patch);
-    uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
-    DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
-    const OatFile* target_oat =
-        class_linker->FindOpenedOatDexFileForDexFile(*patch->GetTargetDexFile())->GetOatFile();
-    // Get where the data actually starts. if target is this oat_file_ it is oat_data_start_,
-    // otherwise it is wherever target_oat is loaded.
-    uintptr_t oat_data_addr = GetBaseAddressFor(target_oat);
-    uintptr_t code_base = reinterpret_cast<uintptr_t>(target_oat->Begin());
-    uintptr_t code_offset = quick_code - code_base;
-    bool is_quick_offset = false;
-    if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
-      is_quick_offset = true;
-      code_offset = oat_header_->GetQuickToInterpreterBridgeOffset();
-    } else if (quick_code ==
-        reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
-      CHECK(target->IsNative());
-      is_quick_offset = true;
-      code_offset = oat_header_->GetQuickGenericJniTrampolineOffset();
-    }
-    uintptr_t value;
-    if (patch->IsRelative()) {
-      // value to patch is relative to the location being patched
-      const void* quick_oat_code =
-        class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
-                                         patch->GetReferrerClassDefIdx(),
-                                         patch->GetReferrerMethodIdx());
-      if (is_quick_offset) {
-        // If its a quick offset it means that we are doing a relative patch from the class linker
-        // oat_file to the elf_patcher oat_file so we need to adjust the quick oat code to be the
-        // one in the output oat_file (ie where it is actually going to be loaded).
-        quick_code = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
-        quick_oat_code =
-            reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
-                oat_data_addr - code_base);
-      }
-      uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
-      uintptr_t patch_location = base + patch->GetLiteralOffset();
-      value = quick_code - patch_location + patch->RelativeOffset();
-    } else if (code_offset != 0) {
-      value = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
-    } else {
-      value = 0;
-    }
-    SetPatchLocation(patch, value);
-  }
-
-  const CallPatches& methods_to_patch = compiler_driver_->GetMethodsToPatch();
-  for (size_t i = 0; i < methods_to_patch.size(); i++) {
-    const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
-    mirror::ArtMethod* target = GetTargetMethod(patch);
-    SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
-  }
-
-  const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
-      compiler_driver_->GetClassesToPatch();
-  for (size_t i = 0; i < classes_to_patch.size(); i++) {
-    const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
-    mirror::Class* target = GetTargetType(patch);
-    SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
-  }
-
-  self->EndAssertNoThreadSuspension(old_cause);
-
-  if (write_patches_) {
-    return WriteOutPatchData();
-  }
-  return true;
-}
-
-bool ElfPatcher::WriteOutPatchData() {
-  Elf32_Shdr* shdr = elf_file_->FindSectionByName(".oat_patches");
-  if (shdr != nullptr) {
-    CHECK_EQ(shdr, elf_file_->FindSectionByType(SHT_OAT_PATCH))
-        << "Incorrect type for .oat_patches section";
-    CHECK_LE(patches_.size() * sizeof(uintptr_t), shdr->sh_size)
-        << "We got more patches than anticipated";
-    CHECK_LE(reinterpret_cast<uintptr_t>(elf_file_->Begin()) + shdr->sh_offset + shdr->sh_size,
-              reinterpret_cast<uintptr_t>(elf_file_->End())) << "section is too large";
-    CHECK(shdr == elf_file_->GetSectionHeader(elf_file_->GetSectionHeaderNum() - 1) ||
-          shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
-        << "Section overlaps onto next section";
-    // It's mmap'd so we can just memcpy.
-    memcpy(elf_file_->Begin() + shdr->sh_offset, patches_.data(),
-           patches_.size() * sizeof(uintptr_t));
-    // TODO We should fill in the newly empty space between the last patch and
-    // the start of the next section by moving the following sections down if
-    // possible.
-    shdr->sh_size = patches_.size() * sizeof(uintptr_t);
-    return true;
-  } else {
-    LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
-    *error_msg_ = "Unable to find section to write patch information to in ";
-    *error_msg_ += elf_file_->GetFile().GetPath();
-    return false;
-  }
-}
-
-}  // namespace art
diff --git a/compiler/elf_patcher.h b/compiler/elf_patcher.h
deleted file mode 100644
index 0a9f0a01..0000000
--- a/compiler/elf_patcher.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_ELF_PATCHER_H_
-#define ART_COMPILER_ELF_PATCHER_H_
-
-#include "base/mutex.h"
-#include "driver/compiler_driver.h"
-#include "elf_file.h"
-#include "mirror/art_method.h"
-#include "mirror/class.h"
-#include "mirror/object.h"
-#include "oat_file.h"
-#include "oat.h"
-#include "os.h"
-
-namespace art {
-
-class ElfPatcher {
- public:
-  typedef void* (*ImageAddressCallback)(void* data, mirror::Object* obj);
-
-  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
-                    const std::string& oat_location,
-                    ImageAddressCallback cb, void* cb_data,
-                    std::string* error_msg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
-                    const OatFile* oat_file, uintptr_t oat_data_begin,
-                    ImageAddressCallback cb, void* cb_data,
-                    std::string* error_msg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
-                    const std::string& oat_location,
-                    std::string* error_msg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return ElfPatcher::Patch(driver, elf_file, oat_location,
-                             DefaultImageAddressCallback, nullptr, error_msg);
-  }
-
-  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
-                    const OatFile* oat_file, uintptr_t oat_data_begin,
-                    std::string* error_msg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return ElfPatcher::Patch(driver, elf_file, oat_file, oat_data_begin,
-                             DefaultImageAddressCallback, nullptr, error_msg);
-  }
-
- private:
-  ElfPatcher(const CompilerDriver* driver, ElfFile* elf_file, const OatFile* oat_file,
-             OatHeader* oat_header, uintptr_t oat_data_begin,
-             ImageAddressCallback cb, void* cb_data, std::string* error_msg)
-      : compiler_driver_(driver), elf_file_(elf_file), oat_file_(oat_file),
-        oat_header_(oat_header), oat_data_begin_(oat_data_begin), get_image_address_(cb),
-        cb_data_(cb_data), error_msg_(error_msg),
-        write_patches_(compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {}
-  ~ElfPatcher() {}
-
-  static void* DefaultImageAddressCallback(void* data_unused, mirror::Object* obj) {
-    return static_cast<void*>(obj);
-  }
-
-  bool PatchElf()
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  mirror::ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  mirror::Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void AddPatch(uintptr_t off);
-
-  void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  // Takes the pointer into the oat_file_ and get the pointer in to the ElfFile.
-  uint32_t* GetPatchLocation(uintptr_t patch_ptr);
-
-  bool WriteOutPatchData();
-
-  uintptr_t GetBaseAddressFor(const OatFile* f) {
-    if (f == oat_file_) {
-      return oat_data_begin_;
-    } else {
-      return reinterpret_cast<uintptr_t>(f->Begin());
-    }
-  }
-
-  const CompilerDriver* compiler_driver_;
-
-  // The elf_file containing the oat_data we are patching up
-  ElfFile* elf_file_;
-
-  // The oat_file that is actually loaded.
-  const OatFile* oat_file_;
-
-  // The oat_header_ within the elf_file_
-  OatHeader* oat_header_;
-
-  // Where the elf_file will be loaded during normal runs.
-  uintptr_t oat_data_begin_;
-
-  // Callback to get image addresses.
-  ImageAddressCallback get_image_address_;
-  void* cb_data_;
-
-  std::string* error_msg_;
-  std::vector<uintptr_t> patches_;
-  std::set<uintptr_t> patches_set_;
-  bool write_patches_;
-
-  DISALLOW_COPY_AND_ASSIGN(ElfPatcher);
-};
-
-}  // namespace art
-#endif  // ART_COMPILER_ELF_PATCHER_H_
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc
index 3dba426..f017641 100644
--- a/compiler/elf_writer_mclinker.cc
+++ b/compiler/elf_writer_mclinker.cc
@@ -67,19 +67,40 @@
                               bool is_host) {
   std::vector<uint8_t> oat_contents;
   oat_contents.reserve(oat_writer->GetSize());
-  VectorOutputStream output_stream("oat contents", oat_contents);
-  CHECK(oat_writer->Write(&output_stream));
-  CHECK_EQ(oat_writer->GetSize(), oat_contents.size());
 
   Init();
-  AddOatInput(oat_contents);
+  mcld::LDSection* oat_section = AddOatInput(oat_writer, &oat_contents);
   if (kUsePortableCompiler) {
     AddMethodInputs(dex_files);
     AddRuntimeInputs(android_root, is_host);
   }
-  if (!Link()) {
+
+  // link inputs
+  if (!linker_->link(*module_.get(), *ir_builder_.get())) {
+    LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
     return false;
   }
+
+  // Fill oat_contents.
+  VectorOutputStream output_stream("oat contents", oat_contents);
+  oat_writer->SetOatDataOffset(oat_section->offset());
+  CHECK(oat_writer->Write(&output_stream));
+  CHECK_EQ(oat_writer->GetSize(), oat_contents.size());
+
+  // emit linked output
+  // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
+  int fd = dup(elf_file_->Fd());
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!linker_->emit(*module_.get(), fd)) {
+    LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
+    return false;
+  }
+  mcld::Finalize();
+  LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
+
   oat_contents.clear();
   if (kUsePortableCompiler) {
     FixupOatMethodOffsets(dex_files);
@@ -156,16 +177,13 @@
   linker_->emulate(*linker_script_.get(), *linker_config_.get());
 }
 
-void ElfWriterMclinker::AddOatInput(std::vector<uint8_t>& oat_contents) {
-  // Add an artificial memory input. Based on LinkerTest.
-  std::string error_msg;
-  std::unique_ptr<OatFile> oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath(), &error_msg));
-  CHECK(oat_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg;
-
-  const char* oat_data_start = reinterpret_cast<const char*>(&oat_file->GetOatHeader());
-  const size_t oat_data_length = oat_file->GetOatHeader().GetExecutableOffset();
+mcld::LDSection* ElfWriterMclinker::AddOatInput(OatWriter* oat_writer,
+                                                std::vector<uint8_t>* oat_contents) {
+  // NOTE: oat_contents has sufficient reserved space but it doesn't contain the data yet.
+  const char* oat_data_start = reinterpret_cast<const char*>(&(*oat_contents)[0]);
+  const size_t oat_data_length = oat_writer->GetOatHeader().GetExecutableOffset();
   const char* oat_code_start = oat_data_start + oat_data_length;
-  const size_t oat_code_length = oat_file->Size() - oat_data_length;
+  const size_t oat_code_length = oat_writer->GetSize() - oat_data_length;
 
   // TODO: ownership of oat_input?
   oat_input_ = ir_builder_->CreateInput("oat contents",
@@ -205,7 +223,7 @@
 
   // TODO: why does IRBuilder::CreateRegion take a non-const pointer?
   mcld::Fragment* text_fragment = ir_builder_->CreateRegion(const_cast<char*>(oat_data_start),
-                                                            oat_file->Size());
+                                                            oat_writer->GetSize());
   CHECK(text_fragment != NULL);
   ir_builder_->AppendFragment(*text_fragment, *text_sectiondata);
 
@@ -236,6 +254,8 @@
                          // subtract a word so symbol is within section
                          (oat_data_length + oat_code_length) - sizeof(uint32_t),  // offset
                          text_section);
+
+  return text_section;
 }
 
 void ElfWriterMclinker::AddMethodInputs(const std::vector<const DexFile*>& dex_files) {
@@ -322,29 +342,6 @@
   CHECK(libm_lib_input_input != NULL);
 }
 
-bool ElfWriterMclinker::Link() {
-  // link inputs
-  if (!linker_->link(*module_.get(), *ir_builder_.get())) {
-    LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
-    return false;
-  }
-
-  // emit linked output
-  // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
-  int fd = dup(elf_file_->Fd());
-  if (fd == -1) {
-    PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
-    return false;
-  }
-  if (!linker_->emit(*module_.get(), fd)) {
-    LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
-    return false;
-  }
-  mcld::Finalize();
-  LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
-  return true;
-}
-
 void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
   std::string error_msg;
   std::unique_ptr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false, &error_msg));
diff --git a/compiler/elf_writer_mclinker.h b/compiler/elf_writer_mclinker.h
index 955e5d2..489fefb 100644
--- a/compiler/elf_writer_mclinker.h
+++ b/compiler/elf_writer_mclinker.h
@@ -61,11 +61,10 @@
   ~ElfWriterMclinker();
 
   void Init();
-  void AddOatInput(std::vector<uint8_t>& oat_contents);
+  mcld::LDSection* AddOatInput(OatWriter* oat_writer, std::vector<uint8_t>* oat_contents);
   void AddMethodInputs(const std::vector<const DexFile*>& dex_files);
   void AddCompiledCodeInput(const CompiledCode& compiled_code);
   void AddRuntimeInputs(const std::string& android_root, bool is_host);
-  bool Link();
   void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   uint32_t FixupCompiledCodeOffset(ElfFile& elf_file,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 703e63f..e661324 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -16,869 +16,66 @@
 
 #include "elf_writer_quick.h"
 
+#include <unordered_map>
+
 #include "base/logging.h"
-#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "buffered_output_stream.h"
 #include "driver/compiler_driver.h"
 #include "dwarf.h"
+#include "elf_builder.h"
+#include "elf_file.h"
 #include "elf_utils.h"
 #include "file_output_stream.h"
 #include "globals.h"
+#include "leb128.h"
 #include "oat.h"
 #include "oat_writer.h"
 #include "utils.h"
 
 namespace art {
 
-static constexpr Elf32_Word NextOffset(const Elf32_Shdr& cur, const Elf32_Shdr& prev) {
-  return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
+static void PushByte(std::vector<uint8_t>* buf, int data) {
+  buf->push_back(data & 0xff);
 }
 
-static uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
-  return ((binding) << 4) + ((type) & 0xf);
+static uint32_t PushStr(std::vector<uint8_t>* buf, const char* str, const char* def = nullptr) {
+  if (str == nullptr) {
+    str = def;
+  }
+
+  uint32_t offset = buf->size();
+  for (size_t i = 0; str[i] != '\0'; ++i) {
+    buf->push_back(str[i]);
+  }
+  buf->push_back('\0');
+  return offset;
 }
 
-class ElfFilePiece {
- public:
-  virtual ~ElfFilePiece() {}
-
-  virtual bool Write(File* elf_file) {
-    if (static_cast<off_t>(offset_) != lseek(elf_file->Fd(), offset_, SEEK_SET)) {
-      PLOG(ERROR) << "Failed to seek to " << GetDescription() << " offset " << offset_ << " for "
-          << elf_file->GetPath();
-      return false;
-    }
-
-    return DoActualWrite(elf_file);
-  }
-
-  static bool Compare(ElfFilePiece* a, ElfFilePiece* b) {
-    return a->offset_ < b->offset_;
-  }
-
- protected:
-  explicit ElfFilePiece(Elf32_Word offset) : offset_(offset) {}
-
-  virtual std::string GetDescription() = 0;
-  virtual bool DoActualWrite(File* elf_file) = 0;
-
-  Elf32_Word offset_;
-};
-
-class ElfFileMemoryPiece : public ElfFilePiece {
- public:
-  ElfFileMemoryPiece(const std::string& name, Elf32_Word offset, const void* data, Elf32_Word size)
-      : ElfFilePiece(offset), dbg_name_(name), data_(data), size_(size) {}
-
-  bool DoActualWrite(File* elf_file) OVERRIDE {
-    DCHECK(data_ != nullptr || size_ == 0U) << dbg_name_ << " " << size_;
-
-    if (!elf_file->WriteFully(data_, size_)) {
-      PLOG(ERROR) << "Failed to write " << dbg_name_ << " for " << elf_file->GetPath();
-      return false;
-    }
-
-    return true;
-  }
-
-  std::string GetDescription() OVERRIDE {
-    return dbg_name_;
-  }
-
- private:
-  const std::string& dbg_name_;
-  const void *data_;
-  Elf32_Word size_;
-};
-
-class ElfFileRodataPiece : public ElfFilePiece {
- public:
-  ElfFileRodataPiece(Elf32_Word offset, OatWriter* oat_writer) : ElfFilePiece(offset),
-      oat_writer_(oat_writer) {}
-
-  bool DoActualWrite(File* elf_file) OVERRIDE {
-    std::unique_ptr<BufferedOutputStream> output_stream(
-        new BufferedOutputStream(new FileOutputStream(elf_file)));
-    if (!oat_writer_->Write(output_stream.get())) {
-      PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file->GetPath();
-      return false;
-    }
-
-    return true;
-  }
-
-  std::string GetDescription() OVERRIDE {
-    return ".rodata";
-  }
-
- private:
-  OatWriter* oat_writer_;
-};
-
-class ElfFileOatTextPiece : public ElfFilePiece {
- public:
-  ElfFileOatTextPiece(Elf32_Word offset, OatWriter* oat_writer) : ElfFilePiece(offset),
-      oat_writer_(oat_writer) {}
-
-  bool DoActualWrite(File* elf_file) OVERRIDE {
-    // All data is written by the ElfFileRodataPiece right now, as the oat writer writes in one
-    // piece. This is for future flexibility.
-    UNUSED(oat_writer_);
-    return true;
-  }
-
-  std::string GetDescription() OVERRIDE {
-    return ".text";
-  }
-
- private:
-  OatWriter* oat_writer_;
-};
-
-static bool WriteOutFile(const std::vector<ElfFilePiece*>& pieces, File* elf_file) {
-  // TODO It would be nice if this checked for overlap.
-  for (auto it = pieces.begin(); it != pieces.end(); ++it) {
-    if (!(*it)->Write(elf_file)) {
-      return false;
-    }
-  }
-  return true;
+static uint32_t PushStr(std::vector<uint8_t>* buf, const std::string &str) {
+  uint32_t offset = buf->size();
+  buf->insert(buf->end(), str.begin(), str.end());
+  buf->push_back('\0');
+  return offset;
 }
 
-bool ElfWriterQuick::ElfBuilder::Write() {
-  // The basic layout of the elf file. Order may be different in final output.
-  // +-------------------------+
-  // | Elf32_Ehdr              |
-  // +-------------------------+
-  // | Elf32_Phdr PHDR         |
-  // | Elf32_Phdr LOAD R       | .dynsym .dynstr .hash .rodata
-  // | Elf32_Phdr LOAD R X     | .text
-  // | Elf32_Phdr LOAD RW      | .dynamic
-  // | Elf32_Phdr DYNAMIC      | .dynamic
-  // +-------------------------+
-  // | .dynsym                 |
-  // | Elf32_Sym  STN_UNDEF    |
-  // | Elf32_Sym  oatdata      |
-  // | Elf32_Sym  oatexec      |
-  // | Elf32_Sym  oatlastword  |
-  // +-------------------------+
-  // | .dynstr                 |
-  // | \0                      |
-  // | oatdata\0               |
-  // | oatexec\0               |
-  // | oatlastword\0           |
-  // | boot.oat\0              |
-  // +-------------------------+
-  // | .hash                   |
-  // | Elf32_Word nbucket = b  |
-  // | Elf32_Word nchain  = c  |
-  // | Elf32_Word bucket[0]    |
-  // |         ...             |
-  // | Elf32_Word bucket[b - 1]|
-  // | Elf32_Word chain[0]     |
-  // |         ...             |
-  // | Elf32_Word chain[c - 1] |
-  // +-------------------------+
-  // | .rodata                 |
-  // | oatdata..oatexec-4      |
-  // +-------------------------+
-  // | .text                   |
-  // | oatexec..oatlastword    |
-  // +-------------------------+
-  // | .dynamic                |
-  // | Elf32_Dyn DT_SONAME     |
-  // | Elf32_Dyn DT_HASH       |
-  // | Elf32_Dyn DT_SYMTAB     |
-  // | Elf32_Dyn DT_SYMENT     |
-  // | Elf32_Dyn DT_STRTAB     |
-  // | Elf32_Dyn DT_STRSZ      |
-  // | Elf32_Dyn DT_NULL       |
-  // +-------------------------+  (Optional)
-  // | .strtab                 |  (Optional)
-  // | program symbol names    |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .symtab                 |  (Optional)
-  // | program symbols         |  (Optional)
-  // +-------------------------+
-  // | .shstrtab               |
-  // | \0                      |
-  // | .dynamic\0              |
-  // | .dynsym\0               |
-  // | .dynstr\0               |
-  // | .hash\0                 |
-  // | .rodata\0               |
-  // | .text\0                 |
-  // | .shstrtab\0             |
-  // | .symtab\0               |  (Optional)
-  // | .strtab\0               |  (Optional)
-  // | .debug_str\0            |  (Optional)
-  // | .debug_info\0           |  (Optional)
-  // | .eh_frame\0             |  (Optional)
-  // | .debug_abbrev\0         |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .debug_str              |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .debug_info             |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .eh_frame               |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .debug_abbrev           |  (Optional)
-  // +-------------------------+
-  // | Elf32_Shdr NULL         |
-  // | Elf32_Shdr .dynsym      |
-  // | Elf32_Shdr .dynstr      |
-  // | Elf32_Shdr .hash        |
-  // | Elf32_Shdr .text        |
-  // | Elf32_Shdr .rodata      |
-  // | Elf32_Shdr .dynamic     |
-  // | Elf32_Shdr .shstrtab    |
-  // | Elf32_Shdr .debug_str   |  (Optional)
-  // | Elf32_Shdr .debug_info  |  (Optional)
-  // | Elf32_Shdr .eh_frame    |  (Optional)
-  // | Elf32_Shdr .debug_abbrev|  (Optional)
-  // +-------------------------+
-
-
-  if (fatal_error_) {
-    return false;
-  }
-  // Step 1. Figure out all the offsets.
-
-  // What phdr is.
-  uint32_t phdr_offset = sizeof(Elf32_Ehdr);
-  const uint8_t PH_PHDR     = 0;
-  const uint8_t PH_LOAD_R__ = 1;
-  const uint8_t PH_LOAD_R_X = 2;
-  const uint8_t PH_LOAD_RW_ = 3;
-  const uint8_t PH_DYNAMIC  = 4;
-  const uint8_t PH_NUM      = 5;
-  uint32_t phdr_size = sizeof(Elf32_Phdr) * PH_NUM;
-  if (debug_logging_) {
-    LOG(INFO) << "phdr_offset=" << phdr_offset << std::hex << " " << phdr_offset;
-    LOG(INFO) << "phdr_size=" << phdr_size << std::hex << " " << phdr_size;
-  }
-  Elf32_Phdr program_headers[PH_NUM];
-  memset(&program_headers, 0, sizeof(program_headers));
-  program_headers[PH_PHDR].p_type    = PT_PHDR;
-  program_headers[PH_PHDR].p_offset  = phdr_offset;
-  program_headers[PH_PHDR].p_vaddr   = phdr_offset;
-  program_headers[PH_PHDR].p_paddr   = phdr_offset;
-  program_headers[PH_PHDR].p_filesz  = sizeof(program_headers);
-  program_headers[PH_PHDR].p_memsz   = sizeof(program_headers);
-  program_headers[PH_PHDR].p_flags   = PF_R;
-  program_headers[PH_PHDR].p_align   = sizeof(Elf32_Word);
-
-  program_headers[PH_LOAD_R__].p_type    = PT_LOAD;
-  program_headers[PH_LOAD_R__].p_offset  = 0;
-  program_headers[PH_LOAD_R__].p_vaddr   = 0;
-  program_headers[PH_LOAD_R__].p_paddr   = 0;
-  program_headers[PH_LOAD_R__].p_flags   = PF_R;
-
-  program_headers[PH_LOAD_R_X].p_type    = PT_LOAD;
-  program_headers[PH_LOAD_R_X].p_flags   = PF_R | PF_X;
-
-  program_headers[PH_LOAD_RW_].p_type    = PT_LOAD;
-  program_headers[PH_LOAD_RW_].p_flags   = PF_R | PF_W;
-
-  program_headers[PH_DYNAMIC].p_type    = PT_DYNAMIC;
-  program_headers[PH_DYNAMIC].p_flags   = PF_R | PF_W;
-
-  // Get the dynstr string.
-  std::string dynstr(dynsym_builder_.GenerateStrtab());
-
-  // Add the SONAME to the dynstr.
-  uint32_t dynstr_soname_offset = dynstr.size();
-  std::string file_name(elf_file_->GetPath());
-  size_t directory_separator_pos = file_name.rfind('/');
-  if (directory_separator_pos != std::string::npos) {
-    file_name = file_name.substr(directory_separator_pos + 1);
-  }
-  dynstr += file_name;
-  dynstr += '\0';
-  if (debug_logging_) {
-    LOG(INFO) << "dynstr size (bytes)   =" << dynstr.size()
-              << std::hex << " " << dynstr.size();
-    LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
-              << std::hex << " " << dynsym_builder_.GetSize();
-  }
-
-  // get the strtab
-  std::string strtab;
-  if (IncludingDebugSymbols()) {
-    strtab = symtab_builder_.GenerateStrtab();
-    if (debug_logging_) {
-      LOG(INFO) << "strtab size (bytes)    =" << strtab.size()
-                << std::hex << " " << strtab.size();
-      LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
-                << std::hex << " " << symtab_builder_.GetSize();
-    }
-  }
-
-  // Get the section header string table.
-  std::vector<Elf32_Shdr*> section_ptrs;
-  std::string shstrtab;
-  shstrtab += '\0';
-
-  // Setup sym_undef
-  Elf32_Shdr null_hdr;
-  memset(&null_hdr, 0, sizeof(null_hdr));
-  null_hdr.sh_type = SHT_NULL;
-  null_hdr.sh_link = SHN_UNDEF;
-  section_ptrs.push_back(&null_hdr);
-
-  uint32_t section_index = 1;
-
-  // setup .dynsym
-  section_ptrs.push_back(&dynsym_builder_.section_);
-  AssignSectionStr(&dynsym_builder_, &shstrtab);
-  dynsym_builder_.section_index_ = section_index++;
-
-  // Setup .dynstr
-  section_ptrs.push_back(&dynsym_builder_.strtab_.section_);
-  AssignSectionStr(&dynsym_builder_.strtab_, &shstrtab);
-  dynsym_builder_.strtab_.section_index_ = section_index++;
-
-  // Setup .hash
-  section_ptrs.push_back(&hash_builder_.section_);
-  AssignSectionStr(&hash_builder_, &shstrtab);
-  hash_builder_.section_index_ = section_index++;
-
-  // Setup .rodata
-  section_ptrs.push_back(&rodata_builder_.section_);
-  AssignSectionStr(&rodata_builder_, &shstrtab);
-  rodata_builder_.section_index_ = section_index++;
-
-  // Setup .text
-  section_ptrs.push_back(&text_builder_.section_);
-  AssignSectionStr(&text_builder_, &shstrtab);
-  text_builder_.section_index_ = section_index++;
-
-  // Setup .dynamic
-  section_ptrs.push_back(&dynamic_builder_.section_);
-  AssignSectionStr(&dynamic_builder_, &shstrtab);
-  dynamic_builder_.section_index_ = section_index++;
-
-  if (IncludingDebugSymbols()) {
-    // Setup .symtab
-    section_ptrs.push_back(&symtab_builder_.section_);
-    AssignSectionStr(&symtab_builder_, &shstrtab);
-    symtab_builder_.section_index_ = section_index++;
-
-    // Setup .strtab
-    section_ptrs.push_back(&symtab_builder_.strtab_.section_);
-    AssignSectionStr(&symtab_builder_.strtab_, &shstrtab);
-    symtab_builder_.strtab_.section_index_ = section_index++;
-  }
-  ElfRawSectionBuilder* it = other_builders_.data();
-  for (uint32_t cnt = 0; cnt < other_builders_.size(); ++it, ++cnt) {
-    // Setup all the other sections.
-    section_ptrs.push_back(&it->section_);
-    AssignSectionStr(it, &shstrtab);
-    it->section_index_ = section_index++;
-  }
-
-  // Setup shstrtab
-  section_ptrs.push_back(&shstrtab_builder_.section_);
-  AssignSectionStr(&shstrtab_builder_, &shstrtab);
-  shstrtab_builder_.section_index_ = section_index++;
-
-  if (debug_logging_) {
-    LOG(INFO) << ".shstrtab size    (bytes)   =" << shstrtab.size()
-              << std::hex << " " << shstrtab.size();
-    LOG(INFO) << "section list size (elements)=" << section_ptrs.size()
-              << std::hex << " " << section_ptrs.size();
-  }
-
-  // Fill in the hash section.
-  std::vector<Elf32_Word> hash = dynsym_builder_.GenerateHashContents();
-
-  if (debug_logging_) {
-    LOG(INFO) << ".hash size (bytes)=" << hash.size() * sizeof(Elf32_Word)
-              << std::hex << " " << hash.size() * sizeof(Elf32_Word);
-  }
-
-  Elf32_Word base_offset = sizeof(Elf32_Ehdr) + sizeof(program_headers);
-  std::vector<ElfFilePiece*> pieces;
-
-  // Get the layout in the sections.
-  //
-  // Get the layout of the dynsym section.
-  dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
-  dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
-  dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf32_Sym);
-  dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
-
-  // Get the layout of the dynstr section.
-  dynsym_builder_.strtab_.section_.sh_offset = NextOffset(dynsym_builder_.strtab_.section_,
-                                                          dynsym_builder_.section_);
-  dynsym_builder_.strtab_.section_.sh_addr = dynsym_builder_.strtab_.section_.sh_offset;
-  dynsym_builder_.strtab_.section_.sh_size = dynstr.size();
-  dynsym_builder_.strtab_.section_.sh_link = dynsym_builder_.strtab_.GetLink();
-
-  // Get the layout of the hash section
-  hash_builder_.section_.sh_offset = NextOffset(hash_builder_.section_,
-                                                dynsym_builder_.strtab_.section_);
-  hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
-  hash_builder_.section_.sh_size = hash.size() * sizeof(Elf32_Word);
-  hash_builder_.section_.sh_link = hash_builder_.GetLink();
-
-  // Get the layout of the rodata section.
-  rodata_builder_.section_.sh_offset = NextOffset(rodata_builder_.section_,
-                                                  hash_builder_.section_);
-  rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
-  rodata_builder_.section_.sh_size = rodata_builder_.size_;
-  rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
-
-  // Get the layout of the text section.
-  text_builder_.section_.sh_offset = NextOffset(text_builder_.section_, rodata_builder_.section_);
-  text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
-  text_builder_.section_.sh_size = text_builder_.size_;
-  text_builder_.section_.sh_link = text_builder_.GetLink();
-  CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
-
-  // Get the layout of the dynamic section.
-  dynamic_builder_.section_.sh_offset = NextOffset(dynamic_builder_.section_,
-                                                   text_builder_.section_);
-  dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
-  dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf32_Dyn);
-  dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
-
-  Elf32_Shdr prev = dynamic_builder_.section_;
-  if (IncludingDebugSymbols()) {
-    // Get the layout of the symtab section.
-    symtab_builder_.section_.sh_offset = NextOffset(symtab_builder_.section_,
-                                                    dynamic_builder_.section_);
-    symtab_builder_.section_.sh_addr = 0;
-    // Add to leave space for the null symbol.
-    symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf32_Sym);
-    symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
-
-    // Get the layout of the dynstr section.
-    symtab_builder_.strtab_.section_.sh_offset = NextOffset(symtab_builder_.strtab_.section_,
-                                                            symtab_builder_.section_);
-    symtab_builder_.strtab_.section_.sh_addr = 0;
-    symtab_builder_.strtab_.section_.sh_size = strtab.size();
-    symtab_builder_.strtab_.section_.sh_link = symtab_builder_.strtab_.GetLink();
-
-    prev = symtab_builder_.strtab_.section_;
-  }
-  if (debug_logging_) {
-    LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
-              << " dynsym size=" << dynsym_builder_.section_.sh_size;
-    LOG(INFO) << "dynstr off=" << dynsym_builder_.strtab_.section_.sh_offset
-              << " dynstr size=" << dynsym_builder_.strtab_.section_.sh_size;
-    LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
-              << " hash size=" << hash_builder_.section_.sh_size;
-    LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
-              << " rodata size=" << rodata_builder_.section_.sh_size;
-    LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
-              << " text size=" << text_builder_.section_.sh_size;
-    LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
-              << " dynamic size=" << dynamic_builder_.section_.sh_size;
-    if (IncludingDebugSymbols()) {
-      LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
-                << " symtab size=" << symtab_builder_.section_.sh_size;
-      LOG(INFO) << "strtab off=" << symtab_builder_.strtab_.section_.sh_offset
-                << " strtab size=" << symtab_builder_.strtab_.section_.sh_size;
-    }
-  }
-  // Get the layout of the extra sections. (This will deal with the debug
-  // sections if they are there)
-  for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
-    it->section_.sh_offset = NextOffset(it->section_, prev);
-    it->section_.sh_addr = 0;
-    it->section_.sh_size = it->GetBuffer()->size();
-    it->section_.sh_link = it->GetLink();
-
-    // We postpone adding an ElfFilePiece to keep the order in "pieces."
-
-    prev = it->section_;
-    if (debug_logging_) {
-      LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
-                << " " << it->name_ << " size=" << it->section_.sh_size;
-    }
-  }
-  // Get the layout of the shstrtab section
-  shstrtab_builder_.section_.sh_offset = NextOffset(shstrtab_builder_.section_, prev);
-  shstrtab_builder_.section_.sh_addr = 0;
-  shstrtab_builder_.section_.sh_size = shstrtab.size();
-  shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
-  if (debug_logging_) {
-      LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
-                << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
-  }
-
-  // The section list comes after come after.
-  Elf32_Word sections_offset = RoundUp(
-      shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
-      sizeof(Elf32_Word));
-
-  // Setup the actual symbol arrays.
-  std::vector<Elf32_Sym> dynsym = dynsym_builder_.GenerateSymtab();
-  CHECK_EQ(dynsym.size() * sizeof(Elf32_Sym), dynsym_builder_.section_.sh_size);
-  std::vector<Elf32_Sym> symtab;
-  if (IncludingDebugSymbols()) {
-    symtab = symtab_builder_.GenerateSymtab();
-    CHECK_EQ(symtab.size() * sizeof(Elf32_Sym), symtab_builder_.section_.sh_size);
-  }
-
-  // Setup the dynamic section.
-  // This will add the 2 values we cannot know until now time, namely the size
-  // and the soname_offset.
-  std::vector<Elf32_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr.size(),
-                                                                dynstr_soname_offset);
-  CHECK_EQ(dynamic.size() * sizeof(Elf32_Dyn), dynamic_builder_.section_.sh_size);
-
-  // Finish setup of the program headers now that we know the layout of the
-  // whole file.
-  Elf32_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
-  program_headers[PH_LOAD_R__].p_filesz = load_r_size;
-  program_headers[PH_LOAD_R__].p_memsz =  load_r_size;
-  program_headers[PH_LOAD_R__].p_align =  rodata_builder_.section_.sh_addralign;
-
-  Elf32_Word load_rx_size = text_builder_.section_.sh_size;
-  program_headers[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
-  program_headers[PH_LOAD_R_X].p_vaddr  = text_builder_.section_.sh_offset;
-  program_headers[PH_LOAD_R_X].p_paddr  = text_builder_.section_.sh_offset;
-  program_headers[PH_LOAD_R_X].p_filesz = load_rx_size;
-  program_headers[PH_LOAD_R_X].p_memsz  = load_rx_size;
-  program_headers[PH_LOAD_R_X].p_align  = text_builder_.section_.sh_addralign;
-
-  program_headers[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
-  program_headers[PH_LOAD_RW_].p_vaddr  = dynamic_builder_.section_.sh_offset;
-  program_headers[PH_LOAD_RW_].p_paddr  = dynamic_builder_.section_.sh_offset;
-  program_headers[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
-  program_headers[PH_LOAD_RW_].p_memsz  = dynamic_builder_.section_.sh_size;
-  program_headers[PH_LOAD_RW_].p_align  = dynamic_builder_.section_.sh_addralign;
-
-  program_headers[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
-  program_headers[PH_DYNAMIC].p_vaddr  = dynamic_builder_.section_.sh_offset;
-  program_headers[PH_DYNAMIC].p_paddr  = dynamic_builder_.section_.sh_offset;
-  program_headers[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
-  program_headers[PH_DYNAMIC].p_memsz  = dynamic_builder_.section_.sh_size;
-  program_headers[PH_DYNAMIC].p_align  = dynamic_builder_.section_.sh_addralign;
-
-  // Finish setup of the Ehdr values.
-  elf_header_.e_phoff = phdr_offset;
-  elf_header_.e_shoff = sections_offset;
-  elf_header_.e_phnum = PH_NUM;
-  elf_header_.e_shnum = section_ptrs.size();
-  elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
-
-  // Add the rest of the pieces to the list.
-  pieces.push_back(new ElfFileMemoryPiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
-  pieces.push_back(new ElfFileMemoryPiece("Program headers", phdr_offset,
-                                          &program_headers, sizeof(program_headers)));
-  pieces.push_back(new ElfFileMemoryPiece(".dynamic", dynamic_builder_.section_.sh_offset,
-                                          dynamic.data(), dynamic_builder_.section_.sh_size));
-  pieces.push_back(new ElfFileMemoryPiece(".dynsym", dynsym_builder_.section_.sh_offset,
-                                          dynsym.data(), dynsym.size() * sizeof(Elf32_Sym)));
-  pieces.push_back(new ElfFileMemoryPiece(".dynstr", dynsym_builder_.strtab_.section_.sh_offset,
-                                          dynstr.c_str(), dynstr.size()));
-  pieces.push_back(new ElfFileMemoryPiece(".hash", hash_builder_.section_.sh_offset,
-                                          hash.data(), hash.size() * sizeof(Elf32_Word)));
-  pieces.push_back(new ElfFileRodataPiece(rodata_builder_.section_.sh_offset, oat_writer_));
-  pieces.push_back(new ElfFileOatTextPiece(text_builder_.section_.sh_offset, oat_writer_));
-  if (IncludingDebugSymbols()) {
-    pieces.push_back(new ElfFileMemoryPiece(".symtab", symtab_builder_.section_.sh_offset,
-                                            symtab.data(), symtab.size() * sizeof(Elf32_Sym)));
-    pieces.push_back(new ElfFileMemoryPiece(".strtab", symtab_builder_.strtab_.section_.sh_offset,
-                                            strtab.c_str(), strtab.size()));
-  }
-  pieces.push_back(new ElfFileMemoryPiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
-                                          &shstrtab[0], shstrtab.size()));
-  for (uint32_t i = 0; i < section_ptrs.size(); ++i) {
-    // Just add all the sections in individually since they are all over the
-    // place on the heap/stack.
-    Elf32_Word cur_off = sections_offset + i * sizeof(Elf32_Shdr);
-    pieces.push_back(new ElfFileMemoryPiece("section table piece", cur_off,
-                                            section_ptrs[i], sizeof(Elf32_Shdr)));
-  }
-
-  // Postponed debug info.
-  for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
-    pieces.push_back(new ElfFileMemoryPiece(it->name_, it->section_.sh_offset,
-                                            it->GetBuffer()->data(), it->GetBuffer()->size()));
-  }
-
-  if (!WriteOutFile(pieces, elf_file_)) {
-    LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
-
-    STLDeleteElements(&pieces);  // Have to manually clean pieces.
-    return false;
-  }
-
-  STLDeleteElements(&pieces);  // Have to manually clean pieces.
-  return true;
+static void UpdateWord(std::vector<uint8_t>* buf, int offset, int data) {
+  (*buf)[offset+0] = data;
+  (*buf)[offset+1] = data >> 8;
+  (*buf)[offset+2] = data >> 16;
+  (*buf)[offset+3] = data >> 24;
 }
 
-void ElfWriterQuick::ElfBuilder::SetupDynamic() {
-  dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
-  dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, &dynsym_builder_.strtab_);
-  dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
-  dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf32_Sym));
+static void PushHalf(std::vector<uint8_t>* buf, int data) {
+  buf->push_back(data & 0xff);
+  buf->push_back((data >> 8) & 0xff);
 }
 
-void ElfWriterQuick::ElfBuilder::SetupRequiredSymbols() {
-  dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
-                            rodata_builder_.size_, STB_GLOBAL, STT_OBJECT);
-  dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
-                            text_builder_.size_, STB_GLOBAL, STT_OBJECT);
-  dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.size_ - 4,
-                            true, 4, STB_GLOBAL, STT_OBJECT);
-}
-
-void ElfWriterQuick::ElfDynamicBuilder::AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un) {
-  if (tag == DT_NULL) {
-    return;
-  }
-  dynamics_.push_back({nullptr, tag, d_un});
-}
-
-void ElfWriterQuick::ElfDynamicBuilder::AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un,
-                                                      ElfSectionBuilder* section) {
-  if (tag == DT_NULL) {
-    return;
-  }
-  dynamics_.push_back({section, tag, d_un});
-}
-
-std::vector<Elf32_Dyn> ElfWriterQuick::ElfDynamicBuilder::GetDynamics(Elf32_Word strsz,
-                                                                      Elf32_Word soname) {
-  std::vector<Elf32_Dyn> ret;
-  for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
-    if (it->section_) {
-      // We are adding an address relative to a section.
-      ret.push_back(
-          {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
-    } else {
-      ret.push_back({it->tag_, {it->off_}});
-    }
-  }
-  ret.push_back({DT_STRSZ, {strsz}});
-  ret.push_back({DT_SONAME, {soname}});
-  ret.push_back({DT_NULL, {0}});
-  return ret;
-}
-
-std::vector<Elf32_Sym> ElfWriterQuick::ElfSymtabBuilder::GenerateSymtab() {
-  std::vector<Elf32_Sym> ret;
-  Elf32_Sym undef_sym;
-  memset(&undef_sym, 0, sizeof(undef_sym));
-  undef_sym.st_shndx = SHN_UNDEF;
-  ret.push_back(undef_sym);
-
-  for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
-    Elf32_Sym sym;
-    memset(&sym, 0, sizeof(sym));
-    sym.st_name = it->name_idx_;
-    if (it->is_relative_) {
-      sym.st_value = it->addr_ + it->section_->section_.sh_offset;
-    } else {
-      sym.st_value = it->addr_;
-    }
-    sym.st_size = it->size_;
-    sym.st_other = it->other_;
-    sym.st_shndx = it->section_->section_index_;
-    sym.st_info = it->info_;
-
-    ret.push_back(sym);
-  }
-  return ret;
-}
-
-std::string ElfWriterQuick::ElfSymtabBuilder::GenerateStrtab() {
-  std::string tab;
-  tab += '\0';
-  for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
-    it->name_idx_ = tab.size();
-    tab += it->name_;
-    tab += '\0';
-  }
-  strtab_.section_.sh_size = tab.size();
-  return tab;
-}
-
-void ElfWriterQuick::ElfBuilder::AssignSectionStr(
-    ElfSectionBuilder* builder, std::string* strtab) {
-  builder->section_.sh_name = strtab->size();
-  *strtab += builder->name_;
-  *strtab += '\0';
-  if (debug_logging_) {
-    LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
-              << "to shstrtab at offset " << builder->section_.sh_name;
-  }
-}
-
-// from bionic
-static unsigned elfhash(const char *_name) {
-  const unsigned char *name = (const unsigned char *) _name;
-  unsigned h = 0, g;
-
-  while (*name) {
-    h = (h << 4) + *name++;
-    g = h & 0xf0000000;
-    h ^= g;
-    h ^= g >> 24;
-  }
-  return h;
-}
-
-
-std::vector<Elf32_Word> ElfWriterQuick::ElfSymtabBuilder::GenerateHashContents() {
-  // Here is how The ELF hash table works.
-  // There are 3 arrays to worry about.
-  // * The symbol table where the symbol information is.
-  // * The bucket array which is an array of indexes into the symtab and chain.
-  // * The chain array which is also an array of indexes into the symtab and chain.
-  //
-  // Lets say the state is something like this.
-  // +--------+       +--------+      +-----------+
-  // | symtab |       | bucket |      |   chain   |
-  // |  nullptr  |       | 1      |      | STN_UNDEF |
-  // | <sym1> |       | 4      |      | 2         |
-  // | <sym2> |       |        |      | 5         |
-  // | <sym3> |       |        |      | STN_UNDEF |
-  // | <sym4> |       |        |      | 3         |
-  // | <sym5> |       |        |      | STN_UNDEF |
-  // +--------+       +--------+      +-----------+
-  //
-  // The lookup process (in python psudocode) is
-  //
-  // def GetSym(name):
-  //     # NB STN_UNDEF == 0
-  //     indx = bucket[elfhash(name) % num_buckets]
-  //     while indx != STN_UNDEF:
-  //         if GetSymbolName(symtab[indx]) == name:
-  //             return symtab[indx]
-  //         indx = chain[indx]
-  //     return SYMBOL_NOT_FOUND
-  //
-  // Between bucket and chain arrays every symtab index must be present exactly
-  // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
-
-  // Select number of buckets.
-  // This is essentially arbitrary.
-  Elf32_Word nbuckets;
-  Elf32_Word chain_size = GetSize();
-  if (symbols_.size() < 8) {
-    nbuckets = 2;
-  } else if (symbols_.size() < 32) {
-    nbuckets = 4;
-  } else if (symbols_.size() < 256) {
-    nbuckets = 16;
-  } else {
-    // Have about 32 ids per bucket.
-    nbuckets = RoundUp(symbols_.size()/32, 2);
-  }
-  std::vector<Elf32_Word> hash;
-  hash.push_back(nbuckets);
-  hash.push_back(chain_size);
-  uint32_t bucket_offset = hash.size();
-  uint32_t chain_offset = bucket_offset + nbuckets;
-  hash.resize(hash.size() + nbuckets + chain_size, 0);
-
-  Elf32_Word* buckets = hash.data() + bucket_offset;
-  Elf32_Word* chain   = hash.data() + chain_offset;
-
-  // Set up the actual hash table.
-  for (Elf32_Word i = 0; i < symbols_.size(); i++) {
-    // Add 1 since we need to have the null symbol that is not in the symbols
-    // list.
-    Elf32_Word index = i + 1;
-    Elf32_Word hash_val = static_cast<Elf32_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
-    if (buckets[hash_val] == 0) {
-      buckets[hash_val] = index;
-    } else {
-      hash_val = buckets[hash_val];
-      CHECK_LT(hash_val, chain_size);
-      while (chain[hash_val] != 0) {
-        hash_val = chain[hash_val];
-        CHECK_LT(hash_val, chain_size);
-      }
-      chain[hash_val] = index;
-      // Check for loops. Works because if this is non-empty then there must be
-      // another cell which already contains the same symbol index as this one,
-      // which means some symbol has more then one name, which isn't allowed.
-      CHECK_EQ(chain[index], static_cast<Elf32_Word>(0));
-    }
-  }
-
-  return hash;
-}
-
-void ElfWriterQuick::ElfBuilder::SetupEhdr() {
-  memset(&elf_header_, 0, sizeof(elf_header_));
-  elf_header_.e_ident[EI_MAG0]       = ELFMAG0;
-  elf_header_.e_ident[EI_MAG1]       = ELFMAG1;
-  elf_header_.e_ident[EI_MAG2]       = ELFMAG2;
-  elf_header_.e_ident[EI_MAG3]       = ELFMAG3;
-  elf_header_.e_ident[EI_CLASS]      = ELFCLASS32;
-  elf_header_.e_ident[EI_DATA]       = ELFDATA2LSB;
-  elf_header_.e_ident[EI_VERSION]    = EV_CURRENT;
-  elf_header_.e_ident[EI_OSABI]      = ELFOSABI_LINUX;
-  elf_header_.e_ident[EI_ABIVERSION] = 0;
-  elf_header_.e_type = ET_DYN;
-  elf_header_.e_version = 1;
-  elf_header_.e_entry = 0;
-  elf_header_.e_ehsize = sizeof(Elf32_Ehdr);
-  elf_header_.e_phentsize = sizeof(Elf32_Phdr);
-  elf_header_.e_shentsize = sizeof(Elf32_Shdr);
-  elf_header_.e_phoff = sizeof(Elf32_Ehdr);
-}
-
-void ElfWriterQuick::ElfBuilder::SetISA(InstructionSet isa) {
-  switch (isa) {
-    case kArm:
-      // Fall through.
-    case kThumb2: {
-      elf_header_.e_machine = EM_ARM;
-      elf_header_.e_flags = EF_ARM_EABI_VER5;
-      break;
-    }
-    case kArm64: {
-      elf_header_.e_machine = EM_AARCH64;
-      elf_header_.e_flags = 0;
-      break;
-    }
-    case kX86: {
-      elf_header_.e_machine = EM_386;
-      elf_header_.e_flags = 0;
-      break;
-    }
-    case kX86_64: {
-      elf_header_.e_machine = EM_X86_64;
-      elf_header_.e_flags = 0;
-      break;
-    }
-    case kMips: {
-      elf_header_.e_machine = EM_MIPS;
-      elf_header_.e_flags = (EF_MIPS_NOREORDER |
-                             EF_MIPS_PIC       |
-                             EF_MIPS_CPIC      |
-                             EF_MIPS_ABI_O32   |
-                             EF_MIPS_ARCH_32R2);
-      break;
-    }
-    default: {
-      fatal_error_ = true;
-      LOG(FATAL) << "Unknown instruction set: " << isa;
-      break;
-    }
-  }
-}
-
-void ElfWriterQuick::ElfSymtabBuilder::AddSymbol(
-    const std::string& name, const ElfSectionBuilder* section, Elf32_Addr addr,
-    bool is_relative, Elf32_Word size, uint8_t binding, uint8_t type, uint8_t other) {
-  CHECK(section);
-  ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
-                                          MakeStInfo(binding, type), other, 0};
-  symbols_.push_back(state);
-}
-
-bool ElfWriterQuick::Create(File* elf_file,
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
+bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::Create(File* elf_file,
                             OatWriter* oat_writer,
                             const std::vector<const DexFile*>& dex_files,
                             const std::string& android_root,
@@ -888,150 +85,428 @@
   return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
 }
 
-// Add patch information to this section. Each patch is a Elf32_Word that
-// identifies an offset from the start of the text section
-void ElfWriterQuick::ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug) {
-  size_t size =
-      compiler_driver_->GetCodeToPatch().size() +
-      compiler_driver_->GetMethodsToPatch().size() +
-      compiler_driver_->GetClassesToPatch().size();
-  if (size == 0) {
-    if (debug) {
-      LOG(INFO) << "No patches to record";
-    }
-    return;
+std::vector<uint8_t>* ConstructCIEFrameX86(bool is_x86_64) {
+  std::vector<uint8_t>* cfi_info = new std::vector<uint8_t>;
+
+  // Length (will be filled in later in this routine).
+  if (is_x86_64) {
+    PushWord(cfi_info, 0xffffffff);  // Indicates 64bit
+    PushWord(cfi_info, 0);
+    PushWord(cfi_info, 0);
+  } else {
+    PushWord(cfi_info, 0);
   }
-  buffer->resize(size * sizeof(uintptr_t));
-  if (debug) {
-    LOG(INFO) << "Patches reserved for " << size;
+
+  // CIE id: always 0.
+  if (is_x86_64) {
+    PushWord(cfi_info, 0);
+    PushWord(cfi_info, 0);
+  } else {
+    PushWord(cfi_info, 0);
+  }
+
+  // Version: always 1.
+  cfi_info->push_back(0x01);
+
+  // Augmentation: 'zR\0'
+  cfi_info->push_back(0x7a);
+  cfi_info->push_back(0x52);
+  cfi_info->push_back(0x0);
+
+  // Code alignment: 1.
+  EncodeUnsignedLeb128(1, cfi_info);
+
+  // Data alignment.
+  if (is_x86_64) {
+    EncodeSignedLeb128(-8, cfi_info);
+  } else {
+    EncodeSignedLeb128(-4, cfi_info);
+  }
+
+  // Return address register.
+  if (is_x86_64) {
+    // R16(RIP)
+    cfi_info->push_back(0x10);
+  } else {
+    // R8(EIP)
+    cfi_info->push_back(0x08);
+  }
+
+  // Augmentation length: 1.
+  cfi_info->push_back(1);
+
+  // Augmentation data.
+  if (is_x86_64) {
+    // 0x04 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8).
+    cfi_info->push_back(0x04);
+  } else {
+    // 0x03 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
+    cfi_info->push_back(0x03);
+  }
+
+  // Initial instructions.
+  if (is_x86_64) {
+    // DW_CFA_def_cfa R7(RSP) 8.
+    cfi_info->push_back(0x0c);
+    cfi_info->push_back(0x07);
+    cfi_info->push_back(0x08);
+
+    // DW_CFA_offset R16(RIP) 1 (* -8).
+    cfi_info->push_back(0x90);
+    cfi_info->push_back(0x01);
+  } else {
+    // DW_CFA_def_cfa R4(ESP) 4.
+    cfi_info->push_back(0x0c);
+    cfi_info->push_back(0x04);
+    cfi_info->push_back(0x04);
+
+    // DW_CFA_offset R8(EIP) 1 (* -4).
+    cfi_info->push_back(0x88);
+    cfi_info->push_back(0x01);
+  }
+
+  // Padding to a multiple of 4
+  while ((cfi_info->size() & 3) != 0) {
+    // DW_CFA_nop is encoded as 0.
+    cfi_info->push_back(0);
+  }
+
+  // Set the length of the CIE inside the generated bytes.
+  if (is_x86_64) {
+    uint32_t length = cfi_info->size() - 12;
+    UpdateWord(cfi_info, 4, length);
+  } else {
+    uint32_t length = cfi_info->size() - 4;
+    UpdateWord(cfi_info, 0, length);
+  }
+  return cfi_info;
+}
+
+std::vector<uint8_t>* ConstructCIEFrame(InstructionSet isa) {
+  switch (isa) {
+    case kX86:
+      return ConstructCIEFrameX86(false);
+    case kX86_64:
+      return ConstructCIEFrameX86(true);
+
+    default:
+      // Not implemented.
+      return nullptr;
   }
 }
 
-bool ElfWriterQuick::Write(OatWriter* oat_writer,
+class OatWriterWrapper : public CodeOutput {
+ public:
+  explicit OatWriterWrapper(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
+
+  void SetCodeOffset(size_t offset) {
+    oat_writer_->SetOatDataOffset(offset);
+  }
+  bool Write(OutputStream* out) OVERRIDE {
+    return oat_writer_->Write(out);
+  }
+ private:
+  OatWriter* oat_writer_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+                              ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                                         Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+                              OatWriter* oat_writer);
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
+bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::Write(OatWriter* oat_writer,
                            const std::vector<const DexFile*>& dex_files_unused,
                            const std::string& android_root_unused,
                            bool is_host_unused) {
-  const bool debug = false;
-  const bool add_symbols = oat_writer->DidAddSymbols();
+  constexpr bool debug = false;
   const OatHeader& oat_header = oat_writer->GetOatHeader();
-  Elf32_Word oat_data_size = oat_header.GetExecutableOffset();
+  Elf_Word oat_data_size = oat_header.GetExecutableOffset();
   uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
 
-  ElfBuilder builder(oat_writer, elf_file_, compiler_driver_->GetInstructionSet(), 0,
-                     oat_data_size, oat_data_size, oat_exec_size, add_symbols, debug);
+  OatWriterWrapper wrapper(oat_writer);
 
-  if (add_symbols) {
-    AddDebugSymbols(builder, oat_writer, debug);
+  std::unique_ptr<ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                             Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> > builder(
+      new ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                     Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>(
+          &wrapper,
+          elf_file_,
+          compiler_driver_->GetInstructionSet(),
+          0,
+          oat_data_size,
+          oat_data_size,
+          oat_exec_size,
+          compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
+          debug));
+
+  if (!builder->Init()) {
+    return false;
   }
 
-  bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr;
-  if (generateDebugInformation) {
-    ElfRawSectionBuilder debug_info(".debug_info",   SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    ElfRawSectionBuilder debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    ElfRawSectionBuilder debug_str(".debug_str",    SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    ElfRawSectionBuilder eh_frame(".eh_frame",  SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
-    eh_frame.SetBuffer(*compiler_driver_->GetCallFrameInformation());
-
-    FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
-                         debug_abbrev.GetBuffer(), debug_str.GetBuffer());
-    builder.RegisterRawSection(debug_info);
-    builder.RegisterRawSection(debug_abbrev);
-    builder.RegisterRawSection(eh_frame);
-    builder.RegisterRawSection(debug_str);
+  if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+    WriteDebugSymbols(compiler_driver_, builder.get(), oat_writer);
   }
 
   if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
-    ElfRawSectionBuilder oat_patches(".oat_patches", SHT_OAT_PATCH, 0, NULL, 0,
-                                     sizeof(size_t), sizeof(size_t));
-    ReservePatchSpace(oat_patches.GetBuffer(), debug);
-    builder.RegisterRawSection(oat_patches);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> oat_patches(
+        ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, sizeof(uintptr_t), sizeof(uintptr_t));
+    const std::vector<uintptr_t>& locations = oat_writer->GetAbsolutePatchLocations();
+    const uint8_t* begin = reinterpret_cast<const uint8_t*>(&locations[0]);
+    const uint8_t* end = begin + locations.size() * sizeof(locations[0]);
+    oat_patches.GetBuffer()->assign(begin, end);
+    if (debug) {
+      LOG(INFO) << "Prepared .oat_patches for " << locations.size() << " patches.";
+    }
+    builder->RegisterRawSection(oat_patches);
   }
 
-  return builder.Write();
+  return builder->Write();
 }
 
-void ElfWriterQuick::AddDebugSymbols(ElfBuilder& builder, OatWriter* oat_writer, bool debug) {
+class LineTableGenerator FINAL : public Leb128Encoder {
+ public:
+  LineTableGenerator(int line_base, int line_range, int opcode_base,
+                     std::vector<uint8_t>* data, uintptr_t current_address,
+                     size_t current_line)
+    : Leb128Encoder(data), line_base_(line_base), line_range_(line_range),
+      opcode_base_(opcode_base), current_address_(current_address),
+      current_line_(current_line), current_file_index_(0) {}
+
+  void PutDelta(unsigned delta_addr, int delta_line) {
+    current_line_ += delta_line;
+    current_address_ += delta_addr;
+
+    if (delta_line >= line_base_ && delta_line < line_base_ + line_range_) {
+      unsigned special_opcode = (delta_line - line_base_) +
+                                (line_range_ * delta_addr) + opcode_base_;
+      if (special_opcode <= 255) {
+        PushByte(data_, special_opcode);
+        return;
+      }
+    }
+
+    // generate standart opcode for address advance
+    if (delta_addr != 0) {
+      PushByte(data_, DW_LNS_advance_pc);
+      PushBackUnsigned(delta_addr);
+    }
+
+    // generate standart opcode for line delta
+    if (delta_line != 0) {
+      PushByte(data_, DW_LNS_advance_line);
+      PushBackSigned(delta_line);
+    }
+
+    // generate standart opcode for new LTN entry
+    PushByte(data_, DW_LNS_copy);
+  }
+
+  void SetAddr(uintptr_t addr) {
+    if (current_address_ == addr) {
+      return;
+    }
+
+    current_address_ = addr;
+
+    PushByte(data_, 0);  // extended opcode:
+    PushByte(data_, 1 + 4);  // length: opcode_size + address_size
+    PushByte(data_, DW_LNE_set_address);
+    PushWord(data_, addr);
+  }
+
+  void SetLine(unsigned line) {
+    int delta_line = line - current_line_;
+    if (delta_line) {
+      current_line_ = line;
+      PushByte(data_, DW_LNS_advance_line);
+      PushBackSigned(delta_line);
+    }
+  }
+
+  void SetFile(unsigned file_index) {
+    if (current_file_index_ != file_index) {
+      current_file_index_ = file_index;
+      PushByte(data_, DW_LNS_set_file);
+      PushBackUnsigned(file_index);
+    }
+  }
+
+  void EndSequence() {
+    // End of Line Table Program
+    // 0(=ext), 1(len), DW_LNE_end_sequence
+    PushByte(data_, 0);
+    PushByte(data_, 1);
+    PushByte(data_, DW_LNE_end_sequence);
+  }
+
+ private:
+  const int line_base_;
+  const int line_range_;
+  const int opcode_base_;
+  uintptr_t current_address_;
+  size_t current_line_;
+  unsigned current_file_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(LineTableGenerator);
+};
+
+// TODO: rewriting it using DexFile::DecodeDebugInfo needs unneeded stuff.
+static void GetLineInfoForJava(const uint8_t* dbgstream, const SrcMap& pc2dex,
+                               SrcMap* result, uint32_t start_pc = 0) {
+  if (dbgstream == nullptr) {
+    return;
+  }
+
+  int adjopcode;
+  uint32_t dex_offset = 0;
+  uint32_t java_line = DecodeUnsignedLeb128(&dbgstream);
+
+  // skip parameters
+  for (uint32_t param_count = DecodeUnsignedLeb128(&dbgstream); param_count != 0; --param_count) {
+    DecodeUnsignedLeb128(&dbgstream);
+  }
+
+  for (bool is_end = false; is_end == false; ) {
+    uint8_t opcode = *dbgstream;
+    dbgstream++;
+    switch (opcode) {
+    case DexFile::DBG_END_SEQUENCE:
+      is_end = true;
+      break;
+
+    case DexFile::DBG_ADVANCE_PC:
+      dex_offset += DecodeUnsignedLeb128(&dbgstream);
+      break;
+
+    case DexFile::DBG_ADVANCE_LINE:
+      java_line += DecodeSignedLeb128(&dbgstream);
+      break;
+
+    case DexFile::DBG_START_LOCAL:
+    case DexFile::DBG_START_LOCAL_EXTENDED:
+      DecodeUnsignedLeb128(&dbgstream);
+      DecodeUnsignedLeb128(&dbgstream);
+      DecodeUnsignedLeb128(&dbgstream);
+
+      if (opcode == DexFile::DBG_START_LOCAL_EXTENDED) {
+        DecodeUnsignedLeb128(&dbgstream);
+      }
+      break;
+
+    case DexFile::DBG_END_LOCAL:
+    case DexFile::DBG_RESTART_LOCAL:
+      DecodeUnsignedLeb128(&dbgstream);
+      break;
+
+    case DexFile::DBG_SET_PROLOGUE_END:
+    case DexFile::DBG_SET_EPILOGUE_BEGIN:
+    case DexFile::DBG_SET_FILE:
+      break;
+
+    default:
+      adjopcode = opcode - DexFile::DBG_FIRST_SPECIAL;
+      dex_offset += adjopcode / DexFile::DBG_LINE_RANGE;
+      java_line += DexFile::DBG_LINE_BASE + (adjopcode % DexFile::DBG_LINE_RANGE);
+
+      for (SrcMap::const_iterator found = pc2dex.FindByTo(dex_offset);
+          found != pc2dex.end() && found->to_ == static_cast<int32_t>(dex_offset);
+          found++) {
+        result->push_back({found->from_ + start_pc, static_cast<int32_t>(java_line)});
+      }
+      break;
+    }
+  }
+}
+
+/*
+ * @brief Generate the DWARF debug_info and debug_abbrev sections
+ * @param oat_writer The Oat file Writer.
+ * @param dbg_info Compilation unit information.
+ * @param dbg_abbrev Abbreviations used to generate dbg_info.
+ * @param dbg_str Debug strings.
+ */
+static void FillInCFIInformation(OatWriter* oat_writer,
+                                 std::vector<uint8_t>* dbg_info,
+                                 std::vector<uint8_t>* dbg_abbrev,
+                                 std::vector<uint8_t>* dbg_str,
+                                 std::vector<uint8_t>* dbg_line,
+                                 uint32_t text_section_offset) {
   const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
-  ElfSymtabBuilder* symtab = &builder.symtab_builder_;
-  for (auto it = method_info.begin(); it != method_info.end(); ++it) {
-    symtab->AddSymbol(it->method_name_, &builder.text_builder_, it->low_pc_, true,
-                      it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
-  }
-}
 
-static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) {
-  (*buf)[offset+0] = data;
-  (*buf)[offset+1] = data >> 8;
-  (*buf)[offset+2] = data >> 16;
-  (*buf)[offset+3] = data >> 24;
-}
+  uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat");
 
-static void PushWord(std::vector<uint8_t>*buf, int data) {
-  buf->push_back(data & 0xff);
-  buf->push_back((data >> 8) & 0xff);
-  buf->push_back((data >> 16) & 0xff);
-  buf->push_back((data >> 24) & 0xff);
-}
-
-static void PushHalf(std::vector<uint8_t>*buf, int data) {
-  buf->push_back(data & 0xff);
-  buf->push_back((data >> 8) & 0xff);
-}
-
-void ElfWriterQuick::FillInCFIInformation(OatWriter* oat_writer,
-                                          std::vector<uint8_t>* dbg_info,
-                                          std::vector<uint8_t>* dbg_abbrev,
-                                          std::vector<uint8_t>* dbg_str) {
   // Create the debug_abbrev section with boilerplate information.
   // We only care about low_pc and high_pc right now for the compilation
   // unit and methods.
 
   // Tag 1: Compilation unit: DW_TAG_compile_unit.
-  dbg_abbrev->push_back(1);
-  dbg_abbrev->push_back(DW_TAG_compile_unit);
+  PushByte(dbg_abbrev, 1);
+  PushByte(dbg_abbrev, DW_TAG_compile_unit);
 
   // There are children (the methods).
-  dbg_abbrev->push_back(DW_CHILDREN_yes);
+  PushByte(dbg_abbrev, DW_CHILDREN_yes);
+
+  // DW_AT_producer DW_FORM_data1.
+  // REVIEW: we can get rid of dbg_str section if
+  // DW_FORM_string (immediate string) was used everywhere instead of
+  // DW_FORM_strp (ref to string from .debug_str section).
+  // DW_FORM_strp makes sense only if we reuse the strings.
+  PushByte(dbg_abbrev, DW_AT_producer);
+  PushByte(dbg_abbrev, DW_FORM_strp);
 
   // DW_LANG_Java DW_FORM_data1.
-  dbg_abbrev->push_back(DW_AT_language);
-  dbg_abbrev->push_back(DW_FORM_data1);
+  PushByte(dbg_abbrev, DW_AT_language);
+  PushByte(dbg_abbrev, DW_FORM_data1);
 
   // DW_AT_low_pc DW_FORM_addr.
-  dbg_abbrev->push_back(DW_AT_low_pc);
-  dbg_abbrev->push_back(DW_FORM_addr);
+  PushByte(dbg_abbrev, DW_AT_low_pc);
+  PushByte(dbg_abbrev, DW_FORM_addr);
 
   // DW_AT_high_pc DW_FORM_addr.
-  dbg_abbrev->push_back(DW_AT_high_pc);
-  dbg_abbrev->push_back(DW_FORM_addr);
+  PushByte(dbg_abbrev, DW_AT_high_pc);
+  PushByte(dbg_abbrev, DW_FORM_addr);
+
+  if (dbg_line != nullptr) {
+    // DW_AT_stmt_list DW_FORM_sec_offset.
+    PushByte(dbg_abbrev, DW_AT_stmt_list);
+    PushByte(dbg_abbrev, DW_FORM_sec_offset);
+  }
 
   // End of DW_TAG_compile_unit.
   PushHalf(dbg_abbrev, 0);
 
   // Tag 2: Compilation unit: DW_TAG_subprogram.
-  dbg_abbrev->push_back(2);
-  dbg_abbrev->push_back(DW_TAG_subprogram);
+  PushByte(dbg_abbrev, 2);
+  PushByte(dbg_abbrev, DW_TAG_subprogram);
 
   // There are no children.
-  dbg_abbrev->push_back(DW_CHILDREN_no);
+  PushByte(dbg_abbrev, DW_CHILDREN_no);
 
   // Name of the method.
-  dbg_abbrev->push_back(DW_AT_name);
-  dbg_abbrev->push_back(DW_FORM_strp);
+  PushByte(dbg_abbrev, DW_AT_name);
+  PushByte(dbg_abbrev, DW_FORM_strp);
 
   // DW_AT_low_pc DW_FORM_addr.
-  dbg_abbrev->push_back(DW_AT_low_pc);
-  dbg_abbrev->push_back(DW_FORM_addr);
+  PushByte(dbg_abbrev, DW_AT_low_pc);
+  PushByte(dbg_abbrev, DW_FORM_addr);
 
   // DW_AT_high_pc DW_FORM_addr.
-  dbg_abbrev->push_back(DW_AT_high_pc);
-  dbg_abbrev->push_back(DW_FORM_addr);
+  PushByte(dbg_abbrev, DW_AT_high_pc);
+  PushByte(dbg_abbrev, DW_FORM_addr);
 
   // End of DW_TAG_subprogram.
   PushHalf(dbg_abbrev, 0);
 
   // Start the debug_info section with the header information
   // 'unit_length' will be filled in later.
+  int cunit_length = dbg_info->size();
   PushWord(dbg_info, 0);
 
   // 'version' - 3.
@@ -1041,55 +516,284 @@
   PushWord(dbg_info, 0);
 
   // Address size: 4.
-  dbg_info->push_back(4);
+  PushByte(dbg_info, 4);
 
   // Start the description for the compilation unit.
   // This uses tag 1.
-  dbg_info->push_back(1);
+  PushByte(dbg_info, 1);
+
+  // The producer is Android dex2oat.
+  PushWord(dbg_info, producer_str_offset);
 
   // The language is Java.
-  dbg_info->push_back(DW_LANG_Java);
+  PushByte(dbg_info, DW_LANG_Java);
 
-  // Leave space for low_pc and high_pc.
-  int low_pc_offset = dbg_info->size();
+  // low_pc and high_pc.
+  uint32_t cunit_low_pc = 0 - 1;
+  uint32_t cunit_high_pc = 0;
+  int cunit_low_pc_pos = dbg_info->size();
   PushWord(dbg_info, 0);
   PushWord(dbg_info, 0);
 
-  // Walk through the information in the method table, and enter into dbg_info.
-  const std::vector<OatWriter::DebugInfo>& dbg = oat_writer->GetCFIMethodInfo();
-  uint32_t low_pc = 0xFFFFFFFFU;
-  uint32_t high_pc = 0;
+  if (dbg_line == nullptr) {
+    for (size_t i = 0; i < method_info.size(); ++i) {
+      const OatWriter::DebugInfo &dbg = method_info[i];
 
-  for (uint32_t i = 0; i < dbg.size(); i++) {
-    const OatWriter::DebugInfo& info = dbg[i];
-    if (info.low_pc_ < low_pc) {
-      low_pc = info.low_pc_;
+      cunit_low_pc = std::min(cunit_low_pc, dbg.low_pc_);
+      cunit_high_pc = std::max(cunit_high_pc, dbg.high_pc_);
+
+      // Start a new TAG: subroutine (2).
+      PushByte(dbg_info, 2);
+
+      // Enter name, low_pc, high_pc.
+      PushWord(dbg_info, PushStr(dbg_str, dbg.method_name_));
+      PushWord(dbg_info, dbg.low_pc_ + text_section_offset);
+      PushWord(dbg_info, dbg.high_pc_ + text_section_offset);
     }
-    if (info.high_pc_ > high_pc) {
-      high_pc = info.high_pc_;
+  } else {
+    // TODO: in gdb info functions <regexp> - reports Java functions, but
+    // source file is <unknown> because .debug_line is formed as one
+    // compilation unit. To fix this it is possible to generate
+    // a separate compilation unit for every distinct Java source.
+    // Each of the these compilation units can have several non-adjacent
+    // method ranges.
+
+    // Line number table offset
+    PushWord(dbg_info, dbg_line->size());
+
+    size_t lnt_length = dbg_line->size();
+    PushWord(dbg_line, 0);
+
+    PushHalf(dbg_line, 4);  // LNT Version DWARF v4 => 4
+
+    size_t lnt_hdr_length = dbg_line->size();
+    PushWord(dbg_line, 0);  // TODO: 64-bit uses 8-byte here
+
+    PushByte(dbg_line, 1);  // minimum_instruction_length (ubyte)
+    PushByte(dbg_line, 1);  // maximum_operations_per_instruction (ubyte) = always 1
+    PushByte(dbg_line, 1);  // default_is_stmt (ubyte)
+
+    const int8_t LINE_BASE = -5;
+    PushByte(dbg_line, LINE_BASE);  // line_base (sbyte)
+
+    const uint8_t LINE_RANGE = 14;
+    PushByte(dbg_line, LINE_RANGE);  // line_range (ubyte)
+
+    const uint8_t OPCODE_BASE = 13;
+    PushByte(dbg_line, OPCODE_BASE);  // opcode_base (ubyte)
+
+    // Standard_opcode_lengths (array of ubyte).
+    PushByte(dbg_line, 0); PushByte(dbg_line, 1); PushByte(dbg_line, 1);
+    PushByte(dbg_line, 1); PushByte(dbg_line, 1); PushByte(dbg_line, 0);
+    PushByte(dbg_line, 0); PushByte(dbg_line, 0); PushByte(dbg_line, 1);
+    PushByte(dbg_line, 0); PushByte(dbg_line, 0); PushByte(dbg_line, 1);
+
+    PushByte(dbg_line, 0);  // include_directories (sequence of path names) = EMPTY
+
+    // File_names (sequence of file entries).
+    std::unordered_map<const char*, size_t> files;
+    for (size_t i = 0; i < method_info.size(); ++i) {
+      const OatWriter::DebugInfo &dbg = method_info[i];
+      // TODO: add package directory to the file name
+      const char* file_name = dbg.src_file_name_ == nullptr ? "null" : dbg.src_file_name_;
+      auto found = files.find(file_name);
+      if (found == files.end()) {
+        size_t file_index = 1 + files.size();
+        files[file_name] = file_index;
+        PushStr(dbg_line, file_name);
+        PushByte(dbg_line, 0);  // include directory index = LEB128(0) - no directory
+        PushByte(dbg_line, 0);  // modification time = LEB128(0) - NA
+        PushByte(dbg_line, 0);  // file length = LEB128(0) - NA
+      }
+    }
+    PushByte(dbg_line, 0);  // End of file_names.
+
+    // Set lnt header length.
+    UpdateWord(dbg_line, lnt_hdr_length, dbg_line->size() - lnt_hdr_length - 4);
+
+    // Generate Line Number Program code, one long program for all methods.
+    LineTableGenerator line_table_generator(LINE_BASE, LINE_RANGE, OPCODE_BASE,
+                                            dbg_line, 0, 1);
+
+    SrcMap pc2java_map;
+    for (size_t i = 0; i < method_info.size(); ++i) {
+      const OatWriter::DebugInfo &dbg = method_info[i];
+      const char* file_name = (dbg.src_file_name_ == nullptr) ? "null" : dbg.src_file_name_;
+      size_t file_index = files[file_name];
+      DCHECK_NE(file_index, 0U) << file_name;
+
+      cunit_low_pc = std::min(cunit_low_pc, dbg.low_pc_);
+      cunit_high_pc = std::max(cunit_high_pc, dbg.high_pc_);
+
+      // Start a new TAG: subroutine (2).
+      PushByte(dbg_info, 2);
+
+      // Enter name, low_pc, high_pc.
+      PushWord(dbg_info, PushStr(dbg_str, dbg.method_name_));
+      PushWord(dbg_info, dbg.low_pc_ + text_section_offset);
+      PushWord(dbg_info, dbg.high_pc_ + text_section_offset);
+
+      GetLineInfoForJava(dbg.dbgstream_, dbg.compiled_method_->GetSrcMappingTable(),
+                         &pc2java_map, dbg.low_pc_);
+      pc2java_map.DeltaFormat({dbg.low_pc_, 1}, dbg.high_pc_);
+      if (!pc2java_map.empty()) {
+        line_table_generator.SetFile(file_index);
+        line_table_generator.SetAddr(dbg.low_pc_ + text_section_offset);
+        line_table_generator.SetLine(1);
+        for (auto& src_map_elem : pc2java_map) {
+          line_table_generator.PutDelta(src_map_elem.from_, src_map_elem.to_);
+        }
+        pc2java_map.clear();
+      }
     }
 
-    // Start a new TAG: subroutine (2).
-    dbg_info->push_back(2);
+    // End Sequence should have the highest address set.
+    line_table_generator.SetAddr(cunit_high_pc + text_section_offset);
+    line_table_generator.EndSequence();
 
-    // Enter the name into the string table (and NUL terminate).
-    uint32_t str_offset = dbg_str->size();
-    dbg_str->insert(dbg_str->end(), info.method_name_.begin(), info.method_name_.end());
-    dbg_str->push_back('\0');
-
-    // Enter name, low_pc, high_pc.
-    PushWord(dbg_info, str_offset);
-    PushWord(dbg_info, info.low_pc_);
-    PushWord(dbg_info, info.high_pc_);
+    // set lnt length
+    UpdateWord(dbg_line, lnt_length, dbg_line->size() - lnt_length - 4);
   }
 
   // One byte terminator
-  dbg_info->push_back(0);
+  PushByte(dbg_info, 0);
 
-  // We have now walked all the methods.  Fill in lengths and low/high PCs.
-  UpdateWord(dbg_info, 0, dbg_info->size() - 4);
-  UpdateWord(dbg_info, low_pc_offset, low_pc);
-  UpdateWord(dbg_info, low_pc_offset + 4, high_pc);
+  // Fill in cunit's low_pc and high_pc.
+  UpdateWord(dbg_info, cunit_low_pc_pos, cunit_low_pc + text_section_offset);
+  UpdateWord(dbg_info, cunit_low_pc_pos + 4, cunit_high_pc + text_section_offset);
+
+  // We have now walked all the methods.  Fill in lengths.
+  UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4);
 }
 
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+                              ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                                         Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+                              OatWriter* oat_writer) {
+  std::unique_ptr<std::vector<uint8_t>> cfi_info(
+      ConstructCIEFrame(compiler_driver->GetInstructionSet()));
+
+  Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
+
+  // Iterate over the compiled methods.
+  const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
+  ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr,
+                   Elf_Sym, Elf_Shdr>* symtab = &builder->symtab_builder_;
+  for (auto it = method_info.begin(); it != method_info.end(); ++it) {
+    symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
+                      it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
+
+    // Include CFI for compiled method, if possible.
+    if (cfi_info.get() != nullptr) {
+      DCHECK(it->compiled_method_ != nullptr);
+
+      // Copy in the FDE, if present
+      const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
+      if (fde != nullptr) {
+        // Copy the information into cfi_info and then fix the address in the new copy.
+        int cur_offset = cfi_info->size();
+        cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+        bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
+
+        // Set the 'CIE_pointer' field.
+        uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
+        uint64_t offset_to_update = CIE_pointer;
+        if (is_64bit) {
+          (*cfi_info)[offset_to_update+0] = CIE_pointer;
+          (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+          (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+          (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+          (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
+          (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
+          (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
+          (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
+        } else {
+          (*cfi_info)[offset_to_update+0] = CIE_pointer;
+          (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+          (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+          (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+        }
+
+        // Set the 'initial_location' field.
+        offset_to_update += is_64bit ? 8 : 4;
+        if (is_64bit) {
+          const uint64_t quick_code_start = it->low_pc_ + text_section_address;
+          (*cfi_info)[offset_to_update+0] = quick_code_start;
+          (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+          (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+          (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+          (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
+          (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
+          (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
+          (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
+        } else {
+          const uint32_t quick_code_start = it->low_pc_ + text_section_address;
+          (*cfi_info)[offset_to_update+0] = quick_code_start;
+          (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+          (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+          (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+        }
+      }
+    }
+  }
+
+  bool hasCFI = (cfi_info.get() != nullptr);
+  bool hasLineInfo = false;
+  for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
+    if (dbg_info.dbgstream_ != nullptr &&
+        !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
+      hasLineInfo = true;
+      break;
+    }
+  }
+
+  if (hasLineInfo || hasCFI) {
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_info(".debug_info",
+                                                                   SHT_PROGBITS,
+                                                                   0, nullptr, 0, 1, 0);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_abbrev(".debug_abbrev",
+                                                                     SHT_PROGBITS,
+                                                                     0, nullptr, 0, 1, 0);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_str(".debug_str",
+                                                                  SHT_PROGBITS,
+                                                                  0, nullptr, 0, 1, 0);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_line(".debug_line",
+                                                                   SHT_PROGBITS,
+                                                                   0, nullptr, 0, 1, 0);
+
+    FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
+                         debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
+                         hasLineInfo ? debug_line.GetBuffer() : nullptr,
+                         text_section_address);
+
+    builder->RegisterRawSection(debug_info);
+    builder->RegisterRawSection(debug_abbrev);
+
+    if (hasCFI) {
+      ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame(".eh_frame",
+                                                                   SHT_PROGBITS,
+                                                                   SHF_ALLOC,
+                                                                   nullptr, 0, 4, 0);
+      eh_frame.SetBuffer(std::move(*cfi_info.get()));
+      builder->RegisterRawSection(eh_frame);
+    }
+
+    if (hasLineInfo) {
+      builder->RegisterRawSection(debug_line);
+    }
+
+    builder->RegisterRawSection(debug_str);
+  }
+}
+
+// Explicit instantiations
+template class ElfWriterQuick<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+                              Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>;
+template class ElfWriterQuick<Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Dyn,
+                              Elf64_Sym, Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>;
+
 }  // namespace art
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 0c22ae2..4990ed0 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -19,10 +19,12 @@
 
 #include "elf_utils.h"
 #include "elf_writer.h"
-#include "instruction_set.h"
 
 namespace art {
 
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
 class ElfWriterQuick FINAL : public ElfWriter {
  public:
   // Write an ELF file. Returns true on success, false on failure.
@@ -47,267 +49,15 @@
     : ElfWriter(driver, elf_file) {}
   ~ElfWriterQuick() {}
 
-  class ElfBuilder;
-  void AddDebugSymbols(ElfBuilder& builder,
-                       OatWriter* oat_writer,
-                       bool debug);
-  void ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug);
-
-  class ElfSectionBuilder {
-   public:
-    ElfSectionBuilder(const std::string& sec_name, Elf32_Word type, Elf32_Word flags,
-                      const ElfSectionBuilder *link, Elf32_Word info, Elf32_Word align,
-                      Elf32_Word entsize)
-        : name_(sec_name), link_(link) {
-      memset(&section_, 0, sizeof(section_));
-      section_.sh_type = type;
-      section_.sh_flags = flags;
-      section_.sh_info = info;
-      section_.sh_addralign = align;
-      section_.sh_entsize = entsize;
-    }
-
-    virtual ~ElfSectionBuilder() {}
-
-    Elf32_Shdr section_;
-    Elf32_Word section_index_ = 0;
-
-   protected:
-    const std::string name_;
-    const ElfSectionBuilder* link_;
-
-    Elf32_Word GetLink() {
-      return (link_) ? link_->section_index_ : 0;
-    }
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfDynamicBuilder : public ElfSectionBuilder {
-   public:
-    void AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un);
-    void AddDynamicTag(Elf32_Sword tag, Elf32_Word offset, ElfSectionBuilder* section);
-
-    ElfDynamicBuilder(const std::string& sec_name, ElfSectionBuilder *link)
-        : ElfSectionBuilder(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC, link,
-                            0, kPageSize, sizeof(Elf32_Dyn)) {}
-    ~ElfDynamicBuilder() {}
-
-   protected:
-    struct ElfDynamicState {
-      ElfSectionBuilder* section_;
-      Elf32_Sword tag_;
-      Elf32_Word off_;
-    };
-    std::vector<ElfDynamicState> dynamics_;
-    Elf32_Word GetSize() {
-      // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
-      // these must be added when we actually put the file together because
-      // their values are very dependent on state.
-      return dynamics_.size() + 3;
-    }
-
-    // Create the actual dynamic vector. strsz should be the size of the .dynstr
-    // table and soname_off should be the offset of the soname in .dynstr.
-    // Since niether can be found prior to final layout we will wait until here
-    // to add them.
-    std::vector<Elf32_Dyn> GetDynamics(Elf32_Word strsz, Elf32_Word soname_off);
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfRawSectionBuilder : public ElfSectionBuilder {
-   public:
-    ElfRawSectionBuilder(const std::string& sec_name, Elf32_Word type, Elf32_Word flags,
-                         const ElfSectionBuilder* link, Elf32_Word info, Elf32_Word align,
-                         Elf32_Word entsize)
-        : ElfSectionBuilder(sec_name, type, flags, link, info, align, entsize) {}
-    ~ElfRawSectionBuilder() {}
-    std::vector<uint8_t>* GetBuffer() { return &buf_; }
-    void SetBuffer(std::vector<uint8_t> buf) { buf_ = buf; }
-
-   protected:
-    std::vector<uint8_t> buf_;
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfOatSectionBuilder : public ElfSectionBuilder {
-   public:
-    ElfOatSectionBuilder(const std::string& sec_name, Elf32_Word size, Elf32_Word offset,
-                         Elf32_Word type, Elf32_Word flags)
-        : ElfSectionBuilder(sec_name, type, flags, NULL, 0, kPageSize, 0),
-          offset_(offset), size_(size) {}
-    ~ElfOatSectionBuilder() {}
-
-   protected:
-    // Offset of the content within the file.
-    Elf32_Word offset_;
-    // Size of the content within the file.
-    Elf32_Word size_;
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfSymtabBuilder : public ElfSectionBuilder {
-   public:
-    // Add a symbol with given name to this symtab. The symbol refers to
-    // 'relative_addr' within the given section and has the given attributes.
-    void AddSymbol(const std::string& name,
-                   const ElfSectionBuilder* section,
-                   Elf32_Addr addr,
-                   bool is_relative,
-                   Elf32_Word size,
-                   uint8_t binding,
-                   uint8_t type,
-                   uint8_t other = 0);
-
-    ElfSymtabBuilder(const std::string& sec_name, Elf32_Word type,
-                     const std::string& str_name, Elf32_Word str_type, bool alloc)
-        : ElfSectionBuilder(sec_name, type, ((alloc) ? SHF_ALLOC : 0U), &strtab_, 0,
-                            sizeof(Elf32_Word), sizeof(Elf32_Sym)),
-          str_name_(str_name), str_type_(str_type),
-          strtab_(str_name, str_type, ((alloc) ? SHF_ALLOC : 0U), NULL, 0, 1, 1) {}
-    ~ElfSymtabBuilder() {}
-
-   protected:
-    std::vector<Elf32_Word> GenerateHashContents();
-    std::string GenerateStrtab();
-    std::vector<Elf32_Sym> GenerateSymtab();
-
-    Elf32_Word GetSize() {
-      // 1 is for the implicit NULL symbol.
-      return symbols_.size() + 1;
-    }
-
-    struct ElfSymbolState {
-      const std::string name_;
-      const ElfSectionBuilder* section_;
-      Elf32_Addr addr_;
-      Elf32_Word size_;
-      bool is_relative_;
-      uint8_t info_;
-      uint8_t other_;
-      // Used during Write() to temporarially hold name index in the strtab.
-      Elf32_Word name_idx_;
-    };
-
-    // Information for the strsym for dynstr sections.
-    const std::string str_name_;
-    Elf32_Word str_type_;
-    // The symbols in the same order they will be in the symbol table.
-    std::vector<ElfSymbolState> symbols_;
-    ElfSectionBuilder strtab_;
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfBuilder FINAL {
-   public:
-    ElfBuilder(OatWriter* oat_writer,
-               File* elf_file,
-               InstructionSet isa,
-               Elf32_Word rodata_relative_offset,
-               Elf32_Word rodata_size,
-               Elf32_Word text_relative_offset,
-               Elf32_Word text_size,
-               const bool add_symbols,
-               bool debug = false)
-        : oat_writer_(oat_writer),
-          elf_file_(elf_file),
-          add_symbols_(add_symbols),
-          debug_logging_(debug),
-          text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
-                        SHF_ALLOC | SHF_EXECINSTR),
-          rodata_builder_(".rodata", rodata_size, rodata_relative_offset,
-                          SHT_PROGBITS, SHF_ALLOC),
-          dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
-          symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
-          hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0,
-                        sizeof(Elf32_Word), sizeof(Elf32_Word)),
-          dynamic_builder_(".dynamic", &dynsym_builder_),
-          shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
-      SetupEhdr();
-      SetupDynamic();
-      SetupRequiredSymbols();
-      SetISA(isa);
-    }
-    ~ElfBuilder() {}
-
-    bool Write();
-
-    // Adds the given raw section to the builder. This will copy it. The caller
-    // is responsible for deallocating their copy.
-    void RegisterRawSection(ElfRawSectionBuilder bld) {
-      other_builders_.push_back(bld);
-    }
-
-   private:
-    OatWriter* oat_writer_;
-    File* elf_file_;
-    const bool add_symbols_;
-    const bool debug_logging_;
-
-    bool fatal_error_ = false;
-
-    Elf32_Ehdr elf_header_;
-
-   public:
-    ElfOatSectionBuilder text_builder_;
-    ElfOatSectionBuilder rodata_builder_;
-    ElfSymtabBuilder dynsym_builder_;
-    ElfSymtabBuilder symtab_builder_;
-    ElfSectionBuilder hash_builder_;
-    ElfDynamicBuilder dynamic_builder_;
-    ElfSectionBuilder shstrtab_builder_;
-    std::vector<ElfRawSectionBuilder> other_builders_;
-
-   private:
-    void SetISA(InstructionSet isa);
-    void SetupEhdr();
-
-    // Sets up a bunch of the required Dynamic Section entries.
-    // Namely it will initialize all the mandatory ones that it can.
-    // Specifically:
-    // DT_HASH
-    // DT_STRTAB
-    // DT_SYMTAB
-    // DT_SYMENT
-    //
-    // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
-    void SetupDynamic();
-
-    // Sets up the basic dynamic symbols that are needed, namely all those we
-    // can know already.
-    //
-    // Specifically adds:
-    // oatdata
-    // oatexec
-    // oatlastword
-    void SetupRequiredSymbols();
-    void AssignSectionStr(ElfSectionBuilder *builder, std::string* strtab);
-
-    bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
-  };
-
-  /*
-   * @brief Generate the DWARF debug_info and debug_abbrev sections
-   * @param oat_writer The Oat file Writer.
-   * @param dbg_info Compilation unit information.
-   * @param dbg_abbrev Abbreviations used to generate dbg_info.
-   * @param dbg_str Debug strings.
-   */
-  void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info,
-                            std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str);
-
   DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
 };
 
+// Explicitly instantiated in elf_writer_quick.cc
+typedef ElfWriterQuick<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+                       Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr> ElfWriterQuick32;
+typedef ElfWriterQuick<Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Dyn,
+                       Elf64_Sym, Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr> ElfWriterQuick64;
+
 }  // namespace art
 
 #endif  // ART_COMPILER_ELF_WRITER_QUICK_H_
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 3d119bb..2a37049 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "base/unix_file/fd_file.h"
+#include "class_linker.h"
 #include "common_compiler_test.h"
 #include "elf_fixup.h"
 #include "gc/space/image_space.h"
@@ -61,6 +62,8 @@
   oat_filename += "oat";
   ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
 
+  const uintptr_t requested_image_base = ART_BASE_ADDRESS;
+  ImageWriter writer(*compiler_driver_, requested_image_base);
   {
     {
       jobject class_loader = NULL;
@@ -78,15 +81,15 @@
       compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
 
       t.NewTiming("WriteElf");
-      ScopedObjectAccess soa(Thread::Current());
       SafeMap<std::string, std::string> key_value_store;
-      OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(), &timings,
-                           &key_value_store);
-      bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
-                                                !kIsTargetBuild,
-                                                class_linker->GetBootClassPath(),
-                                                &oat_writer,
-                                                oat_file.GetFile());
+      OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(),
+                           &writer, &timings, &key_value_store);
+      bool success = writer.PrepareImageAddressSpace() &&
+          compiler_driver_->WriteElf(GetTestAndroidRoot(),
+                                     !kIsTargetBuild,
+                                     class_linker->GetBootClassPath(),
+                                     &oat_writer,
+                                     oat_file.GetFile());
       ASSERT_TRUE(success);
     }
   }
@@ -94,11 +97,9 @@
   std::unique_ptr<File> dup_oat(OS::OpenFileReadWrite(oat_file.GetFilename().c_str()));
   ASSERT_TRUE(dup_oat.get() != NULL);
 
-  const uintptr_t requested_image_base = ART_BASE_ADDRESS;
   {
-    ImageWriter writer(*compiler_driver_.get());
-    bool success_image = writer.Write(image_file.GetFilename(), requested_image_base,
-                                      dup_oat->GetPath(), dup_oat->GetPath());
+    bool success_image =
+        writer.Write(image_file.GetFilename(), dup_oat->GetPath(), dup_oat->GetPath());
     ASSERT_TRUE(success_image);
     bool success_fixup = ElfFixup::Fixup(dup_oat.get(), writer.GetOatDataBegin());
     ASSERT_TRUE(success_fixup);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c6fc115..1c8b8d5 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -29,7 +29,6 @@
 #include "driver/compiler_driver.h"
 #include "elf_file.h"
 #include "elf_utils.h"
-#include "elf_patcher.h"
 #include "elf_writer.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/heap_bitmap.h"
@@ -68,15 +67,38 @@
 
 namespace art {
 
+bool ImageWriter::PrepareImageAddressSpace() {
+  {
+    Thread::Current()->TransitionFromSuspendedToRunnable();
+    PruneNonImageClasses();  // Remove junk
+    ComputeLazyFieldsForImageClasses();  // Add useful information
+    ComputeEagerResolvedStrings();
+    Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+  }
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  heap->CollectGarbage(false);  // Remove garbage.
+
+  if (!AllocMemory()) {
+    return false;
+  }
+
+  if (kIsDebugBuild) {
+    ScopedObjectAccess soa(Thread::Current());
+    CheckNonImageClassesRemoved();
+  }
+
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  CalculateNewObjectOffsets();
+  Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+  return true;
+}
+
 bool ImageWriter::Write(const std::string& image_filename,
-                        uintptr_t image_begin,
                         const std::string& oat_filename,
                         const std::string& oat_location) {
   CHECK(!image_filename.empty());
 
-  CHECK_NE(image_begin, 0U);
-  image_begin_ = reinterpret_cast<byte*>(image_begin);
-
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
   std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
@@ -115,35 +137,18 @@
       oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset();
   quick_to_interpreter_bridge_offset_ =
       oat_file_->GetOatHeader().GetQuickToInterpreterBridgeOffset();
-  {
-    Thread::Current()->TransitionFromSuspendedToRunnable();
-    PruneNonImageClasses();  // Remove junk
-    ComputeLazyFieldsForImageClasses();  // Add useful information
-    ComputeEagerResolvedStrings();
-    Thread::Current()->TransitionFromRunnableToSuspended(kNative);
-  }
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  heap->CollectGarbage(false);  // Remove garbage.
 
-  if (!AllocMemory()) {
-    return false;
-  }
-
-  if (kIsDebugBuild) {
-    ScopedObjectAccess soa(Thread::Current());
-    CheckNonImageClassesRemoved();
-  }
-
-  Thread::Current()->TransitionFromSuspendedToRunnable();
   size_t oat_loaded_size = 0;
   size_t oat_data_offset = 0;
   ElfWriter::GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset);
-  CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset);
-  CopyAndFixupObjects();
 
-  PatchOatCodeAndMethods(oat_file.get());
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  CreateHeader(oat_loaded_size, oat_data_offset);
+  CopyAndFixupObjects();
   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
 
+  SetOatChecksumFromElfFile(oat_file.get());
+
   std::unique_ptr<File> image_file(OS::CreateEmptyFile(image_filename.c_str()));
   ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
   if (image_file.get() == NULL) {
@@ -232,7 +237,7 @@
   size_t length = RoundUp(Runtime::Current()->GetHeap()->GetTotalMemory(), kPageSize);
   std::string error_msg;
   image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, PROT_READ | PROT_WRITE,
-                                    true, &error_msg));
+                                    false, &error_msg));
   if (UNLIKELY(image_.get() == nullptr)) {
     LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
     return false;
@@ -527,8 +532,7 @@
   writer->WalkFieldsInOrder(obj);
 }
 
-void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) {
-  CHECK_NE(0U, oat_loaded_size);
+void ImageWriter::CalculateNewObjectOffsets() {
   Thread* self = Thread::Current();
   StackHandleScope<1> hs(self);
   Handle<ObjectArray<Object>> image_roots(hs.NewHandle(CreateImageRoots()));
@@ -543,14 +547,19 @@
   {
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
     // TODO: Image spaces only?
-    const char* old = self->StartAssertNoThreadSuspension("ImageWriter");
     DCHECK_LT(image_end_, image_->Size());
     // Clear any pre-existing monitors which may have been in the monitor words.
     heap->VisitObjects(WalkFieldsCallback, this);
-    self->EndAssertNoThreadSuspension(old);
   }
 
-  const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize);
+  image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots.Get()));
+
+  // Note that image_end_ is left at end of used space
+}
+
+void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
+  CHECK_NE(0U, oat_loaded_size);
+  const byte* oat_file_begin = GetOatFileBegin();
   const byte* oat_file_end = oat_file_begin + oat_loaded_size;
   oat_data_begin_ = oat_file_begin + oat_data_offset;
   const byte* oat_data_end = oat_data_begin_ + oat_file_->Size();
@@ -560,37 +569,33 @@
   const size_t heap_bytes_per_bitmap_byte = kBitsPerByte * kObjectAlignment;
   const size_t bitmap_bytes = RoundUp(image_end_, heap_bytes_per_bitmap_byte) /
       heap_bytes_per_bitmap_byte;
-  ImageHeader image_header(PointerToLowMemUInt32(image_begin_),
-                           static_cast<uint32_t>(image_end_),
-                           RoundUp(image_end_, kPageSize),
-                           RoundUp(bitmap_bytes, kPageSize),
-                           PointerToLowMemUInt32(GetImageAddress(image_roots.Get())),
-                           oat_file_->GetOatHeader().GetChecksum(),
-                           PointerToLowMemUInt32(oat_file_begin),
-                           PointerToLowMemUInt32(oat_data_begin_),
-                           PointerToLowMemUInt32(oat_data_end),
-                           PointerToLowMemUInt32(oat_file_end));
-  memcpy(image_->Begin(), &image_header, sizeof(image_header));
-
-  // Note that image_end_ is left at end of used space
+  new (image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_begin_),
+                                    static_cast<uint32_t>(image_end_),
+                                    RoundUp(image_end_, kPageSize),
+                                    RoundUp(bitmap_bytes, kPageSize),
+                                    image_roots_address_,
+                                    oat_file_->GetOatHeader().GetChecksum(),
+                                    PointerToLowMemUInt32(oat_file_begin),
+                                    PointerToLowMemUInt32(oat_data_begin_),
+                                    PointerToLowMemUInt32(oat_data_end),
+                                    PointerToLowMemUInt32(oat_file_end));
 }
 
+
 void ImageWriter::CopyAndFixupObjects()
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Thread* self = Thread::Current();
-  const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
+  ScopedAssertNoThreadSuspension ants(Thread::Current(), "ImageWriter");
   gc::Heap* heap = Runtime::Current()->GetHeap();
   // TODO: heap validation can't handle this fix up pass
   heap->DisableObjectValidation();
   // TODO: Image spaces only?
-  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  WriterMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   heap->VisitObjects(CopyAndFixupObjectsCallback, this);
   // Fix up the object previously had hash codes.
   for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) {
     hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second), false);
   }
   saved_hashes_.clear();
-  self->EndAssertNoThreadSuspension(old_cause);
 }
 
 void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
@@ -739,23 +744,17 @@
 
   // The resolution method has a special trampoline to call.
   if (UNLIKELY(orig == Runtime::Current()->GetResolutionMethod())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
     copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
-#endif
     copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
   } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
     copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
-#endif
     copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_imt_conflict_trampoline_offset_));
   } else {
     // We assume all methods have code. If they don't currently then we set them to the use the
     // resolution trampoline. Abstract methods never have code and so we need to make sure their
     // use results in an AbstractMethodError. We use the interpreter to achieve this.
     if (UNLIKELY(orig->IsAbstract())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
       copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_to_interpreter_bridge_offset_));
-#endif
       copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_to_interpreter_bridge_offset_));
       copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
           (const_cast<byte*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
@@ -765,9 +764,8 @@
       copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
 
       // Portable entrypoint:
-      bool portable_is_interpreted = false;
-#if defined(ART_USE_PORTABLE_COMPILER)
       const byte* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
+      bool portable_is_interpreted = false;
       if (portable_code != nullptr &&
           (!orig->IsStatic() || orig->IsConstructor() || orig->GetDeclaringClass()->IsInitialized())) {
         // We have code for a non-static or initialized method, just use the code.
@@ -787,7 +785,7 @@
         portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
       }
       copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(portable_code);
-#endif
+
       // JNI entrypoint:
       if (orig->IsNative()) {
         // The native method's pointer is set to a stub to lookup via dlsym.
@@ -820,19 +818,12 @@
   return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
 }
 
-void ImageWriter::PatchOatCodeAndMethods(File* elf_file) {
+void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) {
   std::string error_msg;
   std::unique_ptr<ElfFile> elf(ElfFile::Open(elf_file, PROT_READ|PROT_WRITE,
                                              MAP_SHARED, &error_msg));
   if (elf.get() == nullptr) {
-    LOG(FATAL) << "Unable patch oat file: " << error_msg;
-    return;
-  }
-  if (!ElfPatcher::Patch(&compiler_driver_, elf.get(), oat_file_,
-                         reinterpret_cast<uintptr_t>(oat_data_begin_),
-                         GetImageAddressCallback, reinterpret_cast<void*>(this),
-                         &error_msg)) {
-    LOG(FATAL) << "unable to patch oat file: " << error_msg;
+    LOG(FATAL) << "Unable open oat file: " << error_msg;
     return;
   }
   OatHeader* oat_header = GetOatHeaderFromElf(elf.get());
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index e8bcf7f..bdf0614 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -37,17 +37,39 @@
 // Write a Space built during compilation for use during execution.
 class ImageWriter {
  public:
-  explicit ImageWriter(const CompilerDriver& compiler_driver)
-      : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
+  ImageWriter(const CompilerDriver& compiler_driver, uintptr_t image_begin)
+      : compiler_driver_(compiler_driver), image_begin_(reinterpret_cast<byte*>(image_begin)),
+        image_end_(0), image_roots_address_(0), oat_file_(NULL),
         oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
-        interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
-        portable_resolution_trampoline_offset_(0), quick_generic_jni_trampoline_offset_(0),
-        quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {}
+        interpreter_to_compiled_code_bridge_offset_(0), jni_dlsym_lookup_offset_(0),
+        portable_imt_conflict_trampoline_offset_(0), portable_resolution_trampoline_offset_(0),
+        portable_to_interpreter_bridge_offset_(0), quick_generic_jni_trampoline_offset_(0),
+        quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
+        quick_to_interpreter_bridge_offset_(0) {
+    CHECK_NE(image_begin, 0U);
+  }
 
   ~ImageWriter() {}
 
+  bool PrepareImageAddressSpace();
+
+  bool IsImageAddressSpaceReady() const {
+    return image_roots_address_ != 0u;
+  }
+
+  mirror::Object* GetImageAddress(mirror::Object* object) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (object == NULL) {
+      return NULL;
+    }
+    return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object));
+  }
+
+  byte* GetOatFileBegin() const {
+    return image_begin_ + RoundUp(image_end_, kPageSize);
+  }
+
   bool Write(const std::string& image_filename,
-             uintptr_t image_begin,
              const std::string& oat_filename,
              const std::string& oat_location)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
@@ -75,14 +97,6 @@
     return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
   }
 
-  mirror::Object* GetImageAddress(mirror::Object* object) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (object == NULL) {
-      return NULL;
-    }
-    return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object));
-  }
-
   mirror::Object* GetLocalAddress(mirror::Object* object) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     size_t offset = GetImageOffset(object);
@@ -131,7 +145,9 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Lays out where the image objects will be at runtime.
-  void CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset)
+  void CalculateNewObjectOffsets()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -162,23 +178,25 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Patches references in OatFile to expect runtime addresses.
-  void PatchOatCodeAndMethods(File* elf_file)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetOatChecksumFromElfFile(File* elf_file);
 
   const CompilerDriver& compiler_driver_;
 
+  // Beginning target image address for the output image.
+  byte* image_begin_;
+
+  // Offset to the free space in image_.
+  size_t image_end_;
+
+  // The image roots address in the image.
+  uint32_t image_roots_address_;
+
   // oat file with code for this image
   OatFile* oat_file_;
 
   // Memory mapped for generating the image.
   std::unique_ptr<MemMap> image_;
 
-  // Offset to the free space in image_.
-  size_t image_end_;
-
-  // Beginning target image address for the output image.
-  byte* image_begin_;
-
   // Saved hashes (objects are inside of the image so that they don't move).
   std::vector<std::pair<mirror::Object*, uint32_t>> saved_hashes_;
 
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index ed1175b..a21004c 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -80,10 +80,8 @@
         CompileMethod(method);
         ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr)
             << method_name << " " << method_sig;
-#if defined(ART_USE_PORTABLE_COMPILER)
         ASSERT_TRUE(method->GetEntryPointFromPortableCompiledCode() != nullptr)
             << method_name << " " << method_sig;
-#endif
       }
     }
   }
@@ -225,13 +223,9 @@
   SetUpForTest(false, "bar", "(I)I", nullptr);
   // calling through stub will link with &Java_MyClassNatives_bar
 
-  ScopedObjectAccess soa(Thread::Current());
   std::string reason;
-  StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
-  ASSERT_TRUE(
-      Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", class_loader, &reason)) << reason;
+  ASSERT_TRUE(Runtime::Current()->GetJavaVM()->LoadNativeLibrary(env_, "", class_loader_, &reason))
+      << reason;
 
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
   EXPECT_EQ(25, result);
@@ -244,13 +238,9 @@
   SetUpForTest(true, "sbar", "(I)I", nullptr);
   // calling through stub will link with &Java_MyClassNatives_sbar
 
-  ScopedObjectAccess soa(Thread::Current());
   std::string reason;
-  StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
-  ASSERT_TRUE(
-      Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", class_loader, &reason)) << reason;
+  ASSERT_TRUE(Runtime::Current()->GetJavaVM()->LoadNativeLibrary(env_, "", class_loader_, &reason))
+      << reason;
 
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
   EXPECT_EQ(43, result);
@@ -978,10 +968,10 @@
   check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass()");
 
   // Here, we just call the method incorrectly; we should catch that too.
-  env_->CallVoidMethod(jobj_, jmethod_);
+  env_->CallObjectMethod(jobj_, jmethod_);
   check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass()");
-  env_->CallStaticVoidMethod(jklass_, jmethod_);
-  check_jni_abort_catcher.Check("calling non-static method java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass() with CallStaticVoidMethodV");
+  env_->CallStaticObjectMethod(jklass_, jmethod_);
+  check_jni_abort_catcher.Check("calling non-static method java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass() with CallStaticObjectMethodV");
 }
 
 JNI_TEST(UpcallReturnTypeChecking_Instance)
@@ -998,10 +988,10 @@
   check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass()");
 
   // Here, we just call the method incorrectly; we should catch that too.
-  env_->CallStaticVoidMethod(jklass_, jmethod_);
+  env_->CallStaticObjectMethod(jklass_, jmethod_);
   check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass()");
-  env_->CallVoidMethod(jobj_, jmethod_);
-  check_jni_abort_catcher.Check("calling static method java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass() with CallVoidMethodV");
+  env_->CallObjectMethod(jobj_, jmethod_);
+  check_jni_abort_catcher.Check("calling static method java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass() with CallObjectMethodV");
 }
 
 JNI_TEST(UpcallReturnTypeChecking_Static)
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index c38cfaf..78a228b 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "jni_compiler.h"
+
 #include <algorithm>
 #include <memory>
 #include <vector>
@@ -27,7 +29,7 @@
 #include "dex_file-inl.h"
 #include "driver/compiler_driver.h"
 #include "entrypoints/quick/quick_entrypoints.h"
-#include "jni_internal.h"
+#include "jni_env_ext.h"
 #include "mirror/art_method.h"
 #include "utils/assembler.h"
 #include "utils/managed_register.h"
@@ -90,6 +92,7 @@
 
   // Assembler that holds generated instructions
   std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set));
+  jni_asm->InitializeFrameDescriptionEntry();
 
   // Offsets into data structures
   // TODO: if cross compiling these offsets are for the host not the target
@@ -432,12 +435,14 @@
   std::vector<uint8_t> managed_code(cs);
   MemoryRegion code(&managed_code[0], managed_code.size());
   __ FinalizeInstructions(code);
+  jni_asm->FinalizeFrameDescriptionEntry();
   return new CompiledMethod(driver,
                             instruction_set,
                             managed_code,
                             frame_size,
                             main_jni_conv->CoreSpillMask(),
-                            main_jni_conv->FpSpillMask());
+                            main_jni_conv->FpSpillMask(),
+                            jni_asm->GetFrameDescriptionEntry());
 }
 
 // Copy a single parameter from the managed to the JNI calling convention
@@ -543,10 +548,9 @@
   }
 }
 
-}  // namespace art
-
-extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver* compiler,
-                                                         uint32_t access_flags, uint32_t method_idx,
-                                                         const art::DexFile& dex_file) {
+CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags,
+                                         uint32_t method_idx, const DexFile& dex_file) {
   return ArtJniCompileMethodInternal(compiler, access_flags, method_idx, dex_file);
 }
+
+}  // namespace art
diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h
new file mode 100644
index 0000000..46277f1
--- /dev/null
+++ b/compiler/jni/quick/jni_compiler.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 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_COMPILER_JNI_QUICK_JNI_COMPILER_H_
+#define ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_
+
+#include "dex_file.h"
+
+namespace art {
+
+class CompilerDriver;
+class CompiledMethod;
+
+CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags,
+                                         uint32_t method_idx, const DexFile& dex_file);
+
+}  // namespace art
+
+#endif  // ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
index 5990e8c..3aeecad 100644
--- a/compiler/llvm/compiler_llvm.cc
+++ b/compiler/llvm/compiler_llvm.cc
@@ -141,7 +141,7 @@
   cunit->SetDexCompilationUnit(dex_compilation_unit);
   cunit->SetCompilerDriver(compiler_driver_);
   // TODO: consolidate ArtCompileMethods
-  CompileOneMethod(*compiler_driver_,
+  CompileOneMethod(compiler_driver_,
                    compiler_driver_->GetCompiler(),
                    dex_compilation_unit->GetCodeItem(),
                    dex_compilation_unit->GetAccessFlags(),
@@ -172,68 +172,62 @@
 }
 
 
-}  // namespace llvm
-}  // namespace art
-
-static art::llvm::CompilerLLVM* ContextOf(art::CompilerDriver* driver) {
+static CompilerLLVM* ContextOf(art::CompilerDriver* driver) {
   void *compiler_context = driver->GetCompilerContext();
   CHECK(compiler_context != NULL);
-  return reinterpret_cast<art::llvm::CompilerLLVM*>(compiler_context);
+  return reinterpret_cast<CompilerLLVM*>(compiler_context);
 }
 
-static art::llvm::CompilerLLVM* ContextOf(const art::CompilerDriver& driver) {
+static CompilerLLVM* ContextOf(const art::CompilerDriver& driver) {
   void *compiler_context = driver.GetCompilerContext();
   CHECK(compiler_context != NULL);
-  return reinterpret_cast<art::llvm::CompilerLLVM*>(compiler_context);
+  return reinterpret_cast<CompilerLLVM*>(compiler_context);
 }
 
-extern "C" void ArtInitCompilerContext(art::CompilerDriver* driver) {
+void ArtInitCompilerContext(CompilerDriver* driver) {
   CHECK(driver->GetCompilerContext() == nullptr);
 
-  art::llvm::CompilerLLVM* compiler_llvm = new art::llvm::CompilerLLVM(driver,
-                                                                       driver->GetInstructionSet());
+  CompilerLLVM* compiler_llvm = new CompilerLLVM(driver, driver->GetInstructionSet());
 
   driver->SetCompilerContext(compiler_llvm);
 }
 
-extern "C" void ArtUnInitCompilerContext(art::CompilerDriver* driver) {
+void ArtUnInitCompilerContext(CompilerDriver* driver) {
   delete ContextOf(driver);
   driver->SetCompilerContext(nullptr);
 }
-extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver* driver,
-                                                 const art::DexFile::CodeItem* code_item,
-                                                 uint32_t access_flags,
-                                                 art::InvokeType invoke_type,
-                                                 uint16_t class_def_idx,
-                                                 uint32_t method_idx,
-                                                 jobject class_loader,
-                                                 const art::DexFile& dex_file) {
+
+CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
+                                 uint32_t access_flags, InvokeType invoke_type,
+                                 uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
+                                 const DexFile& dex_file) {
   UNUSED(class_def_idx);  // TODO: this is used with Compiler::RequiresConstructorBarrier.
-  art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
+  ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
 
-  art::DexCompilationUnit dex_compilation_unit(
-    NULL, class_loader, class_linker, dex_file, code_item,
-    class_def_idx, method_idx, access_flags, driver->GetVerifiedMethod(&dex_file, method_idx));
-  art::llvm::CompilerLLVM* compiler_llvm = ContextOf(driver);
-  art::CompiledMethod* result = compiler_llvm->CompileDexMethod(&dex_compilation_unit, invoke_type);
+  DexCompilationUnit dex_compilation_unit(nullptr, class_loader, class_linker, dex_file, code_item,
+                                          class_def_idx, method_idx, access_flags,
+                                          driver->GetVerifiedMethod(&dex_file, method_idx));
+  CompilerLLVM* compiler_llvm = ContextOf(driver);
+  CompiledMethod* result = compiler_llvm->CompileDexMethod(&dex_compilation_unit, invoke_type);
   return result;
 }
 
-extern "C" art::CompiledMethod* ArtLLVMJniCompileMethod(art::CompilerDriver* driver,
-                                                        uint32_t access_flags, uint32_t method_idx,
-                                                        const art::DexFile& dex_file) {
-  art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
+CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
+                                        uint32_t method_idx, const DexFile& dex_file) {
+  ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
 
-  art::DexCompilationUnit dex_compilation_unit(
-      nullptr, nullptr, class_linker, dex_file, nullptr,
-      0, method_idx, access_flags, nullptr);
+  DexCompilationUnit dex_compilation_unit(nullptr, nullptr, class_linker, dex_file, nullptr,
+                                          0, method_idx, access_flags, nullptr);
 
-  art::llvm::CompilerLLVM* compiler_llvm = ContextOf(driver);
-  art::CompiledMethod* result = compiler_llvm->CompileNativeMethod(&dex_compilation_unit);
+  CompilerLLVM* compiler_llvm = ContextOf(driver);
+  CompiledMethod* result = compiler_llvm->CompileNativeMethod(&dex_compilation_unit);
   return result;
 }
 
-extern "C" void compilerLLVMSetBitcodeFileName(const art::CompilerDriver& driver,
-                                               const std::string& filename) {
+void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
   ContextOf(driver)->SetBitcodeFileName(filename);
 }
+
+}  // namespace llvm
+}  // namespace art
+
diff --git a/compiler/llvm/compiler_llvm.h b/compiler/llvm/compiler_llvm.h
index cc74deb..7d29198 100644
--- a/compiler/llvm/compiler_llvm.h
+++ b/compiler/llvm/compiler_llvm.h
@@ -95,6 +95,19 @@
   DISALLOW_COPY_AND_ASSIGN(CompilerLLVM);
 };
 
+void ArtInitCompilerContext(CompilerDriver* driver);
+
+void ArtUnInitCompilerContext(CompilerDriver* driver);
+
+CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
+                                 uint32_t access_flags, InvokeType invoke_type,
+                                 uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
+                                 const DexFile& dex_file);
+
+CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
+                                        uint32_t method_idx, const DexFile& dex_file);
+
+void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename);
 
 }  // namespace llvm
 }  // namespace art
diff --git a/compiler/llvm/llvm_compiler.cc b/compiler/llvm/llvm_compiler.cc
new file mode 100644
index 0000000..55af614
--- /dev/null
+++ b/compiler/llvm/llvm_compiler.cc
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "llvm_compiler.h"
+
+#ifdef ART_USE_PORTABLE_COMPILER
+#include "compiler.h"
+#include "compiler_llvm.h"
+#include "dex/portable/mir_to_gbc.h"
+#include "dex_file.h"
+#include "elf_writer_mclinker.h"
+#include "mirror/art_method-inl.h"
+#endif
+
+namespace art {
+
+#ifdef ART_USE_PORTABLE_COMPILER
+
+namespace llvm {
+
+// Thread-local storage compiler worker threads
+class LLVMCompilerTls : public CompilerTls {
+  public:
+    LLVMCompilerTls() : llvm_info_(nullptr) {}
+    ~LLVMCompilerTls() {}
+
+    void* GetLLVMInfo() { return llvm_info_; }
+
+    void SetLLVMInfo(void* llvm_info) { llvm_info_ = llvm_info; }
+
+  private:
+    void* llvm_info_;
+};
+
+
+
+class LLVMCompiler FINAL : public Compiler {
+ public:
+  explicit LLVMCompiler(CompilerDriver* driver) : Compiler(driver, 1000) {}
+
+  CompilerTls* CreateNewCompilerTls() {
+    return new LLVMCompilerTls();
+  }
+
+  void Init() const OVERRIDE {
+    ArtInitCompilerContext(GetCompilerDriver());
+  }
+
+  void UnInit() const OVERRIDE {
+    ArtUnInitCompilerContext(GetCompilerDriver());
+  }
+
+  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+      OVERRIDE {
+    return true;
+  }
+
+  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+                          uint32_t access_flags,
+                          InvokeType invoke_type,
+                          uint16_t class_def_idx,
+                          uint32_t method_idx,
+                          jobject class_loader,
+                          const DexFile& dex_file) const OVERRIDE {
+    CompiledMethod* method = TryCompileWithSeaIR(code_item,
+                                                 access_flags,
+                                                 invoke_type,
+                                                 class_def_idx,
+                                                 method_idx,
+                                                 class_loader,
+                                                 dex_file);
+    if (method != nullptr) {
+      return method;
+    }
+
+    return ArtCompileMethod(GetCompilerDriver(),
+                            code_item,
+                            access_flags,
+                            invoke_type,
+                            class_def_idx,
+                            method_idx,
+                            class_loader,
+                            dex_file);
+  }
+
+  CompiledMethod* JniCompile(uint32_t access_flags,
+                             uint32_t method_idx,
+                             const DexFile& dex_file) const OVERRIDE {
+    return ArtLLVMJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
+  }
+
+  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const {
+    return reinterpret_cast<uintptr_t>(method->GetEntryPointFromPortableCompiledCode());
+  }
+
+  bool WriteElf(art::File* file,
+                OatWriter* oat_writer,
+                const std::vector<const art::DexFile*>& dex_files,
+                const std::string& android_root,
+                bool is_host, const CompilerDriver& driver) const
+      OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return art::ElfWriterMclinker::Create(
+        file, oat_writer, dex_files, android_root, is_host, driver);
+  }
+
+  Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+    return PortableCodeGenerator(
+        cu, cu->mir_graph.get(), &cu->arena,
+        reinterpret_cast<art::llvm::LlvmCompilationUnit*>(compilation_unit));
+  }
+
+  void InitCompilationUnit(CompilationUnit& cu) const {
+      // Fused long branches not currently useful in bitcode.
+    cu.disable_opt |=
+        (1 << kBranchFusing) |
+        (1 << kSuppressExceptionEdges);
+  }
+
+  bool IsPortable() const OVERRIDE {
+    return true;
+  }
+
+  void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
+    typedef void (*SetBitcodeFileNameFn)(const CompilerDriver&, const std::string&);
+
+    SetBitcodeFileNameFn set_bitcode_file_name =
+      reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName);
+
+    set_bitcode_file_name(driver, filename);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LLVMCompiler);
+};
+
+}  // namespace llvm
+#endif
+
+Compiler* CreateLLVMCompiler(CompilerDriver* driver) {
+#ifdef ART_USE_PORTABLE_COMPILER
+      return new llvm::LLVMCompiler(driver);
+#else
+      return nullptr;
+#endif
+}
+
+}  // namespace art
diff --git a/compiler/llvm/llvm_compiler.h b/compiler/llvm/llvm_compiler.h
new file mode 100644
index 0000000..da6d0e9
--- /dev/null
+++ b/compiler/llvm/llvm_compiler.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 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_COMPILER_LLVM_LLVM_COMPILER_H_
+#define ART_COMPILER_LLVM_LLVM_COMPILER_H_
+
+namespace art {
+
+class Compiler;
+class CompilerDriver;
+
+Compiler* CreateLLVMCompiler(CompilerDriver* driver);
+
+}
+
+#endif  // ART_COMPILER_LLVM_LLVM_COMPILER_H_
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 11d1728..0b1f9e2 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "class_linker.h"
 #include "common_compiler_test.h"
 #include "compiler.h"
 #include "dex/verification_results.h"
@@ -113,7 +114,6 @@
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
 
-  ScopedObjectAccess soa(Thread::Current());
   ScratchFile tmp;
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
@@ -122,6 +122,7 @@
                        4096U,
                        0,
                        compiler_driver_.get(),
+                       nullptr,
                        &timings,
                        &key_value_store);
   bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
@@ -151,6 +152,7 @@
                                                                     &dex_file_checksum);
   ASSERT_TRUE(oat_dex_file != nullptr);
   CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
+  ScopedObjectAccess soa(Thread::Current());
   for (size_t i = 0; i < dex_file->NumClassDefs(); i++) {
     const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
     const byte* class_data = dex_file->GetClassData(class_def);
@@ -187,7 +189,7 @@
   EXPECT_EQ(84U, sizeof(OatHeader));
   EXPECT_EQ(8U, sizeof(OatMethodOffsets));
   EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(79 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+  EXPECT_EQ(91 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
 }
 
 TEST_F(OatTest, OatHeaderIsValid) {
@@ -196,13 +198,13 @@
     std::vector<const DexFile*> dex_files;
     uint32_t image_file_location_oat_checksum = 0;
     uint32_t image_file_location_oat_begin = 0;
-    OatHeader* oat_header = OatHeader::Create(instruction_set,
-                                              instruction_set_features,
-                                              &dex_files,
-                                              image_file_location_oat_checksum,
-                                              image_file_location_oat_begin,
-                                              nullptr);
-    ASSERT_NE(oat_header, nullptr);
+    std::unique_ptr<OatHeader> oat_header(OatHeader::Create(instruction_set,
+                                                            instruction_set_features,
+                                                            &dex_files,
+                                                            image_file_location_oat_checksum,
+                                                            image_file_location_oat_begin,
+                                                            nullptr));
+    ASSERT_NE(oat_header.get(), nullptr);
     ASSERT_TRUE(oat_header->IsValid());
 
     char* magic = const_cast<char*>(oat_header->GetMagic());
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 5bf19ed..dd64368 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -18,6 +18,7 @@
 
 #include <zlib.h>
 
+#include "base/allocator.h"
 #include "base/bit_vector.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
@@ -26,6 +27,7 @@
 #include "dex_file-inl.h"
 #include "dex/verification_results.h"
 #include "gc/space/space.h"
+#include "image_writer.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/array.h"
 #include "mirror/class_loader.h"
@@ -35,10 +37,270 @@
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
 #include "handle_scope-inl.h"
+#include "utils/arm/assembler_thumb2.h"
 #include "verifier/method_verifier.h"
 
 namespace art {
 
+class OatWriter::RelativeCallPatcher {
+ public:
+  virtual ~RelativeCallPatcher() { }
+
+  // Reserve space for relative call thunks if needed, return adjusted offset.
+  // After all methods have been processed it's call one last time with compiled_method == nullptr.
+  virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) = 0;
+
+  // Write relative call thunks if needed, return adjusted offset.
+  virtual uint32_t WriteThunks(OutputStream* out, uint32_t offset) = 0;
+
+  // Patch method code. The input displacement is relative to the patched location,
+  // the patcher may need to adjust it if the correct base is different.
+  virtual void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+                     uint32_t target_offset) = 0;
+
+ protected:
+  RelativeCallPatcher() { }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RelativeCallPatcher);
+};
+
+class OatWriter::NoRelativeCallPatcher FINAL : public RelativeCallPatcher {
+ public:
+  NoRelativeCallPatcher() { }
+
+  uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+    return offset;  // No space reserved; no patches expected.
+  }
+
+  uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+    return offset;  // No thunks added; no patches expected.
+  }
+
+  void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+             uint32_t target_offset) OVERRIDE {
+    LOG(FATAL) << "Unexpected relative patch.";
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NoRelativeCallPatcher);
+};
+
+class OatWriter::X86RelativeCallPatcher FINAL : public RelativeCallPatcher {
+ public:
+  X86RelativeCallPatcher() { }
+
+  uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+    return offset;  // No space reserved; no limit on relative call distance.
+  }
+
+  uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+    return offset;  // No thunks added; no limit on relative call distance.
+  }
+
+  void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+             uint32_t target_offset) OVERRIDE {
+    DCHECK_LE(literal_offset + 4u, code->size());
+    // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
+    uint32_t displacement = target_offset - patch_offset;
+    displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
+
+    typedef __attribute__((__aligned__(1))) int32_t unaligned_int32_t;
+    reinterpret_cast<unaligned_int32_t*>(&(*code)[literal_offset])[0] = displacement;
+  }
+
+ private:
+  // PC displacement from patch location; x86 PC for relative calls points to the next
+  // instruction and the patch location is 4 bytes earlier.
+  static constexpr int32_t kPcDisplacement = 4;
+
+  DISALLOW_COPY_AND_ASSIGN(X86RelativeCallPatcher);
+};
+
+class OatWriter::Thumb2RelativeCallPatcher FINAL : public RelativeCallPatcher {
+ public:
+  explicit Thumb2RelativeCallPatcher(OatWriter* writer)
+      : writer_(writer), thunk_code_(CompileThunkCode()),
+        thunk_locations_(), current_thunk_to_write_(0u), unprocessed_patches_() {
+  }
+
+  uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+    // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
+    // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
+    // of code. To avoid any alignment discrepancies for the final chunk, we always align the
+    // offset after reserving of writing any chunk.
+    if (UNLIKELY(compiled_method == nullptr)) {
+      uint32_t aligned_offset = CompiledMethod::AlignCode(offset, kThumb2);
+      bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
+      if (needs_thunk) {
+        thunk_locations_.push_back(aligned_offset);
+        offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), kThumb2);
+      }
+      return offset;
+    }
+    DCHECK(compiled_method->GetQuickCode() != nullptr);
+    uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
+    uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
+    uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
+    if (!unprocessed_patches_.empty() &&
+        next_aligned_offset - unprocessed_patches_.front().second > kMaxPositiveDisplacement) {
+      bool needs_thunk = ReserveSpaceProcessPatches(next_aligned_offset);
+      if (needs_thunk) {
+        // A single thunk will cover all pending patches.
+        unprocessed_patches_.clear();
+        uint32_t thunk_location = compiled_method->AlignCode(offset);
+        thunk_locations_.push_back(thunk_location);
+        offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), kThumb2);
+      }
+    }
+    for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+      if (patch.Type() == kLinkerPatchCallRelative) {
+        unprocessed_patches_.emplace_back(patch.TargetMethod(),
+                                          quick_code_offset + patch.LiteralOffset());
+      }
+    }
+    return offset;
+  }
+
+  uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+    if (current_thunk_to_write_ == thunk_locations_.size()) {
+      return offset;
+    }
+    uint32_t aligned_offset = CompiledMethod::AlignCode(offset, kThumb2);
+    if (UNLIKELY(aligned_offset == thunk_locations_[current_thunk_to_write_])) {
+      ++current_thunk_to_write_;
+      uint32_t aligned_code_delta = aligned_offset - offset;
+      if (aligned_code_delta != 0u && !writer_->WriteCodeAlignment(out, aligned_code_delta)) {
+        return 0u;
+      }
+      if (!out->WriteFully(thunk_code_.data(), thunk_code_.size())) {
+        return 0u;
+      }
+      writer_->size_relative_call_thunks_ += thunk_code_.size();
+      uint32_t thunk_end_offset = aligned_offset + thunk_code_.size();
+      // Align after writing chunk, see the ReserveSpace() above.
+      offset = CompiledMethod::AlignCode(thunk_end_offset, kThumb2);
+      aligned_code_delta = offset - thunk_end_offset;
+      if (aligned_code_delta != 0u && !writer_->WriteCodeAlignment(out, aligned_code_delta)) {
+        return 0u;
+      }
+    }
+    return offset;
+  }
+
+  void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+             uint32_t target_offset) OVERRIDE {
+    DCHECK_LE(literal_offset + 4u, code->size());
+    DCHECK_EQ(literal_offset & 1u, 0u);
+    DCHECK_EQ(patch_offset & 1u, 0u);
+    DCHECK_EQ(target_offset & 1u, 1u);  // Thumb2 mode bit.
+    // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
+    uint32_t displacement = target_offset - 1u - patch_offset;
+    // NOTE: With unsigned arithmetic we do mean to use && rather than || below.
+    if (displacement > kMaxPositiveDisplacement && displacement < -kMaxNegativeDisplacement) {
+      // Unwritten thunks have higher offsets, check if it's within range.
+      DCHECK(current_thunk_to_write_ == thunk_locations_.size() ||
+             thunk_locations_[current_thunk_to_write_] > patch_offset);
+      if (current_thunk_to_write_ != thunk_locations_.size() &&
+          thunk_locations_[current_thunk_to_write_] - patch_offset < kMaxPositiveDisplacement) {
+        displacement = thunk_locations_[current_thunk_to_write_] - patch_offset;
+      } else {
+        // We must have a previous thunk then.
+        DCHECK_NE(current_thunk_to_write_, 0u);
+        DCHECK_LT(thunk_locations_[current_thunk_to_write_ - 1], patch_offset);
+        displacement = thunk_locations_[current_thunk_to_write_ - 1] - patch_offset;
+        DCHECK(displacement >= -kMaxNegativeDisplacement);
+      }
+    }
+    displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
+    DCHECK_EQ(displacement & 1u, 0u);
+    DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u);  // 25-bit signed.
+    uint32_t signbit = (displacement >> 31) & 0x1;
+    uint32_t i1 = (displacement >> 23) & 0x1;
+    uint32_t i2 = (displacement >> 22) & 0x1;
+    uint32_t imm10 = (displacement >> 12) & 0x03ff;
+    uint32_t imm11 = (displacement >> 1) & 0x07ff;
+    uint32_t j1 = i1 ^ (signbit ^ 1);
+    uint32_t j2 = i2 ^ (signbit ^ 1);
+    uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11;
+    value |= 0xf000d000;  // BL
+
+    uint8_t* addr = &(*code)[literal_offset];
+    // Check that we're just overwriting an existing BL.
+    DCHECK_EQ(addr[1] & 0xf8, 0xf0);
+    DCHECK_EQ(addr[3] & 0xd0, 0xd0);
+    // Write the new BL.
+    addr[0] = (value >> 16) & 0xff;
+    addr[1] = (value >> 24) & 0xff;
+    addr[2] = (value >> 0) & 0xff;
+    addr[3] = (value >> 8) & 0xff;
+  }
+
+ private:
+  bool ReserveSpaceProcessPatches(uint32_t next_aligned_offset) {
+    // Process as many patches as possible, stop only on unresolved targets or calls too far back.
+    while (!unprocessed_patches_.empty()) {
+      uint32_t patch_offset = unprocessed_patches_.front().second;
+      auto it = writer_->method_offset_map_.find(unprocessed_patches_.front().first);
+      if (it == writer_->method_offset_map_.end()) {
+        // If still unresolved, check if we have a thunk within range.
+        DCHECK(thunk_locations_.empty() || thunk_locations_.back() <= patch_offset);
+        if (thunk_locations_.empty() ||
+            patch_offset - thunk_locations_.back() > kMaxNegativeDisplacement) {
+          return next_aligned_offset - patch_offset > kMaxPositiveDisplacement;
+        }
+      } else if (it->second >= patch_offset) {
+        DCHECK_LE(it->second - patch_offset, kMaxPositiveDisplacement);
+      } else {
+        // When calling back, check if we have a thunk that's closer than the actual target.
+        uint32_t target_offset = (thunk_locations_.empty() || it->second > thunk_locations_.back())
+            ? it->second
+            : thunk_locations_.back();
+        DCHECK_GT(patch_offset, target_offset);
+        if (patch_offset - target_offset > kMaxNegativeDisplacement) {
+          return true;
+        }
+      }
+      unprocessed_patches_.pop_front();
+    }
+    return false;
+  }
+
+  static std::vector<uint8_t> CompileThunkCode() {
+    // The thunk just uses the entry point in the ArtMethod. This works even for calls
+    // to the generic JNI and interpreter trampolines.
+    arm::Thumb2Assembler assembler;
+    assembler.LoadFromOffset(
+        arm::kLoadWord, arm::PC, arm::R0,
+        mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+    assembler.bkpt(0);
+    std::vector<uint8_t> thunk_code(assembler.CodeSize());
+    MemoryRegion code(thunk_code.data(), thunk_code.size());
+    assembler.FinalizeInstructions(code);
+    return thunk_code;
+  }
+
+  // PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
+  static constexpr int32_t kPcDisplacement = 4;
+
+  // Maximum positive and negative displacement measured from the patch location.
+  // (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from
+  // the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.)
+  static constexpr uint32_t kMaxPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
+  static constexpr uint32_t kMaxNegativeDisplacement = (1u << 24) - kPcDisplacement;
+
+  OatWriter* const writer_;
+  const std::vector<uint8_t> thunk_code_;
+  std::vector<uint32_t> thunk_locations_;
+  size_t current_thunk_to_write_;
+
+  // ReserveSpace() tracks unprocessed patches.
+  typedef std::pair<MethodReference, uint32_t> UnprocessedPatch;
+  std::deque<UnprocessedPatch> unprocessed_patches_;
+
+  DISALLOW_COPY_AND_ASSIGN(Thumb2RelativeCallPatcher);
+};
+
 #define DCHECK_OFFSET() \
   DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
     << "file_offset=" << file_offset << " relative_offset=" << relative_offset
@@ -52,10 +314,14 @@
                      uintptr_t image_file_location_oat_begin,
                      int32_t image_patch_delta,
                      const CompilerDriver* compiler,
+                     ImageWriter* image_writer,
                      TimingLogger* timings,
                      SafeMap<std::string, std::string>* key_value_store)
   : compiler_driver_(compiler),
+    image_writer_(image_writer),
     dex_files_(&dex_files),
+    size_(0u),
+    oat_data_offset_(0u),
     image_file_location_oat_checksum_(image_file_location_oat_checksum),
     image_file_location_oat_begin_(image_file_location_oat_begin),
     image_patch_delta_(image_patch_delta),
@@ -80,6 +346,7 @@
     size_method_header_(0),
     size_code_(0),
     size_code_alignment_(0),
+    size_relative_call_thunks_(0),
     size_mapping_table_(0),
     size_vmap_table_(0),
     size_gc_map_(0),
@@ -91,9 +358,27 @@
     size_oat_class_type_(0),
     size_oat_class_status_(0),
     size_oat_class_method_bitmaps_(0),
-    size_oat_class_method_offsets_(0) {
+    size_oat_class_method_offsets_(0),
+    method_offset_map_() {
   CHECK(key_value_store != nullptr);
 
+  switch (compiler_driver_->GetInstructionSet()) {
+    case kX86:
+    case kX86_64:
+      relative_call_patcher_.reset(new X86RelativeCallPatcher);
+      break;
+    case kArm:
+      // Fall through: we generate Thumb2 code for "arm".
+    case kThumb2:
+      relative_call_patcher_.reset(new Thumb2RelativeCallPatcher(this));
+      break;
+    case kArm64:
+      // TODO: Implement relative calls for arm64.
+    default:
+      relative_call_patcher_.reset(new NoRelativeCallPatcher);
+      break;
+  }
+
   size_t offset;
   {
     TimingLogger::ScopedTiming split("InitOatHeader", timings);
@@ -126,6 +411,7 @@
   size_ = offset;
 
   CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
+  CHECK_EQ(compiler->IsImage(), image_writer_ != nullptr);
   CHECK_EQ(compiler->IsImage(),
            key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end());
   CHECK_ALIGNED(image_patch_delta_, kPageSize);
@@ -139,7 +425,7 @@
 
 struct OatWriter::GcMapDataAccess {
   static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
-    return &compiled_method->GetGcMap();
+    return compiled_method->GetGcMap();
   }
 
   static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
@@ -315,6 +601,7 @@
     OatClass* oat_class = new OatClass(offset_, compiled_methods_,
                                        num_non_null_compiled_methods_, status);
     writer_->oat_classes_.push_back(oat_class);
+    oat_class->UpdateChecksum(writer_->oat_header_);
     offset_ += oat_class->SizeOf();
     return DexMethodVisitor::EndClass();
   }
@@ -328,6 +615,16 @@
  public:
   InitCodeMethodVisitor(OatWriter* writer, size_t offset)
     : OatDexMethodVisitor(writer, offset) {
+    writer_->absolute_patch_locations_.reserve(
+        writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
+  }
+
+  bool EndClass() {
+    OatDexMethodVisitor::EndClass();
+    if (oat_class_index_ == writer_->oat_classes_.size()) {
+      offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, nullptr);
+    }
+    return true;
   }
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
@@ -349,6 +646,7 @@
             oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
       } else {
         CHECK(quick_code != nullptr);
+        offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
         offset_ = compiled_method->AlignCode(offset_);
         DCHECK_ALIGNED_PARAM(offset_,
                              GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
@@ -357,7 +655,6 @@
         uint32_t thumb_offset = compiled_method->CodeDelta();
         quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
 
-        bool force_debug_capture = false;
         bool deduped = false;
 
         // Deduplicate code arrays.
@@ -369,6 +666,18 @@
           dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
         }
 
+        MethodReference method_ref(dex_file_, it.GetMemberIndex());
+        auto method_lb = writer_->method_offset_map_.lower_bound(method_ref);
+        if (method_lb != writer_->method_offset_map_.end() &&
+            !writer_->method_offset_map_.key_comp()(method_ref, method_lb->first)) {
+          // TODO: Should this be a hard failure?
+          LOG(WARNING) << "Multiple definitions of "
+              << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file)
+              << ((method_lb->second != quick_code_offset) ? "; OFFSET MISMATCH" : "");
+        } else {
+          writer_->method_offset_map_.PutBefore(method_lb, method_ref, quick_code_offset);
+        }
+
         // Update quick method header.
         DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
         OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
@@ -392,55 +701,39 @@
                                               frame_size_in_bytes, core_spill_mask, fp_spill_mask,
                                               code_size);
 
-        // Update checksum if this wasn't a duplicate.
         if (!deduped) {
-          writer_->oat_header_->UpdateChecksum(method_header, sizeof(*method_header));
+          // Update offsets. (Checksum is updated when writing.)
           offset_ += sizeof(*method_header);  // Method header is prepended before code.
-          writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
           offset_ += code_size;
-        }
-
-        uint32_t quick_code_start = quick_code_offset - writer_->oat_header_->GetExecutableOffset();
-        std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
-        if (cfi_info != nullptr) {
-          // Copy in the FDE, if present
-          const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
-          if (fde != nullptr) {
-            // Copy the information into cfi_info and then fix the address in the new copy.
-            int cur_offset = cfi_info->size();
-            cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
-            // Set the 'CIE_pointer' field to cur_offset+4.
-            uint32_t CIE_pointer = cur_offset + 4;
-            uint32_t offset_to_update = cur_offset + sizeof(uint32_t);
-            (*cfi_info)[offset_to_update+0] = CIE_pointer;
-            (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
-            (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
-            (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
-
-            // Set the 'initial_location' field to address the start of the method.
-            offset_to_update = cur_offset + 2*sizeof(uint32_t);
-            (*cfi_info)[offset_to_update+0] = quick_code_start;
-            (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
-            (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
-            (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
-            force_debug_capture = true;
+          // Record absolute patch locations.
+          if (!compiled_method->GetPatches().empty()) {
+            uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
+            for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+              if (patch.Type() != kLinkerPatchCallRelative) {
+                writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
+              }
+            }
           }
         }
 
+        if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+          // Record debug information for this function if we are doing that.
 
-        if (writer_->compiler_driver_->DidIncludeDebugSymbols() || force_debug_capture) {
-          // Record debug information for this function if we are doing that or
-          // we have CFI and so need it.
           std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
           if (deduped) {
-            // TODO We should place the DEDUPED tag on the first instance of a
-            // deduplicated symbol so that it will show up in a debuggerd crash
-            // report.
+            // TODO We should place the DEDUPED tag on the first instance of a deduplicated symbol
+            // so that it will show up in a debuggerd crash report.
             name += " [ DEDUPED ]";
           }
-          writer_->method_info_.push_back(DebugInfo(name, quick_code_start,
-                                                    quick_code_start + code_size));
+
+          const uint32_t quick_code_start = quick_code_offset -
+              writer_->oat_header_->GetExecutableOffset();
+          const DexFile::CodeItem *code_item = it.GetMethodCodeItem();
+          writer_->method_info_.push_back(DebugInfo(name,
+                dex_file_->GetSourceFile(dex_file_->GetClassDef(class_def_index_)),
+                quick_code_start, quick_code_start + code_size,
+                code_item == nullptr ? nullptr : dex_file_->GetDebugInfoStream(code_item),
+                compiled_method));
         }
       }
 
@@ -457,13 +750,15 @@
         } else {
           status = mirror::Class::kStatusNotReady;
         }
-        const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
-        size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
-        bool is_native = it.MemberIsNative();
-        CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
-            << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
-            << (status < mirror::Class::kStatusVerified) << " " << status << " "
-            << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+        std::vector<uint8_t> const * gc_map = compiled_method->GetGcMap();
+        if (gc_map != nullptr) {
+          size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]);
+          bool is_native = it.MemberIsNative();
+          CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
+              << gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
+              << (status < mirror::Class::kStatusVerified) << " " << status << " "
+              << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+        }
       }
 
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
@@ -498,7 +793,7 @@
       DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
 
       const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
-      uint32_t map_size = map->size() * sizeof((*map)[0]);
+      uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
       if (map_size != 0u) {
         auto lb = dedupe_map_.lower_bound(map);
         if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map, lb->first)) {
@@ -550,7 +845,14 @@
                                                       NullHandle<mirror::ClassLoader>(),
                                                       NullHandle<mirror::ArtMethod>(),
                                                       invoke_type);
-    CHECK(method != NULL) << PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
+    if (method == nullptr) {
+      LOG(ERROR) << "Unexpected failure to resolve a method: "
+                 << PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
+      soa.Self()->AssertPendingException();
+      mirror::Throwable* exc = soa.Self()->GetException(nullptr);
+      std::string dump = exc->Dump();
+      LOG(FATAL) << dump;
+    }
     // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
     method->SetQuickOatCodeOffset(offsets.code_offset_);
     method->SetOatNativeGcMapOffset(offsets.gc_map_offset_);
@@ -562,13 +864,51 @@
 class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
  public:
   WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
-                             size_t relative_offset)
+                         size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
     : OatDexMethodVisitor(writer, relative_offset),
       out_(out),
-      file_offset_(file_offset) {
+      file_offset_(file_offset),
+      self_(Thread::Current()),
+      old_no_thread_suspension_cause_(self_->StartAssertNoThreadSuspension("OatWriter patching")),
+      class_linker_(Runtime::Current()->GetClassLinker()),
+      dex_cache_(nullptr) {
+    if (writer_->image_writer_ != nullptr) {
+      // If we're creating the image, the address space must be ready so that we can apply patches.
+      CHECK(writer_->image_writer_->IsImageAddressSpaceReady());
+      patched_code_.reserve(16 * KB);
+    }
+    self_->TransitionFromSuspendedToRunnable();
   }
 
-  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+  ~WriteCodeMethodVisitor() UNLOCK_FUNCTION(Locks::mutator_lock_) {
+    self_->EndAssertNoThreadSuspension(old_no_thread_suspension_cause_);
+    self_->TransitionFromRunnableToSuspended(kNative);
+  }
+
+  bool StartClass(const DexFile* dex_file, size_t class_def_index)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    OatDexMethodVisitor::StartClass(dex_file, class_def_index);
+    if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
+      dex_cache_ = class_linker_->FindDexCache(*dex_file);
+    }
+    return true;
+  }
+
+  bool EndClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    bool result = OatDexMethodVisitor::EndClass();
+    if (oat_class_index_ == writer_->oat_classes_.size()) {
+      DCHECK(result);  // OatDexMethodVisitor::EndClass() never fails.
+      offset_ = writer_->relative_call_patcher_->WriteThunks(out_, offset_);
+      if (UNLIKELY(offset_ == 0u)) {
+        PLOG(ERROR) << "Failed to write final relative call thunks";
+        result = false;
+      }
+    }
+    return result;
+  }
+
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
     const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
@@ -579,18 +919,18 @@
       const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       if (quick_code != nullptr) {
         CHECK(compiled_method->GetPortableCode() == nullptr);
+        offset_ = writer_->relative_call_patcher_->WriteThunks(out, offset_);
+        if (offset_ == 0u) {
+          ReportWriteFailure("relative call thunk", it);
+          return false;
+        }
         uint32_t aligned_offset = compiled_method->AlignCode(offset_);
         uint32_t aligned_code_delta = aligned_offset - offset_;
         if (aligned_code_delta != 0) {
-          static const uint8_t kPadding[] = {
-              0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
-          };
-          DCHECK_LE(aligned_code_delta, sizeof(kPadding));
-          if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
+          if (!writer_->WriteCodeAlignment(out, aligned_code_delta)) {
             ReportWriteFailure("code alignment padding", it);
             return false;
           }
-          writer_->size_code_alignment_ += aligned_code_delta;
           offset_ += aligned_code_delta;
           DCHECK_OFFSET_();
         }
@@ -605,7 +945,9 @@
                    offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
             << PrettyMethod(it.GetMemberIndex(), *dex_file_);
         if (method_offsets.code_offset_ >= offset_) {
-          const OatQuickMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_];
+          const OatQuickMethodHeader& method_header =
+              oat_class->method_headers_[method_offsets_index_];
+          writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
           if (!out->WriteFully(&method_header, sizeof(method_header))) {
             ReportWriteFailure("method header", it);
             return false;
@@ -613,6 +955,31 @@
           writer_->size_method_header_ += sizeof(method_header);
           offset_ += sizeof(method_header);
           DCHECK_OFFSET_();
+
+          if (!compiled_method->GetPatches().empty()) {
+            patched_code_ =  *quick_code;
+            quick_code = &patched_code_;
+            for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+              if (patch.Type() == kLinkerPatchCallRelative) {
+                // NOTE: Relative calls across oat files are not supported.
+                uint32_t target_offset = GetTargetOffset(patch);
+                uint32_t literal_offset = patch.LiteralOffset();
+                writer_->relative_call_patcher_->Patch(&patched_code_, literal_offset,
+                                                       offset_ + literal_offset, target_offset);
+              } else if (patch.Type() == kLinkerPatchCall) {
+                uint32_t target_offset = GetTargetOffset(patch);
+                PatchCodeAddress(&patched_code_, patch.LiteralOffset(), target_offset);
+              } else if (patch.Type() == kLinkerPatchMethod) {
+                mirror::ArtMethod* method = GetTargetMethod(patch);
+                PatchObjectAddress(&patched_code_, patch.LiteralOffset(), method);
+              } else if (patch.Type() == kLinkerPatchType) {
+                mirror::Class* type = GetTargetType(patch);
+                PatchObjectAddress(&patched_code_, patch.LiteralOffset(), type);
+              }
+            }
+          }
+
+          writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
           if (!out->WriteFully(&(*quick_code)[0], code_size)) {
             ReportWriteFailure("method code", it);
             return false;
@@ -631,11 +998,81 @@
  private:
   OutputStream* const out_;
   size_t const file_offset_;
+  Thread* const self_;
+  const char* const old_no_thread_suspension_cause_;  // TODO: Use ScopedAssertNoThreadSuspension.
+  ClassLinker* const class_linker_;
+  mirror::DexCache* dex_cache_;
+  std::vector<uint8_t> patched_code_;
 
   void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) {
     PLOG(ERROR) << "Failed to write " << what << " for "
         << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation();
   }
+
+  mirror::ArtMethod* GetTargetMethod(const LinkerPatch& patch)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    MethodReference ref = patch.TargetMethod();
+    mirror::DexCache* dex_cache =
+        (dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache(*ref.dex_file);
+    mirror::ArtMethod* method = dex_cache->GetResolvedMethod(ref.dex_method_index);
+    CHECK(method != nullptr);
+    return method;
+  }
+
+  uint32_t GetTargetOffset(const LinkerPatch& patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    auto target_it = writer_->method_offset_map_.find(patch.TargetMethod());
+    uint32_t target_offset =
+        (target_it != writer_->method_offset_map_.end()) ? target_it->second : 0u;
+    // If there's no compiled code, point to the correct trampoline.
+    if (UNLIKELY(target_offset == 0)) {
+      mirror::ArtMethod* target = GetTargetMethod(patch);
+      DCHECK(target != nullptr);
+      DCHECK_EQ(target->GetQuickOatCodeOffset(), 0u);
+      target_offset = target->IsNative()
+          ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
+          : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
+    }
+    return target_offset;
+  }
+
+  mirror::Class* GetTargetType(const LinkerPatch& patch)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::DexCache* dex_cache = (dex_file_ == patch.TargetTypeDexFile())
+        ? dex_cache_ : class_linker_->FindDexCache(*patch.TargetTypeDexFile());
+    mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
+    CHECK(type != nullptr);
+    return type;
+  }
+
+  void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // NOTE: Direct method pointers across oat files don't use linker patches. However, direct
+    // type pointers across oat files do. (TODO: Investigate why.)
+    if (writer_->image_writer_ != nullptr) {
+      object = writer_->image_writer_->GetImageAddress(object);
+    }
+    uint32_t address = PointerToLowMemUInt32(object);
+    DCHECK_LE(offset + 4, code->size());
+    uint8_t* data = &(*code)[offset];
+    data[0] = address & 0xffu;
+    data[1] = (address >> 8) & 0xffu;
+    data[2] = (address >> 16) & 0xffu;
+    data[3] = (address >> 24) & 0xffu;
+  }
+
+  void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // NOTE: Direct calls across oat files don't use linker patches.
+    DCHECK(writer_->image_writer_ != nullptr);
+    uint32_t address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
+                                             writer_->oat_data_offset_ + target_offset);
+    DCHECK_LE(offset + 4, code->size());
+    uint8_t* data = &(*code)[offset];
+    data[0] = address & 0xffu;
+    data[1] = (address >> 8) & 0xffu;
+    data[2] = (address >> 16) & 0xffu;
+    data[3] = (address >> 24) & 0xffu;
+  }
 };
 
 template <typename DataAccess>
@@ -661,7 +1098,7 @@
 
       // Write deduplicated map.
       const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
-      size_t map_size = map->size() * sizeof((*map)[0]);
+      size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
       DCHECK((map_size == 0u && map_offset == 0u) ||
             (map_size != 0u && map_offset != 0u && map_offset <= offset_))
           << PrettyMethod(it.GetMemberIndex(), *dex_file_);
@@ -877,11 +1314,17 @@
 }
 
 bool OatWriter::Write(OutputStream* out) {
-  const size_t file_offset = out->Seek(0, kSeekCurrent);
+  const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
+  if (raw_file_offset == (off_t) -1) {
+    LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
+    return false;
+  }
+  const size_t file_offset = static_cast<size_t>(raw_file_offset);
 
+  // Reserve space for header. It will be written last - after updating the checksum.
   size_t header_size = oat_header_->GetHeaderSize();
-  if (!out->WriteFully(oat_header_, header_size)) {
-    PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
+  if (out->Seek(header_size, kSeekCurrent) == (off_t) -1) {
+    PLOG(ERROR) << "Failed to reserve space for oat header in " << out->GetLocation();
     return false;
   }
   size_oat_header_ += sizeof(OatHeader);
@@ -892,7 +1335,12 @@
     return false;
   }
 
-  size_t relative_offset = out->Seek(0, kSeekCurrent) - file_offset;
+  off_t tables_end_offset = out->Seek(0, kSeekCurrent);
+  if (tables_end_offset == (off_t) -1) {
+    LOG(ERROR) << "Failed to seek to oat code position in " << out->GetLocation();
+    return false;
+  }
+  size_t relative_offset = static_cast<size_t>(tables_end_offset) - file_offset;
   relative_offset = WriteMaps(out, file_offset, relative_offset);
   if (relative_offset == 0) {
     LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
@@ -911,6 +1359,12 @@
     return false;
   }
 
+  const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent);
+  if (oat_end_file_offset == (off_t) -1) {
+    LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation();
+    return false;
+  }
+
   if (kIsDebugBuild) {
     uint32_t size_total = 0;
     #define DO_STAT(x) \
@@ -936,6 +1390,7 @@
     DO_STAT(size_method_header_);
     DO_STAT(size_code_);
     DO_STAT(size_code_alignment_);
+    DO_STAT(size_relative_call_thunks_);
     DO_STAT(size_mapping_table_);
     DO_STAT(size_vmap_table_);
     DO_STAT(size_gc_map_);
@@ -951,13 +1406,29 @@
     #undef DO_STAT
 
     VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \
-    CHECK_EQ(file_offset + size_total, static_cast<uint32_t>(out->Seek(0, kSeekCurrent)));
+    CHECK_EQ(file_offset + size_total, static_cast<size_t>(oat_end_file_offset));
     CHECK_EQ(size_, size_total);
   }
 
-  CHECK_EQ(file_offset + size_, static_cast<uint32_t>(out->Seek(0, kSeekCurrent)));
+  CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset));
   CHECK_EQ(size_, relative_offset);
 
+  // Write the header now that the checksum is final.
+  if (out->Seek(file_offset, kSeekSet) == (off_t) -1) {
+    PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation();
+    return false;
+  }
+  DCHECK_EQ(raw_file_offset, out->Seek(0, kSeekCurrent));
+  if (!out->WriteFully(oat_header_, header_size)) {
+    PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
+    return false;
+  }
+  if (out->Seek(oat_end_file_offset, kSeekSet) == (off_t) -1) {
+    PLOG(ERROR) << "Failed to seek to end after writing oat header to " << out->GetLocation();
+    return false;
+  }
+  DCHECK_EQ(oat_end_file_offset, out->Seek(0, kSeekCurrent));
+
   return true;
 }
 
@@ -1084,6 +1555,18 @@
   return relative_offset;
 }
 
+bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) {
+  static const uint8_t kPadding[] = {
+      0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
+  };
+  DCHECK_LE(aligned_code_delta, sizeof(kPadding));
+  if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
+    return false;
+  }
+  size_code_alignment_ += aligned_code_delta;
+  return true;
+}
+
 OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) {
   offset_ = offset;
   const std::string& location(dex_file.GetLocation());
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 945048e..5545ba8 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -23,6 +23,7 @@
 
 #include "driver/compiler_driver.h"
 #include "mem_map.h"
+#include "method_reference.h"
 #include "oat.h"
 #include "mirror/class.h"
 #include "safe_map.h"
@@ -30,6 +31,8 @@
 namespace art {
 
 class BitVector;
+class CompiledMethod;
+class ImageWriter;
 class OutputStream;
 
 // OatHeader         variable length with count of D OatDexFiles
@@ -81,6 +84,7 @@
             uintptr_t image_file_location_oat_begin,
             int32_t image_patch_delta,
             const CompilerDriver* compiler,
+            ImageWriter* image_writer,
             TimingLogger* timings,
             SafeMap<std::string, std::string>* key_value_store);
 
@@ -92,27 +96,38 @@
     return size_;
   }
 
+  const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
+    return absolute_patch_locations_;
+  }
+
+  void SetOatDataOffset(size_t oat_data_offset) {
+    oat_data_offset_ = oat_data_offset;
+  }
+
   bool Write(OutputStream* out);
 
   ~OatWriter();
 
   struct DebugInfo {
-    DebugInfo(const std::string& method_name, uint32_t low_pc, uint32_t high_pc)
-      : method_name_(method_name), low_pc_(low_pc), high_pc_(high_pc) {
+    DebugInfo(const std::string& method_name, const char* src_file_name,
+              uint32_t low_pc, uint32_t high_pc, const uint8_t* dbgstream,
+              CompiledMethod* compiled_method)
+      : method_name_(method_name), src_file_name_(src_file_name),
+        low_pc_(low_pc), high_pc_(high_pc), dbgstream_(dbgstream),
+        compiled_method_(compiled_method) {
     }
-    std::string method_name_;
+    std::string method_name_;  // Note: this name is a pretty-printed name.
+    const char* src_file_name_;
     uint32_t    low_pc_;
     uint32_t    high_pc_;
+    const uint8_t* dbgstream_;
+    CompiledMethod* compiled_method_;
   };
 
   const std::vector<DebugInfo>& GetCFIMethodInfo() const {
     return method_info_;
   }
 
-  bool DidAddSymbols() const {
-    return compiler_driver_->DidIncludeDebugSymbols();
-  }
-
  private:
   // The DataAccess classes are helper classes that provide access to members related to
   // a given map, i.e. GC map, mapping table or vmap table. By abstracting these away
@@ -156,6 +171,8 @@
   size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
   size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
 
+  bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
+
   class OatDexFile {
    public:
     explicit OatDexFile(size_t offset, const DexFile& dex_file);
@@ -244,6 +261,7 @@
   std::vector<DebugInfo> method_info_;
 
   const CompilerDriver* const compiler_driver_;
+  ImageWriter* const image_writer_;
 
   // note OatFile does not take ownership of the DexFiles
   const std::vector<const DexFile*>* dex_files_;
@@ -251,6 +269,9 @@
   // Size required for Oat data structures.
   size_t size_;
 
+  // Offset of the oat data from the start of the mmapped region of the elf file.
+  size_t oat_data_offset_;
+
   // dependencies on the image.
   uint32_t image_file_location_oat_checksum_;
   uintptr_t image_file_location_oat_begin_;
@@ -292,6 +313,7 @@
   uint32_t size_method_header_;
   uint32_t size_code_;
   uint32_t size_code_alignment_;
+  uint32_t size_relative_call_thunks_;
   uint32_t size_mapping_table_;
   uint32_t size_vmap_table_;
   uint32_t size_gc_map_;
@@ -305,6 +327,18 @@
   uint32_t size_oat_class_method_bitmaps_;
   uint32_t size_oat_class_method_offsets_;
 
+  class RelativeCallPatcher;
+  class NoRelativeCallPatcher;
+  class X86RelativeCallPatcher;
+  class Thumb2RelativeCallPatcher;
+
+  std::unique_ptr<RelativeCallPatcher> relative_call_patcher_;
+
+  // The locations of absolute patches relative to the start of the executable section.
+  std::vector<uintptr_t> absolute_patch_locations_;
+
+  SafeMap<MethodReference, uint32_t, MethodReferenceComparator> method_offset_map_;
+
   struct CodeOffsetsKeyComparator {
     bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
       if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
@@ -317,6 +351,18 @@
       if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
         return &lhs->GetVmapTable() < &rhs->GetVmapTable();
       }
+      const auto& lhs_patches = lhs->GetPatches();
+      const auto& rhs_patches = rhs->GetPatches();
+      if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) {
+        return lhs_patches.size() < rhs_patches.size();
+      }
+      auto rit = rhs_patches.begin();
+      for (const LinkerPatch& lpatch : lhs_patches) {
+        if (UNLIKELY(!(lpatch == *rit))) {
+          return lpatch < *rit;
+        }
+        ++rit;
+      }
       return false;
     }
   };
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 43e6b83..5015bd0 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -138,13 +138,15 @@
 
 template<typename T>
 void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_offset) {
+  int32_t target_offset = instruction.GetTargetOffset();
+  PotentiallyAddSuspendCheck(target_offset, dex_offset);
   HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
   HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
   T* comparison = new (arena_) T(first, second);
   current_block_->AddInstruction(comparison);
   HInstruction* ifinst = new (arena_) HIf(comparison);
   current_block_->AddInstruction(ifinst);
-  HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset());
+  HBasicBlock* target = FindBlockStartingAt(dex_offset + target_offset);
   DCHECK(target != nullptr);
   current_block_->AddSuccessor(target);
   target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits());
@@ -155,12 +157,14 @@
 
 template<typename T>
 void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_offset) {
+  int32_t target_offset = instruction.GetTargetOffset();
+  PotentiallyAddSuspendCheck(target_offset, dex_offset);
   HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
   T* comparison = new (arena_) T(value, GetIntConstant(0));
   current_block_->AddInstruction(comparison);
   HInstruction* ifinst = new (arena_) HIf(comparison);
   current_block_->AddInstruction(ifinst);
-  HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset());
+  HBasicBlock* target = FindBlockStartingAt(dex_offset + target_offset);
   DCHECK(target != nullptr);
   current_block_->AddSuccessor(target);
   target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits());
@@ -179,9 +183,9 @@
 
   // Setup the graph with the entry block and exit block.
   graph_ = new (arena_) HGraph(arena_);
-  entry_block_ = new (arena_) HBasicBlock(graph_);
+  entry_block_ = new (arena_) HBasicBlock(graph_, 0);
   graph_->AddBlock(entry_block_);
-  exit_block_ = new (arena_) HBasicBlock(graph_);
+  exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc);
   graph_->SetEntryBlock(entry_block_);
   graph_->SetExitBlock(exit_block_);
 
@@ -209,6 +213,8 @@
   // Add the exit block at the end to give it the highest id.
   graph_->AddBlock(exit_block_);
   exit_block_->AddInstruction(new (arena_) HExit());
+  // Add the suspend check to the entry block.
+  entry_block_->AddInstruction(new (arena_) HSuspendCheck(0));
   entry_block_->AddInstruction(new (arena_) HGoto());
   return graph_;
 }
@@ -235,7 +241,7 @@
   branch_targets_.SetSize(code_end - code_ptr);
 
   // Create the first block for the dex instructions, single successor of the entry block.
-  HBasicBlock* block = new (arena_) HBasicBlock(graph_);
+  HBasicBlock* block = new (arena_) HBasicBlock(graph_, 0);
   branch_targets_.Put(0, block);
   entry_block_->AddSuccessor(block);
 
@@ -248,13 +254,13 @@
       int32_t target = instruction.GetTargetOffset() + dex_offset;
       // Create a block for the target instruction.
       if (FindBlockStartingAt(target) == nullptr) {
-        block = new (arena_) HBasicBlock(graph_);
+        block = new (arena_) HBasicBlock(graph_, target);
         branch_targets_.Put(target, block);
       }
       dex_offset += instruction.SizeInCodeUnits();
       code_ptr += instruction.SizeInCodeUnits();
       if ((code_ptr < code_end) && (FindBlockStartingAt(dex_offset) == nullptr)) {
-        block = new (arena_) HBasicBlock(graph_);
+        block = new (arena_) HBasicBlock(graph_, dex_offset);
         branch_targets_.Put(dex_offset, block);
       }
     } else {
@@ -325,18 +331,61 @@
                                 bool is_range,
                                 uint32_t* args,
                                 uint32_t register_index) {
+  Instruction::Code opcode = instruction.Opcode();
+  InvokeType invoke_type;
+  switch (opcode) {
+    case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_STATIC_RANGE:
+      invoke_type = kStatic;
+      break;
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_DIRECT_RANGE:
+      invoke_type = kDirect;
+      break;
+    case Instruction::INVOKE_VIRTUAL:
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+      invoke_type = kVirtual;
+      break;
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_INTERFACE_RANGE:
+      invoke_type = kInterface;
+      break;
+    case Instruction::INVOKE_SUPER_RANGE:
+    case Instruction::INVOKE_SUPER:
+      invoke_type = kSuper;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected invoke op: " << opcode;
+      return false;
+  }
+
   const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
   const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(method_id.proto_idx_);
   const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_);
   Primitive::Type return_type = Primitive::GetType(descriptor[0]);
-  bool is_instance_call =
-      instruction.Opcode() != Instruction::INVOKE_STATIC
-      && instruction.Opcode() != Instruction::INVOKE_STATIC_RANGE;
+  bool is_instance_call = invoke_type != kStatic;
   const size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1);
 
-  // Treat invoke-direct like static calls for now.
-  HInvoke* invoke = new (arena_) HInvokeStatic(
-      arena_, number_of_arguments, return_type, dex_offset, method_idx);
+  HInvoke* invoke = nullptr;
+  if (invoke_type == kVirtual) {
+    MethodReference target_method(dex_file_, method_idx);
+    uintptr_t direct_code;
+    uintptr_t direct_method;
+    int vtable_index;
+    // TODO: Add devirtualization support.
+    compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_offset, true, true,
+                                        &invoke_type, &target_method, &vtable_index,
+                                        &direct_code, &direct_method);
+    if (vtable_index == -1) {
+      return false;
+    }
+    invoke = new (arena_) HInvokeVirtual(
+        arena_, number_of_arguments, return_type, dex_offset, vtable_index);
+  } else {
+    // Treat invoke-direct like static calls for now.
+    invoke = new (arena_) HInvokeStatic(
+        arena_, number_of_arguments, return_type, dex_offset, method_idx);
+  }
 
   size_t start_index = 0;
   Temporaries temps(graph_, is_instance_call ? 1 : 0);
@@ -413,6 +462,7 @@
     current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
         null_check,
         value,
+        field_type,
         resolved_field->GetOffset()));
   } else {
     current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
@@ -453,14 +503,23 @@
   if (is_put) {
     HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
     // TODO: Insert a type check node if the type is Object.
-    current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset));
+    current_block_->AddInstruction(new (arena_) HArraySet(
+        object, index, value, anticipated_type, dex_offset));
   } else {
     current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
   }
 }
 
-bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) {
+void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset) {
+  if (target_offset <= 0) {
+    // Unconditionnally add a suspend check to backward branches. We can remove
+    // them after we recognize loops in the graph.
+    current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_offset));
+  }
+}
+
+bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_offset) {
   if (current_block_ == nullptr) {
     return true;  // Dead code
   }
@@ -578,7 +637,9 @@
     case Instruction::GOTO:
     case Instruction::GOTO_16:
     case Instruction::GOTO_32: {
-      HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset);
+      int32_t offset = instruction.GetTargetOffset();
+      PotentiallyAddSuspendCheck(offset, dex_offset);
+      HBasicBlock* target = FindBlockStartingAt(offset + dex_offset);
       DCHECK(target != nullptr);
       current_block_->AddInstruction(new (arena_) HGoto());
       current_block_->AddSuccessor(target);
@@ -602,7 +663,8 @@
     }
 
     case Instruction::INVOKE_STATIC:
-    case Instruction::INVOKE_DIRECT: {
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_VIRTUAL: {
       uint32_t method_idx = instruction.VRegB_35c();
       uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
       uint32_t args[5];
@@ -614,7 +676,8 @@
     }
 
     case Instruction::INVOKE_STATIC_RANGE:
-    case Instruction::INVOKE_DIRECT_RANGE: {
+    case Instruction::INVOKE_DIRECT_RANGE:
+    case Instruction::INVOKE_VIRTUAL_RANGE: {
       uint32_t method_idx = instruction.VRegB_3rc();
       uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
       uint32_t register_index = instruction.VRegC();
@@ -750,6 +813,13 @@
     ARRAY_XX(_CHAR, Primitive::kPrimChar);
     ARRAY_XX(_SHORT, Primitive::kPrimShort);
 
+    case Instruction::ARRAY_LENGTH: {
+      HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+      current_block_->AddInstruction(new (arena_) HArrayLength(object));
+      UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+      break;
+    }
+
     default:
       return false;
   }
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 170c427..e143786 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -54,7 +54,7 @@
   // Analyzes the dex instruction and adds HInstruction to the graph
   // to execute that instruction. Returns whether the instruction can
   // be handled.
-  bool AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset);
+  bool AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_offset);
 
   // Finds all instructions that start a new block, and populates branch_targets_ with
   // the newly created blocks.
@@ -70,6 +70,7 @@
   HLocal* GetLocalAt(int register_index) const;
   void UpdateLocal(int register_index, HInstruction* instruction) const;
   HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
+  void PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset);
 
   // Temporarily returns whether the compiler supports the parameters
   // of the method.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index bd8c27e..2a9a7b3 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -19,11 +19,13 @@
 #include "code_generator_arm.h"
 #include "code_generator_x86.h"
 #include "code_generator_x86_64.h"
+#include "compiled_method.h"
 #include "dex/verified_method.h"
 #include "driver/dex_compilation_unit.h"
 #include "gc_map_builder.h"
 #include "leb128.h"
 #include "mapping_table.h"
+#include "ssa_liveness_analysis.h"
 #include "utils/assembler.h"
 #include "verifier/dex_gc_map.h"
 #include "vmap_table.h"
@@ -40,17 +42,19 @@
   if (!is_leaf) {
     MarkNotLeaf();
   }
-  ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs()
-                   + GetGraph()->GetNumberOfLocalVRegs()
-                   + GetGraph()->GetNumberOfTemporaries()
-                   + 1 /* filler */);
+  ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
+                     + GetGraph()->GetNumberOfTemporaries()
+                     + 1 /* filler */,
+                   0, /* the baseline compiler does not have live registers at slow path */
+                   GetGraph()->GetMaximumNumberOfOutVRegs()
+                     + 1 /* current method */);
   GenerateFrameEntry();
 
+  HGraphVisitor* location_builder = GetLocationBuilder();
+  HGraphVisitor* instruction_visitor = GetInstructionVisitor();
   for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
     HBasicBlock* block = blocks.Get(i);
     Bind(GetLabelOf(block));
-    HGraphVisitor* location_builder = GetLocationBuilder();
-    HGraphVisitor* instruction_visitor = GetInstructionVisitor();
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
       current->Accept(location_builder);
@@ -75,10 +79,10 @@
   block_labels_.SetSize(blocks.Size());
 
   GenerateFrameEntry();
+  HGraphVisitor* instruction_visitor = GetInstructionVisitor();
   for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
     HBasicBlock* block = blocks.Get(i);
     Bind(GetLabelOf(block));
-    HGraphVisitor* instruction_visitor = GetInstructionVisitor();
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
       current->Accept(instruction_visitor);
@@ -109,10 +113,15 @@
   return -1;
 }
 
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots) {
+void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots,
+                                     size_t maximum_number_of_live_registers,
+                                     size_t number_of_out_slots) {
+  first_register_slot_in_slow_path_ = (number_of_out_slots + number_of_spill_slots) * kVRegSize;
+
   SetFrameSize(RoundUp(
       number_of_spill_slots * kVRegSize
-      + kVRegSize  // Art method
+      + number_of_out_slots * kVRegSize
+      + maximum_number_of_live_registers * GetWordSize()
       + FrameEntrySpillSize(),
       kStackAlignment));
 }
@@ -297,7 +306,7 @@
   }
 }
 
-void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data) const {
+void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data, SrcMap* src_map) const {
   uint32_t pc2dex_data_size = 0u;
   uint32_t pc2dex_entries = pc_infos_.Size();
   uint32_t pc2dex_offset = 0u;
@@ -305,6 +314,10 @@
   uint32_t dex2pc_data_size = 0u;
   uint32_t dex2pc_entries = 0u;
 
+  if (src_map != nullptr) {
+    src_map->reserve(pc2dex_entries);
+  }
+
   // We currently only have pc2dex entries.
   for (size_t i = 0; i < pc2dex_entries; i++) {
     struct PcInfo pc_info = pc_infos_.Get(i);
@@ -312,6 +325,9 @@
     pc2dex_data_size += SignedLeb128Size(pc_info.dex_pc - pc2dex_dalvik_offset);
     pc2dex_offset = pc_info.native_pc;
     pc2dex_dalvik_offset = pc_info.dex_pc;
+    if (src_map != nullptr) {
+      src_map->push_back(SrcMapElem({pc2dex_offset, pc2dex_dalvik_offset}));
+    }
   }
 
   uint32_t total_entries = pc2dex_entries + dex2pc_entries;
@@ -368,4 +384,158 @@
   *data = vmap_encoder.GetData();
 }
 
+void CodeGenerator::BuildStackMaps(std::vector<uint8_t>* data) {
+  uint32_t size = stack_map_stream_.ComputeNeededSize();
+  data->resize(size);
+  MemoryRegion region(data->data(), size);
+  stack_map_stream_.FillIn(region);
+}
+
+void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) {
+  // Collect PC infos for the mapping table.
+  struct PcInfo pc_info;
+  pc_info.dex_pc = dex_pc;
+  pc_info.native_pc = GetAssembler()->CodeSize();
+  pc_infos_.Add(pc_info);
+
+  // Populate stack map information.
+
+  if (instruction == nullptr) {
+    // For stack overflow checks.
+    stack_map_stream_.AddStackMapEntry(dex_pc, pc_info.native_pc, 0, 0, 0, 0);
+    return;
+  }
+
+  LocationSummary* locations = instruction->GetLocations();
+  HEnvironment* environment = instruction->GetEnvironment();
+
+  size_t environment_size = instruction->EnvironmentSize();
+
+  size_t register_mask = 0;
+  size_t inlining_depth = 0;
+  stack_map_stream_.AddStackMapEntry(
+      dex_pc, pc_info.native_pc, register_mask,
+      locations->GetStackMask(), environment_size, inlining_depth);
+
+  // Walk over the environment, and record the location of dex registers.
+  for (size_t i = 0; i < environment_size; ++i) {
+    HInstruction* current = environment->GetInstructionAt(i);
+    if (current == nullptr) {
+      stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kNone, 0);
+      continue;
+    }
+
+    Location location = locations->GetEnvironmentAt(i);
+    switch (location.GetKind()) {
+      case Location::kConstant: {
+        DCHECK(current == location.GetConstant());
+        if (current->IsLongConstant()) {
+          int64_t value = current->AsLongConstant()->GetValue();
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+          ++i;
+          DCHECK_LT(i, environment_size);
+        } else {
+          DCHECK(current->IsIntConstant());
+          int32_t value = current->AsIntConstant()->GetValue();
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+        }
+        break;
+      }
+
+      case Location::kStackSlot: {
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+        break;
+      }
+
+      case Location::kDoubleStackSlot: {
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack,
+                                              location.GetHighStackIndex(kVRegSize));
+        ++i;
+        DCHECK_LT(i, environment_size);
+        break;
+      }
+
+      case Location::kRegister : {
+        int id = location.reg().RegId();
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+        if (current->GetType() == Primitive::kPrimDouble
+            || current->GetType() == Primitive::kPrimLong) {
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+          ++i;
+          DCHECK_LT(i, environment_size);
+        }
+        break;
+      }
+
+      default:
+        LOG(FATAL) << "Unexpected kind " << location.GetKind();
+    }
+  }
+}
+
+size_t CodeGenerator::GetStackOffsetOfSavedRegister(size_t index) {
+  return first_register_slot_in_slow_path_ + index * GetWordSize();
+}
+
+void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) {
+  RegisterSet* register_set = locations->GetLiveRegisters();
+  uint32_t count = 0;
+  for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
+    if (register_set->ContainsCoreRegister(i)) {
+      size_t stack_offset = GetStackOffsetOfSavedRegister(count);
+      ++count;
+      SaveCoreRegister(Location::StackSlot(stack_offset), i);
+      // If the register holds an object, update the stack mask.
+      if (locations->RegisterContainsObject(i)) {
+        locations->SetStackBit(stack_offset / kVRegSize);
+      }
+    }
+  }
+
+  for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+    if (register_set->ContainsFloatingPointRegister(i)) {
+      LOG(FATAL) << "Unimplemented";
+    }
+  }
+}
+
+void CodeGenerator::RestoreLiveRegisters(LocationSummary* locations) {
+  RegisterSet* register_set = locations->GetLiveRegisters();
+  uint32_t count = 0;
+  for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
+    if (register_set->ContainsCoreRegister(i)) {
+      size_t stack_offset = GetStackOffsetOfSavedRegister(count);
+      ++count;
+      RestoreCoreRegister(Location::StackSlot(stack_offset), i);
+    }
+  }
+
+  for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+    if (register_set->ContainsFloatingPointRegister(i)) {
+      LOG(FATAL) << "Unimplemented";
+    }
+  }
+}
+
+void CodeGenerator::ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const {
+  LocationSummary* locations = suspend_check->GetLocations();
+  HBasicBlock* block = suspend_check->GetBlock();
+  DCHECK(block->GetLoopInformation()->GetSuspendCheck() == suspend_check);
+  DCHECK(block->IsLoopHeader());
+
+  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+    HInstruction* current = it.Current();
+    LiveInterval* interval = current->GetLiveInterval();
+    // We only need to clear bits of loop phis containing objects and allocated in register.
+    // Loop phis allocated on stack already have the object in the stack.
+    if (current->GetType() == Primitive::kPrimNot
+        && interval->HasRegister()
+        && interval->HasSpillSlot()) {
+      locations->ClearStackBit(interval->GetSpillSlot() / kVRegSize);
+    }
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index b31c3a3..b58f3b3 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -23,6 +23,7 @@
 #include "locations.h"
 #include "memory_region.h"
 #include "nodes.h"
+#include "stack_map_stream.h"
 #include "utils/assembler.h"
 
 namespace art {
@@ -32,6 +33,7 @@
 
 class CodeGenerator;
 class DexCompilationUnit;
+class SrcMap;
 
 class CodeAllocator {
  public:
@@ -96,7 +98,9 @@
   virtual HGraphVisitor* GetInstructionVisitor() = 0;
   virtual Assembler* GetAssembler() = 0;
   virtual size_t GetWordSize() const = 0;
-  void ComputeFrameSize(size_t number_of_spill_slots);
+  void ComputeFrameSize(size_t number_of_spill_slots,
+                        size_t maximum_number_of_live_registers,
+                        size_t number_of_out_slots);
   virtual size_t FrameEntrySpillSize() const = 0;
   int32_t GetStackSlot(HLocal* local) const;
   Location GetTemporaryLocation(HTemporary* temp) const;
@@ -112,13 +116,10 @@
   virtual void DumpCoreRegister(std::ostream& stream, int reg) const = 0;
   virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0;
   virtual InstructionSet GetInstructionSet() const = 0;
+  virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) = 0;
+  virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) = 0;
 
-  void RecordPcInfo(uint32_t dex_pc) {
-    struct PcInfo pc_info;
-    pc_info.dex_pc = dex_pc;
-    pc_info.native_pc = GetAssembler()->CodeSize();
-    pc_infos_.Add(pc_info);
-  }
+  void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc);
 
   void AddSlowPath(SlowPathCode* slow_path) {
     slow_paths_.Add(slow_path);
@@ -126,10 +127,13 @@
 
   void GenerateSlowPaths();
 
-  void BuildMappingTable(std::vector<uint8_t>* vector) const;
+  void BuildMappingTable(std::vector<uint8_t>* vector, SrcMap* src_map) const;
   void BuildVMapTable(std::vector<uint8_t>* vector) const;
   void BuildNativeGCMap(
       std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
+  void BuildStackMaps(std::vector<uint8_t>* vector);
+  void SaveLiveRegisters(LocationSummary* locations);
+  void RestoreLiveRegisters(LocationSummary* locations);
 
   bool IsLeafMethod() const {
     return is_leaf_;
@@ -139,15 +143,25 @@
     is_leaf_ = false;
   }
 
+  // Clears the spill slots taken by loop phis in the `LocationSummary` of the
+  // suspend check. This is called when the code generator generates code
+  // for the suspend check at the back edge (instead of where the suspend check
+  // is, which is the loop entry). At this point, the spill slots for the phis
+  // have not been written to.
+  void ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const;
+
  protected:
   CodeGenerator(HGraph* graph, size_t number_of_registers)
       : frame_size_(kUninitializedFrameSize),
+        core_spill_mask_(0),
+        first_register_slot_in_slow_path_(0),
         graph_(graph),
         block_labels_(graph->GetArena(), 0),
         pc_infos_(graph->GetArena(), 32),
         slow_paths_(graph->GetArena(), 8),
         blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)),
-        is_leaf_(true) {}
+        is_leaf_(true),
+        stack_map_stream_(graph->GetArena()) {}
   ~CodeGenerator() {}
 
   // Register allocation logic.
@@ -166,9 +180,11 @@
   // Frame size required for this method.
   uint32_t frame_size_;
   uint32_t core_spill_mask_;
+  uint32_t first_register_slot_in_slow_path_;
 
  private:
   void InitLocations(HInstruction* instruction);
+  size_t GetStackOffsetOfSavedRegister(size_t index);
 
   HGraph* const graph_;
 
@@ -182,6 +198,8 @@
 
   bool is_leaf_;
 
+  StackMapStream stack_map_stream_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
 };
 
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2c954a0..1876cb9 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -20,6 +20,7 @@
 #include "gc/accounting/card_table.h"
 #include "mirror/array.h"
 #include "mirror/art_method.h"
+#include "mirror/class.h"
 #include "thread.h"
 #include "utils/assembler.h"
 #include "utils/arm/assembler_arm.h"
@@ -61,18 +62,18 @@
 
 class NullCheckSlowPathARM : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     __ Bind(GetEntryLabel());
     int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
     __ ldr(LR, Address(TR, offset));
     __ blx(LR);
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HNullCheck* const instruction_;
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
 };
 
@@ -90,12 +91,50 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
 };
 
+class SuspendCheckSlowPathARM : public SlowPathCode {
+ public:
+  explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
+      : instruction_(instruction), successor_(successor) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(instruction_->GetLocations());
+    int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value();
+    __ ldr(LR, Address(TR, offset));
+    __ blx(LR);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    codegen->RestoreLiveRegisters(instruction_->GetLocations());
+    if (successor_ == nullptr) {
+      __ b(GetReturnLabel());
+    } else {
+      __ b(codegen->GetLabelOf(successor_));
+    }
+  }
+
+  Label* GetReturnLabel() {
+    DCHECK(successor_ == nullptr);
+    return &return_label_;
+  }
+
+ private:
+  HSuspendCheck* const instruction_;
+  // If not null, the block to branch to after the suspend check.
+  HBasicBlock* const successor_;
+
+  // If `successor_` is null, the label to branch to after the suspend check.
+  Label return_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM);
+};
+
 class BoundsCheckSlowPathARM : public SlowPathCode {
  public:
-  explicit BoundsCheckSlowPathARM(uint32_t dex_pc,
-                                  Location index_location,
-                                  Location length_location)
-      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+  BoundsCheckSlowPathARM(HBoundsCheck* instruction,
+                         Location index_location,
+                         Location length_location)
+      : instruction_(instruction),
+        index_location_(index_location),
+        length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
@@ -106,11 +145,11 @@
     int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
     __ ldr(LR, Address(TR, offset));
     __ blx(LR);
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
@@ -156,6 +195,14 @@
   stream << ArmManagedRegister::FromDRegister(DRegister(reg));
 }
 
+void CodeGeneratorARM::SaveCoreRegister(Location stack_location, uint32_t reg_id) {
+  __ str(static_cast<Register>(reg_id), Address(SP, stack_location.GetStackIndex()));
+}
+
+void CodeGeneratorARM::RestoreCoreRegister(Location stack_location, uint32_t reg_id) {
+  __ ldr(static_cast<Register>(reg_id), Address(SP, stack_location.GetStackIndex()));
+}
+
 CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
     : CodeGenerator(graph, kNumberOfRegIds),
       location_builder_(graph, this),
@@ -277,7 +324,7 @@
     } else {
       __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
       __ ldr(IP, Address(IP, 0));
-      RecordPcInfo(0);
+      RecordPcInfo(nullptr, 0);
     }
   }
 
@@ -526,9 +573,22 @@
 
 void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
   HBasicBlock* successor = got->GetSuccessor();
-  if (GetGraph()->GetExitBlock() == successor) {
-    codegen_->GenerateFrameExit();
-  } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
+  DCHECK(!successor->IsExitBlock());
+
+  HBasicBlock* block = got->GetBlock();
+  HInstruction* previous = got->GetPrevious();
+
+  HLoopInformation* info = block->GetLoopInformation();
+  if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
+    GenerateSuspendCheck(info->GetSuspendCheck(), successor);
+    return;
+  }
+
+  if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
+    GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+  }
+  if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
     __ b(codegen_->GetLabelOf(successor));
   }
 }
@@ -545,14 +605,14 @@
 }
 
 void LocationsBuilderARM::VisitIf(HIf* if_instr) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
   HInstruction* cond = if_instr->InputAt(0);
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
-    locations->SetInAt(0, Location::Any());
+    locations->SetInAt(0, Location::RequiresRegister());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
@@ -564,7 +624,7 @@
     DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
     __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
            ShifterOperand(0));
-    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ);
+    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE);
   } else {
     // Condition has not been materialized, use its inputs as the comparison and its
     // condition as the branch condition.
@@ -595,13 +655,13 @@
 
 
 void LocationsBuilderARM::VisitCondition(HCondition* comp) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
   if (comp->NeedsMaterialization()) {
     locations->SetOut(Location::RequiresRegister());
   }
-  comp->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
@@ -695,7 +755,8 @@
 }
 
 void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
   switch (store->InputAt(1)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -713,25 +774,24 @@
     default:
       LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
   }
-  store->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
 }
 
 void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
 }
 
 void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) {
@@ -747,7 +807,8 @@
 }
 
 void LocationsBuilderARM::VisitReturn(HReturn* ret) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
   switch (ret->InputAt(0)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -766,8 +827,6 @@
     default:
       LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
   }
-
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
@@ -794,40 +853,7 @@
 }
 
 void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
-  locations->AddTemp(ArmCoreLocation(R0));
-
-  InvokeDexCallingConventionVisitor calling_convention_visitor;
-  for (size_t i = 0; i < invoke->InputCount(); i++) {
-    HInstruction* input = invoke->InputAt(i);
-    locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
-  }
-
-  switch (invoke->GetType()) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot:
-      locations->SetOut(ArmCoreLocation(R0));
-      break;
-
-    case Primitive::kPrimLong:
-      locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
-      break;
-
-    case Primitive::kPrimVoid:
-      break;
-
-    case Primitive::kPrimDouble:
-    case Primitive::kPrimFloat:
-      LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
-      break;
-  }
-
-  invoke->SetLocations(locations);
+  HandleInvoke(invoke);
 }
 
 void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
@@ -859,12 +885,78 @@
   // LR()
   __ blx(LR);
 
-  codegen_->RecordPcInfo(invoke->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
 }
 
+void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  HandleInvoke(invoke);
+}
+
+void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
+  locations->AddTemp(ArmCoreLocation(R0));
+
+  InvokeDexCallingConventionVisitor calling_convention_visitor;
+  for (size_t i = 0; i < invoke->InputCount(); i++) {
+    HInstruction* input = invoke->InputAt(i);
+    locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
+  }
+
+  switch (invoke->GetType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot:
+      locations->SetOut(ArmCoreLocation(R0));
+      break;
+
+    case Primitive::kPrimLong:
+      locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
+      break;
+
+    case Primitive::kPrimVoid:
+      break;
+
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimFloat:
+      LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
+      break;
+  }
+}
+
+
+void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister();
+  uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() +
+          invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  // temp = object->GetClass();
+  if (receiver.IsStackSlot()) {
+    __ ldr(temp, Address(SP, receiver.GetStackIndex()));
+    __ ldr(temp, Address(temp, class_offset));
+  } else {
+    __ ldr(temp, Address(receiver.AsArm().AsCoreRegister(), class_offset));
+  }
+  // temp = temp->GetMethodAt(method_offset);
+  uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value();
+  __ ldr(temp, Address(temp, method_offset));
+  // LR = temp->GetEntryPoint();
+  __ ldr(LR, Address(temp, entry_point));
+  // LR();
+  __ blx(LR);
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
 void LocationsBuilderARM::VisitAdd(HAdd* add) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
   switch (add->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -884,7 +976,6 @@
     default:
       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
@@ -924,7 +1015,8 @@
 }
 
 void LocationsBuilderARM::VisitSub(HSub* sub) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -944,7 +1036,6 @@
     default:
       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
@@ -985,13 +1076,12 @@
 }
 
 void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
   locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
   locations->SetOut(ArmCoreLocation(R0));
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
@@ -1003,12 +1093,13 @@
   __ ldr(LR, Address(TR, offset));
   __ blx(LR);
 
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
   if (location.IsStackSlot()) {
     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -1016,7 +1107,6 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
@@ -1024,10 +1114,10 @@
 }
 
 void LocationsBuilderARM::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
@@ -1037,11 +1127,11 @@
 }
 
 void LocationsBuilderARM::VisitCompare(HCompare* compare) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  compare->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
@@ -1081,12 +1171,12 @@
 }
 
 void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     locations->SetInAt(i, Location::Any());
   }
   locations->SetOut(Location::Any());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
@@ -1094,22 +1184,22 @@
 }
 
 void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // Temporary registers for the write barrier.
-  if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+  if (instruction->GetFieldType() == Primitive::kPrimNot) {
     locations->AddTemp(Location::RequiresRegister());
     locations->AddTemp(Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
@@ -1154,10 +1244,10 @@
 }
 
 void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1214,16 +1304,15 @@
 }
 
 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1237,11 +1326,11 @@
 }
 
 void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
@@ -1340,27 +1429,27 @@
 }
 
 void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
-  if (value_type == Primitive::kPrimNot) {
+  Primitive::Type value_type = instruction->GetComponentType();
+  bool is_object = value_type == Primitive::kPrimNot;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+      instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+  if (is_object) {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0)));
     locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1)));
     locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2)));
-    codegen_->MarkNotLeaf();
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
     locations->SetInAt(2, Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
@@ -1408,7 +1497,7 @@
       int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
       __ ldr(LR, Address(TR, offset));
       __ blx(LR);
-      codegen_->RecordPcInfo(instruction->GetDexPc());
+      codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
       DCHECK(!codegen_->IsLeafMethod());
       break;
     }
@@ -1436,10 +1525,10 @@
 }
 
 void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
@@ -1451,18 +1540,18 @@
 }
 
 void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
   Register index = locations->InAt(0).AsArm().AsCoreRegister();
@@ -1497,6 +1586,41 @@
   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
 }
 
+void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) {
+  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+}
+
+void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) {
+  HBasicBlock* block = instruction->GetBlock();
+  if (block->GetLoopInformation() != nullptr) {
+    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
+    // The back edge will generate the suspend check.
+    return;
+  }
+  if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
+    // The goto will generate the suspend check.
+    return;
+  }
+  GenerateSuspendCheck(instruction, nullptr);
+}
+
+void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction,
+                                                       HBasicBlock* successor) {
+  SuspendCheckSlowPathARM* slow_path =
+      new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor);
+  codegen_->AddSlowPath(slow_path);
+
+  __ AddConstant(R4, R4, -1);
+  __ cmp(R4, ShifterOperand(0));
+  if (successor == nullptr) {
+    __ b(slow_path->GetEntryLabel(), LE);
+    __ Bind(slow_path->GetReturnLabel());
+  } else {
+    __ b(codegen_->GetLabelOf(successor), GT);
+    __ b(slow_path->GetEntryLabel());
+  }
+}
+
 ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
   return codegen_->GetAssembler();
 }
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 610625c..8c86b7a 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -83,7 +83,7 @@
 
 class LocationsBuilderARM : public HGraphVisitor {
  public:
-  explicit LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen)
+  LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen)
       : HGraphVisitor(graph), codegen_(codegen) {}
 
 #define DECLARE_VISIT_INSTRUCTION(name)     \
@@ -93,6 +93,8 @@
 
 #undef DECLARE_VISIT_INSTRUCTION
 
+  void HandleInvoke(HInvoke* invoke);
+
  private:
   CodeGeneratorARM* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -115,6 +117,11 @@
   void LoadCurrentMethod(Register reg);
 
  private:
+  // Generate code for the given suspend check. If not null, `successor`
+  // is the block to branch to if the suspend check is not needed, and after
+  // the suspend call.
+  void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
+
   ArmAssembler* const assembler_;
   CodeGeneratorARM* const codegen_;
 
@@ -124,12 +131,14 @@
 class CodeGeneratorARM : public CodeGenerator {
  public:
   explicit CodeGeneratorARM(HGraph* graph);
-  virtual ~CodeGeneratorARM() { }
+  virtual ~CodeGeneratorARM() {}
 
   virtual void GenerateFrameEntry() OVERRIDE;
   virtual void GenerateFrameExit() OVERRIDE;
   virtual void Bind(Label* label) OVERRIDE;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+  virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
+  virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
 
   virtual size_t GetWordSize() const OVERRIDE {
     return kArmWordSize;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f544d47..ea67dfd 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -15,17 +15,18 @@
  */
 
 #include "code_generator_x86.h"
+
+#include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
+#include "mirror/array.h"
+#include "mirror/art_method.h"
+#include "mirror/class.h"
+#include "thread.h"
 #include "utils/assembler.h"
 #include "utils/stack_checks.h"
 #include "utils/x86/assembler_x86.h"
 #include "utils/x86/managed_register_x86.h"
 
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "mirror/array.h"
-#include "mirror/art_method.h"
-#include "thread.h"
-
 namespace art {
 
 x86::X86ManagedRegister Location::AsX86() const {
@@ -61,16 +62,16 @@
 
 class NullCheckSlowPathX86 : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathX86(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     __ Bind(GetEntryLabel());
     __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowNullPointer)));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HNullCheck* const instruction_;
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
 };
 
@@ -91,10 +92,10 @@
 
 class BoundsCheckSlowPathX86 : public SlowPathCode {
  public:
-  explicit BoundsCheckSlowPathX86(uint32_t dex_pc,
-                                  Location index_location,
-                                  Location length_location)
-      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+  BoundsCheckSlowPathX86(HBoundsCheck* instruction,
+                         Location index_location,
+                         Location length_location)
+      : instruction_(instruction), index_location_(index_location), length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen);
@@ -103,17 +104,48 @@
     x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
     x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
     __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds)));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86);
 };
 
+class SuspendCheckSlowPathX86 : public SlowPathCode {
+ public:
+  explicit SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor)
+      : instruction_(instruction), successor_(successor) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(instruction_->GetLocations());
+    __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pTestSuspend)));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    codegen->RestoreLiveRegisters(instruction_->GetLocations());
+    if (successor_ == nullptr) {
+      __ jmp(GetReturnLabel());
+    } else {
+      __ jmp(codegen->GetLabelOf(successor_));
+    }
+  }
+
+  Label* GetReturnLabel() {
+    DCHECK(successor_ == nullptr);
+    return &return_label_;
+  }
+
+ private:
+  HSuspendCheck* const instruction_;
+  HBasicBlock* const successor_;
+  Label return_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86);
+};
+
 #undef __
 #define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
 
@@ -139,6 +171,14 @@
   stream << X86ManagedRegister::FromXmmRegister(XmmRegister(reg));
 }
 
+void CodeGeneratorX86::SaveCoreRegister(Location stack_location, uint32_t reg_id) {
+  __ movl(Address(ESP, stack_location.GetStackIndex()), static_cast<Register>(reg_id));
+}
+
+void CodeGeneratorX86::RestoreCoreRegister(Location stack_location, uint32_t reg_id) {
+  __ movl(static_cast<Register>(reg_id), Address(ESP, stack_location.GetStackIndex()));
+}
+
 CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
     : CodeGenerator(graph, kNumberOfRegIds),
       location_builder_(graph, this),
@@ -244,7 +284,7 @@
   bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
   if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
     __ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
-    RecordPcInfo(0);
+    RecordPcInfo(nullptr, 0);
   }
 
   // The return PC has already been pushed on the stack.
@@ -398,6 +438,7 @@
       __ popl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1)));
     }
   } else {
+    DCHECK(destination.IsDoubleStackSlot());
     if (source.IsRegister()) {
       __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsRegisterPairLow());
       __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)),
@@ -484,9 +525,22 @@
 
 void InstructionCodeGeneratorX86::VisitGoto(HGoto* got) {
   HBasicBlock* successor = got->GetSuccessor();
-  if (GetGraph()->GetExitBlock() == successor) {
-    codegen_->GenerateFrameExit();
-  } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
+  DCHECK(!successor->IsExitBlock());
+
+  HBasicBlock* block = got->GetBlock();
+  HInstruction* previous = got->GetPrevious();
+
+  HLoopInformation* info = block->GetLoopInformation();
+  if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
+    GenerateSuspendCheck(info->GetSuspendCheck(), successor);
+    return;
+  }
+
+  if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
+    GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+  }
+  if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
     __ jmp(codegen_->GetLabelOf(successor));
   }
 }
@@ -503,14 +557,14 @@
 }
 
 void LocationsBuilderX86::VisitIf(HIf* if_instr) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
   HInstruction* cond = if_instr->InputAt(0);
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
@@ -518,14 +572,18 @@
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
-    // Materialized condition, compare against 0
-    Location lhs = if_instr->GetLocations()->InAt(0);
-    if (lhs.IsRegister()) {
-      __ cmpl(lhs.AsX86().AsCpuRegister(), Immediate(0));
-    } else {
-      __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+    // Moves do not affect the eflags register, so if the condition is evaluated
+    // just before the if, we don't need to evaluate it again.
+    if (!condition->IsBeforeWhenDisregardMoves(if_instr)) {
+      // Materialized condition, compare against 0
+      Location lhs = if_instr->GetLocations()->InAt(0);
+      if (lhs.IsRegister()) {
+        __ cmpl(lhs.AsX86().AsCpuRegister(), Immediate(0));
+      } else {
+        __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+      }
     }
-    __ j(kEqual,  codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+    __ j(kNotEqual,  codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   } else {
     Location lhs = condition->GetLocations()->InAt(0);
     Location rhs = condition->GetLocations()->InAt(1);
@@ -564,7 +622,8 @@
 }
 
 void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
   switch (store->InputAt(1)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -589,18 +648,21 @@
 }
 
 void LocationsBuilderX86::VisitCondition(HCondition* comp) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   if (comp->NeedsMaterialization()) {
     locations->SetOut(Location::RequiresRegister());
   }
-  comp->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) {
   if (comp->NeedsMaterialization()) {
     LocationSummary* locations = comp->GetLocations();
+    Register reg = locations->Out().AsX86().AsCpuRegister();
+    // Clear register: setcc only sets the low byte.
+    __ xorl(reg, reg);
     if (locations->InAt(1).IsRegister()) {
       __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
               locations->InAt(1).AsX86().AsCpuRegister());
@@ -612,7 +674,7 @@
       __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
               Address(ESP, locations->InAt(1).GetStackIndex()));
     }
-    __ setb(X86Condition(comp->GetCondition()), locations->Out().AsX86().AsCpuRegister());
+    __ setb(X86Condition(comp->GetCondition()), reg);
   }
 }
 
@@ -665,18 +727,18 @@
 }
 
 void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) {
 }
 
 void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) {
@@ -693,7 +755,8 @@
 }
 
 void LocationsBuilderX86::VisitReturn(HReturn* ret) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
   switch (ret->InputAt(0)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -712,7 +775,6 @@
     default:
       LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
   }
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
@@ -740,8 +802,42 @@
 }
 
 void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+  HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+  Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister();
+  uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
+  size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
+      invoke->GetIndexInDexCache() * kX86WordSize;
+
+  // TODO: Implement all kinds of calls:
+  // 1) boot -> boot
+  // 2) app -> boot
+  // 3) app -> app
+  //
+  // Currently we implement the app -> app logic, which looks up in the resolve cache.
+
+  // temp = method;
+  LoadCurrentMethod(temp);
+  // temp = temp->dex_cache_resolved_methods_;
+  __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+  // temp = temp[index_in_cache]
+  __ movl(temp, Address(temp, index_in_cache));
+  // (temp + offset_of_quick_compiled_code)()
+  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  HandleInvoke(invoke);
+}
+
+void LocationsBuilderX86::HandleInvoke(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
   locations->AddTemp(X86CpuLocation(EAX));
 
   InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -776,34 +872,32 @@
   invoke->SetLocations(locations);
 }
 
-void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
   Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister();
-  uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
-  size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
-      invoke->GetIndexInDexCache() * kX86WordSize;
-
-  // TODO: Implement all kinds of calls:
-  // 1) boot -> boot
-  // 2) app -> boot
-  // 3) app -> app
-  //
-  // Currently we implement the app -> app logic, which looks up in the resolve cache.
-
-  // temp = method;
-  LoadCurrentMethod(temp);
-  // temp = temp->dex_cache_resolved_methods_;
-  __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
-  // temp = temp[index_in_cache]
-  __ movl(temp, Address(temp, index_in_cache));
-  // (temp + offset_of_quick_compiled_code)()
+  uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() +
+          invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+  // temp = object->GetClass();
+  if (receiver.IsStackSlot()) {
+    __ movl(temp, Address(ESP, receiver.GetStackIndex()));
+    __ movl(temp, Address(temp, class_offset));
+  } else {
+    __ movl(temp, Address(receiver.AsX86().AsCpuRegister(), class_offset));
+  }
+  // temp = temp->GetMethodAt(method_offset);
+  __ movl(temp, Address(temp, method_offset));
+  // call temp->GetEntryPoint();
   __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
 
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(invoke->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
 void LocationsBuilderX86::VisitAdd(HAdd* add) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
   switch (add->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -823,7 +917,6 @@
     default:
       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
@@ -876,7 +969,8 @@
 }
 
 void LocationsBuilderX86::VisitSub(HSub* sub) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -896,7 +990,6 @@
     default:
       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
@@ -949,13 +1042,12 @@
 }
 
 void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   locations->SetOut(X86CpuLocation(EAX));
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(0)));
   locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(1)));
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
@@ -966,12 +1058,13 @@
   __ fs()->call(
       Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
 
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
   if (location.IsStackSlot()) {
     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -979,17 +1072,16 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
 }
 
 void LocationsBuilderX86::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) {
@@ -1000,11 +1092,11 @@
 }
 
 void LocationsBuilderX86::VisitCompare(HCompare* compare) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   locations->SetOut(Location::RequiresRegister());
-  compare->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
@@ -1050,12 +1142,12 @@
 }
 
 void LocationsBuilderX86::VisitPhi(HPhi* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     locations->SetInAt(i, Location::Any());
   }
   locations->SetOut(Location::Any());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) {
@@ -1063,9 +1155,10 @@
 }
 
 void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
   if (field_type == Primitive::kPrimBoolean || field_type == Primitive::kPrimByte) {
     // Ensure the value is in a byte register.
     locations->SetInAt(1, X86CpuLocation(EAX));
@@ -1078,14 +1171,13 @@
     // Ensure the card is in a byte register.
     locations->AddTemp(X86CpuLocation(ECX));
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsX86().AsCpuRegister();
   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
@@ -1144,10 +1236,10 @@
 }
 
 void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1205,16 +1297,15 @@
 }
 
 void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::Any());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction->GetDexPc());
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1231,11 +1322,11 @@
 }
 
 void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
@@ -1331,14 +1422,16 @@
 }
 
 void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+      instruction,
+      value_type == Primitive::kPrimNot ? LocationSummary::kCall : LocationSummary::kNoCall);
+
   if (value_type == Primitive::kPrimNot) {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0)));
     locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1)));
     locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2)));
-    codegen_->MarkNotLeaf();
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -1349,15 +1442,13 @@
       locations->SetInAt(2, Location::RequiresRegister());
     }
   }
-
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsX86().AsCpuRegister();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
@@ -1401,7 +1492,7 @@
     case Primitive::kPrimNot: {
       DCHECK(!codegen_->IsLeafMethod());
       __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
-      codegen_->RecordPcInfo(instruction->GetDexPc());
+      codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
       break;
     }
 
@@ -1446,18 +1537,18 @@
 }
 
 void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
   Register index = locations->InAt(0).AsX86().AsCpuRegister();
@@ -1483,6 +1574,40 @@
   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
 }
 
+void LocationsBuilderX86::VisitSuspendCheck(HSuspendCheck* instruction) {
+  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+}
+
+void InstructionCodeGeneratorX86::VisitSuspendCheck(HSuspendCheck* instruction) {
+  HBasicBlock* block = instruction->GetBlock();
+  if (block->GetLoopInformation() != nullptr) {
+    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
+    // The back edge will generate the suspend check.
+    return;
+  }
+  if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
+    // The goto will generate the suspend check.
+    return;
+  }
+  GenerateSuspendCheck(instruction, nullptr);
+}
+
+void InstructionCodeGeneratorX86::GenerateSuspendCheck(HSuspendCheck* instruction,
+                                                       HBasicBlock* successor) {
+  SuspendCheckSlowPathX86* slow_path =
+      new (GetGraph()->GetArena()) SuspendCheckSlowPathX86(instruction, successor);
+  codegen_->AddSlowPath(slow_path);
+  __ fs()->cmpw(Address::Absolute(
+      Thread::ThreadFlagsOffset<kX86WordSize>().Int32Value()), Immediate(0));
+  if (successor == nullptr) {
+    __ j(kNotEqual, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetReturnLabel());
+  } else {
+    __ j(kEqual, codegen_->GetLabelOf(successor));
+    __ jmp(slow_path->GetEntryLabel());
+  }
+}
+
 X86Assembler* ParallelMoveResolverX86::GetAssembler() const {
   return codegen_->GetAssembler();
 }
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 7c50204..23145bf 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -94,6 +94,8 @@
 
 #undef DECLARE_VISIT_INSTRUCTION
 
+  void HandleInvoke(HInvoke* invoke);
+
  private:
   CodeGeneratorX86* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -117,6 +119,11 @@
   X86Assembler* GetAssembler() const { return assembler_; }
 
  private:
+  // Generate code for the given suspend check. If not null, `successor`
+  // is the block to branch to if the suspend check is not needed, and after
+  // the suspend call.
+  void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
+
   X86Assembler* const assembler_;
   CodeGeneratorX86* const codegen_;
 
@@ -132,6 +139,8 @@
   virtual void GenerateFrameExit() OVERRIDE;
   virtual void Bind(Label* label) OVERRIDE;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+  virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
+  virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
 
   virtual size_t GetWordSize() const OVERRIDE {
     return kX86WordSize;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e1807dc..78c7d9d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -20,6 +20,7 @@
 #include "gc/accounting/card_table.h"
 #include "mirror/array.h"
 #include "mirror/art_method.h"
+#include "mirror/class.h"
 #include "mirror/object_reference.h"
 #include "thread.h"
 #include "utils/assembler.h"
@@ -35,7 +36,7 @@
 
 namespace x86_64 {
 
-static constexpr bool kExplicitStackOverflowCheck = true;
+static constexpr bool kExplicitStackOverflowCheck = false;
 
 // Some x86_64 instructions require a register to be available as temp.
 static constexpr Register TMP = R11;
@@ -65,17 +66,17 @@
 
 class NullCheckSlowPathX86_64 : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathX86_64(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     __ Bind(GetEntryLabel());
     __ gs()->call(
         Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HNullCheck* const instruction_;
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
 };
 
@@ -95,12 +96,45 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
 };
 
+class SuspendCheckSlowPathX86_64 : public SlowPathCode {
+ public:
+  explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor)
+      : instruction_(instruction), successor_(successor) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    __ Bind(GetEntryLabel());
+    codegen->SaveLiveRegisters(instruction_->GetLocations());
+    __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pTestSuspend), true));
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+    codegen->RestoreLiveRegisters(instruction_->GetLocations());
+    if (successor_ == nullptr) {
+      __ jmp(GetReturnLabel());
+    } else {
+      __ jmp(codegen->GetLabelOf(successor_));
+    }
+  }
+
+  Label* GetReturnLabel() {
+    DCHECK(successor_ == nullptr);
+    return &return_label_;
+  }
+
+ private:
+  HSuspendCheck* const instruction_;
+  HBasicBlock* const successor_;
+  Label return_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86_64);
+};
+
 class BoundsCheckSlowPathX86_64 : public SlowPathCode {
  public:
-  explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc,
-                                     Location index_location,
-                                     Location length_location)
-      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+  BoundsCheckSlowPathX86_64(HBoundsCheck* instruction,
+                            Location index_location,
+                            Location length_location)
+      : instruction_(instruction),
+        index_location_(index_location),
+        length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
@@ -110,11 +144,11 @@
     x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
     __ gs()->call(Address::Absolute(
         QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
@@ -146,6 +180,14 @@
   stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg));
 }
 
+void CodeGeneratorX86_64::SaveCoreRegister(Location stack_location, uint32_t reg_id) {
+  __ movq(Address(CpuRegister(RSP), stack_location.GetStackIndex()), CpuRegister(reg_id));
+}
+
+void CodeGeneratorX86_64::RestoreCoreRegister(Location stack_location, uint32_t reg_id) {
+  __ movq(CpuRegister(reg_id), Address(CpuRegister(RSP), stack_location.GetStackIndex()));
+}
+
 CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
       : CodeGenerator(graph, kNumberOfRegIds),
         location_builder_(graph, this),
@@ -208,25 +250,26 @@
   static const int kFakeReturnRegister = 16;
   core_spill_mask_ |= (1 << kFakeReturnRegister);
 
+  bool skip_overflow_check = IsLeafMethod()
+      && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
+
+  if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
+    __ testq(CpuRegister(RAX), Address(
+        CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
+    RecordPcInfo(nullptr, 0);
+  }
+
   // The return PC has already been pushed on the stack.
   __ subq(CpuRegister(RSP),
           Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
 
-  bool skip_overflow_check = IsLeafMethod()
-      && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
+  if (!skip_overflow_check && kExplicitStackOverflowCheck) {
+    SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
+    AddSlowPath(slow_path);
 
-  if (!skip_overflow_check) {
-    if (kExplicitStackOverflowCheck) {
-      SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
-      AddSlowPath(slow_path);
-
-      __ gs()->cmpq(CpuRegister(RSP),
-                    Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
-      __ j(kLess, slow_path->GetEntryLabel());
-    } else {
-      __ testq(CpuRegister(RAX), Address(
-          CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
-    }
+    __ gs()->cmpq(CpuRegister(RSP),
+                  Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
+    __ j(kLess, slow_path->GetEntryLabel());
   }
 
   __ movl(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI));
@@ -365,9 +408,22 @@
 
 void InstructionCodeGeneratorX86_64::VisitGoto(HGoto* got) {
   HBasicBlock* successor = got->GetSuccessor();
-  if (GetGraph()->GetExitBlock() == successor) {
-    codegen_->GenerateFrameExit();
-  } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
+  DCHECK(!successor->IsExitBlock());
+
+  HBasicBlock* block = got->GetBlock();
+  HInstruction* previous = got->GetPrevious();
+
+  HLoopInformation* info = block->GetLoopInformation();
+  if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
+    GenerateSuspendCheck(info->GetSuspendCheck(), successor);
+    return;
+  }
+
+  if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
+    GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+  }
+  if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
     __ jmp(codegen_->GetLabelOf(successor));
   }
 }
@@ -384,14 +440,14 @@
 }
 
 void LocationsBuilderX86_64::VisitIf(HIf* if_instr) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
   HInstruction* cond = if_instr->InputAt(0);
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) {
@@ -399,14 +455,18 @@
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
-    // Materialized condition, compare against 0.
-    Location lhs = if_instr->GetLocations()->InAt(0);
-    if (lhs.IsRegister()) {
-      __ cmpl(lhs.AsX86_64().AsCpuRegister(), Immediate(0));
-    } else {
-      __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0));
+    // Moves do not affect the eflags register, so if the condition is evaluated
+    // just before the if, we don't need to evaluate it again.
+    if (!condition->IsBeforeWhenDisregardMoves(if_instr)) {
+      // Materialized condition, compare against 0.
+      Location lhs = if_instr->GetLocations()->InAt(0);
+      if (lhs.IsRegister()) {
+        __ cmpl(lhs.AsX86_64().AsCpuRegister(), Immediate(0));
+      } else {
+        __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0));
+      }
     }
-    __ j(kEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+    __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
   } else {
     Location lhs = condition->GetLocations()->InAt(0);
     Location rhs = condition->GetLocations()->InAt(1);
@@ -443,7 +503,8 @@
 }
 
 void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
   switch (store->InputAt(1)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -461,25 +522,27 @@
     default:
       LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
   }
-  store->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) {
 }
 
 void LocationsBuilderX86_64::VisitCondition(HCondition* comp) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   if (comp->NeedsMaterialization()) {
     locations->SetOut(Location::RequiresRegister());
   }
-  comp->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {
   if (comp->NeedsMaterialization()) {
     LocationSummary* locations = comp->GetLocations();
+    CpuRegister reg = locations->Out().AsX86_64().AsCpuRegister();
+    // Clear register: setcc only sets the low byte.
+    __ xorq(reg, reg);
     if (locations->InAt(1).IsRegister()) {
       __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
               locations->InAt(1).AsX86_64().AsCpuRegister());
@@ -490,8 +553,7 @@
       __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
               Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
     }
-    __ setcc(X86_64Condition(comp->GetCondition()),
-             comp->GetLocations()->Out().AsX86_64().AsCpuRegister());
+    __ setcc(X86_64Condition(comp->GetCondition()), reg);
   }
 }
 
@@ -544,11 +606,11 @@
 }
 
 void LocationsBuilderX86_64::VisitCompare(HCompare* compare) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  compare->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
@@ -577,18 +639,18 @@
 }
 
 void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) {
 }
 
 void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) {
@@ -604,7 +666,8 @@
 }
 
 void LocationsBuilderX86_64::VisitReturn(HReturn* ret) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
   switch (ret->InputAt(0)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -619,7 +682,6 @@
     default:
       LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
   }
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) {
@@ -685,37 +747,7 @@
 }
 
 void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
-  locations->AddTemp(X86_64CpuLocation(RDI));
-
-  InvokeDexCallingConventionVisitor calling_convention_visitor;
-  for (size_t i = 0; i < invoke->InputCount(); ++i) {
-    HInstruction* input = invoke->InputAt(i);
-    locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
-  }
-
-  switch (invoke->GetType()) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot:
-    case Primitive::kPrimLong:
-      locations->SetOut(X86_64CpuLocation(RAX));
-      break;
-
-    case Primitive::kPrimVoid:
-      break;
-
-    case Primitive::kPrimDouble:
-    case Primitive::kPrimFloat:
-      LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
-      break;
-  }
-
-  invoke->SetLocations(locations);
+  HandleInvoke(invoke);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
@@ -741,11 +773,71 @@
   __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
 
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(invoke->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  HandleInvoke(invoke);
+}
+
+void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
+  locations->AddTemp(X86_64CpuLocation(RDI));
+
+  InvokeDexCallingConventionVisitor calling_convention_visitor;
+  for (size_t i = 0; i < invoke->InputCount(); i++) {
+    HInstruction* input = invoke->InputAt(i);
+    locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
+  }
+
+  switch (invoke->GetType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot:
+    case Primitive::kPrimLong:
+      locations->SetOut(X86_64CpuLocation(RAX));
+      break;
+
+    case Primitive::kPrimVoid:
+      break;
+
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimFloat:
+      LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
+      break;
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsX86_64().AsCpuRegister();
+  size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() +
+          invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
+  LocationSummary* locations = invoke->GetLocations();
+  Location receiver = locations->InAt(0);
+  size_t class_offset = mirror::Object::ClassOffset().SizeValue();
+  // temp = object->GetClass();
+  if (receiver.IsStackSlot()) {
+    __ movq(temp, Address(CpuRegister(RSP), receiver.GetStackIndex()));
+    __ movq(temp, Address(temp, class_offset));
+  } else {
+    __ movq(temp, Address(receiver.AsX86_64().AsCpuRegister(), class_offset));
+  }
+  // temp = temp->GetMethodAt(method_offset);
+  __ movl(temp, Address(temp, method_offset));
+  // call temp->GetEntryPoint();
+  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+
+  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
 void LocationsBuilderX86_64::VisitAdd(HAdd* add) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
   switch (add->GetResultType()) {
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
@@ -770,7 +862,6 @@
     default:
       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
@@ -811,7 +902,8 @@
 }
 
 void LocationsBuilderX86_64::VisitSub(HSub* sub) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
@@ -836,7 +928,6 @@
     default:
       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
@@ -877,10 +968,9 @@
 }
 
 void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   locations->SetOut(X86_64CpuLocation(RAX));
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
@@ -892,11 +982,12 @@
       QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
 
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
 }
 
 void LocationsBuilderX86_64::VisitParameterValue(HParameterValue* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
   if (location.IsStackSlot()) {
     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -904,7 +995,6 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instruction) {
@@ -912,10 +1002,10 @@
 }
 
 void LocationsBuilderX86_64::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitNot(HNot* instruction) {
@@ -926,12 +1016,12 @@
 }
 
 void LocationsBuilderX86_64::VisitPhi(HPhi* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     locations->SetInAt(i, Location::Any());
   }
   locations->SetOut(Location::Any());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction) {
@@ -939,15 +1029,15 @@
 }
 
 void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // Temporary registers for the write barrier.
-  if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+  if (instruction->GetFieldType() == Primitive::kPrimNot) {
     locations->AddTemp(Location::RequiresRegister());
     locations->AddTemp(Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
@@ -955,7 +1045,7 @@
   CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
   CpuRegister value = locations->InAt(1).AsX86_64().AsCpuRegister();
   size_t offset = instruction->GetFieldOffset().SizeValue();
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
@@ -996,10 +1086,10 @@
 }
 
 void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1050,16 +1140,15 @@
 }
 
 void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::Any());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction->GetDexPc());
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1076,11 +1165,11 @@
 }
 
 void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
@@ -1173,27 +1262,27 @@
 }
 
 void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
-  if (value_type == Primitive::kPrimNot) {
+  Primitive::Type value_type = instruction->GetComponentType();
+  bool is_object = value_type == Primitive::kPrimNot;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+      instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+  if (is_object) {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0)));
     locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1)));
     locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2)));
-    codegen_->MarkNotLeaf();
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
     locations->SetInAt(2, Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
@@ -1237,7 +1326,7 @@
     case Primitive::kPrimNot: {
       __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
       DCHECK(!codegen_->IsLeafMethod());
-      codegen_->RecordPcInfo(instruction->GetDexPc());
+      codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
       break;
     }
 
@@ -1263,10 +1352,10 @@
 }
 
 void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
@@ -1278,18 +1367,18 @@
 }
 
 void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
   CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister();
@@ -1330,6 +1419,40 @@
   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
 }
 
+void LocationsBuilderX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
+  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+}
+
+void InstructionCodeGeneratorX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
+  HBasicBlock* block = instruction->GetBlock();
+  if (block->GetLoopInformation() != nullptr) {
+    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
+    // The back edge will generate the suspend check.
+    return;
+  }
+  if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
+    // The goto will generate the suspend check.
+    return;
+  }
+  GenerateSuspendCheck(instruction, nullptr);
+}
+
+void InstructionCodeGeneratorX86_64::GenerateSuspendCheck(HSuspendCheck* instruction,
+                                                          HBasicBlock* successor) {
+  SuspendCheckSlowPathX86_64* slow_path =
+      new (GetGraph()->GetArena()) SuspendCheckSlowPathX86_64(instruction, successor);
+  codegen_->AddSlowPath(slow_path);
+  __ gs()->cmpw(Address::Absolute(
+      Thread::ThreadFlagsOffset<kX86_64WordSize>().Int32Value(), true), Immediate(0));
+  if (successor == nullptr) {
+    __ j(kNotEqual, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetReturnLabel());
+  } else {
+    __ j(kEqual, codegen_->GetLabelOf(successor));
+    __ jmp(slow_path->GetEntryLabel());
+  }
+}
+
 X86_64Assembler* ParallelMoveResolverX86_64::GetAssembler() const {
   return codegen_->GetAssembler();
 }
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 44552ea..a299cf6 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -91,6 +91,8 @@
 
 #undef DECLARE_VISIT_INSTRUCTION
 
+  void HandleInvoke(HInvoke* invoke);
+
  private:
   CodeGeneratorX86_64* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -114,6 +116,11 @@
   X86_64Assembler* GetAssembler() const { return assembler_; }
 
  private:
+  // Generate code for the given suspend check. If not null, `successor`
+  // is the block to branch to if the suspend check is not needed, and after
+  // the suspend call.
+  void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
+
   X86_64Assembler* const assembler_;
   CodeGeneratorX86_64* const codegen_;
 
@@ -129,6 +136,8 @@
   virtual void GenerateFrameExit() OVERRIDE;
   virtual void Bind(Label* label) OVERRIDE;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+  virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
+  virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
 
   virtual size_t GetWordSize() const OVERRIDE {
     return kX86_64WordSize;
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index d7ac10d..7161eed 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -15,7 +15,9 @@
  */
 
 #include "builder.h"
-#include "code_generator.h"
+#include "code_generator_arm.h"
+#include "code_generator_x86.h"
+#include "code_generator_x86_64.h"
 #include "common_compiler_test.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
@@ -47,7 +49,6 @@
   DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator);
 };
 
-#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
 static void Run(const InternalCodeAllocator& allocator,
                 const CodeGenerator& codegen,
                 bool has_result,
@@ -64,7 +65,6 @@
     CHECK_EQ(result, expected);
   }
 }
-#endif
 
 static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) {
   ArenaPool pool;
@@ -72,28 +72,30 @@
   HGraphBuilder builder(&arena);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
   HGraph* graph = builder.BuildGraph(*item);
+  // Remove suspend checks, they cannot be executed in this context.
+  RemoveSuspendChecks(graph);
   ASSERT_NE(graph, nullptr);
   InternalCodeAllocator allocator;
 
-  CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, kX86);
+  x86::CodeGeneratorX86 codegenX86(graph);
   // We avoid doing a stack overflow check that requires the runtime being setup,
   // by making sure the compiler knows the methods we are running are leaf methods.
-  codegen->CompileBaseline(&allocator, true);
-#if defined(__i386__)
-  Run(allocator, *codegen, has_result, expected);
-#endif
+  codegenX86.CompileBaseline(&allocator, true);
+  if (kRuntimeISA == kX86) {
+    Run(allocator, codegenX86, has_result, expected);
+  }
 
-  codegen = CodeGenerator::Create(&arena, graph, kArm);
-  codegen->CompileBaseline(&allocator, true);
-#if defined(__arm__)
-  Run(allocator, *codegen, has_result, expected);
-#endif
+  arm::CodeGeneratorARM codegenARM(graph);
+  codegenARM.CompileBaseline(&allocator, true);
+  if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
+    Run(allocator, codegenARM, has_result, expected);
+  }
 
-  codegen = CodeGenerator::Create(&arena, graph, kX86_64);
-  codegen->CompileBaseline(&allocator, true);
-#if defined(__x86_64__)
-  Run(allocator, *codegen, has_result, expected);
-#endif
+  x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+  codegenX86_64.CompileBaseline(&allocator, true);
+  if (kRuntimeISA == kX86_64) {
+    Run(allocator, codegenX86_64, has_result, expected);
+  }
 }
 
 TEST(CodegenTest, ReturnVoid) {
diff --git a/compiler/optimizing/constant_propagation.cc b/compiler/optimizing/constant_propagation.cc
new file mode 100644
index 0000000..d675164
--- /dev/null
+++ b/compiler/optimizing/constant_propagation.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "constant_propagation.h"
+
+namespace art {
+
+void ConstantPropagation::Run() {
+  // Process basic blocks in reverse post-order in the dominator tree,
+  // so that an instruction turned into a constant, used as input of
+  // another instruction, may possibly be used to turn that second
+  // instruction into a constant as well.
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    // Traverse this block's instructions in (forward) order and
+    // replace the ones that can be statically evaluated by a
+    // compile-time counterpart.
+    for (HInstructionIterator it(block->GetInstructions());
+         !it.Done(); it.Advance()) {
+      HInstruction* inst = it.Current();
+      // Constant folding: replace `c <- a op b' with a compile-time
+      // evaluation of `a op b' if `a' and `b' are constant.
+      if (inst->IsBinaryOperation()) {
+        HConstant* constant =
+          inst->AsBinaryOperation()->TryStaticEvaluation(graph_->GetArena());
+        if (constant != nullptr) {
+          inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant);
+        }
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/constant_propagation.h b/compiler/optimizing/constant_propagation.h
new file mode 100644
index 0000000..0729881
--- /dev/null
+++ b/compiler/optimizing/constant_propagation.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_
+
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * Optimization pass performing a simple constant propagation on the
+ * SSA form.
+ */
+class ConstantPropagation : public ValueObject {
+ public:
+  explicit ConstantPropagation(HGraph* graph)
+    : graph_(graph) {}
+
+  void Run();
+
+ private:
+  HGraph* const graph_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConstantPropagation);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_
diff --git a/compiler/optimizing/constant_propagation_test.cc b/compiler/optimizing/constant_propagation_test.cc
new file mode 100644
index 0000000..5c8c709
--- /dev/null
+++ b/compiler/optimizing/constant_propagation_test.cc
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "constant_propagation.h"
+#include "dead_code_elimination.h"
+#include "pretty_printer.h"
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void TestCode(const uint16_t* data,
+                     const std::string& expected_before,
+                     const std::string& expected_after_cp,
+                     const std::string& expected_after_dce) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = CreateCFG(&allocator, data);
+  ASSERT_NE(graph, nullptr);
+
+  graph->BuildDominatorTree();
+  graph->TransformToSSA();
+
+  StringPrettyPrinter printer_before(graph);
+  printer_before.VisitInsertionOrder();
+  std::string actual_before = printer_before.str();
+  ASSERT_EQ(expected_before, actual_before);
+
+  ConstantPropagation(graph).Run();
+
+  StringPrettyPrinter printer_after_cp(graph);
+  printer_after_cp.VisitInsertionOrder();
+  std::string actual_after_cp = printer_after_cp.str();
+  ASSERT_EQ(expected_after_cp, actual_after_cp);
+
+  DeadCodeElimination(graph).Run();
+
+  StringPrettyPrinter printer_after_dce(graph);
+  printer_after_dce.VisitInsertionOrder();
+  std::string actual_after_dce = printer_after_dce.str();
+  ASSERT_EQ(expected_after_dce, actual_after_dce);
+
+  SSAChecker ssa_checker(&allocator, graph);
+  ssa_checker.VisitInsertionOrder();
+  ASSERT_TRUE(ssa_checker.IsValid());
+}
+
+
+/**
+ * Tiny three-register program exercising int constant folding on addition.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v0 <- 1                  0.      const/4 v0, #+1
+ *     v1 <- 2                  1.      const/4 v1, #+2
+ *     v2 <- v0 + v1            2.      add-int v2, v0, v1
+ *     return v2                4.      return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingOnAddition1) {
+  const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 << 8 | 1 << 12,
+    Instruction::CONST_4 | 1 << 8 | 2 << 12,
+    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+    Instruction::RETURN | 2 << 8);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  3: IntConstant [9]\n"
+    "  5: IntConstant [9]\n"
+    "  14: SuspendCheck\n"
+    "  15: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 2\n"
+    "  9: Add(3, 5) [12]\n"
+    "  12: Return(9)\n"
+    "BasicBlock 2, pred: 1\n"
+    "  13: Exit\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_cp_diff = {
+    { "  3: IntConstant [9]\n", "  3: IntConstant\n" },
+    { "  5: IntConstant [9]\n", "  5: IntConstant\n" },
+    { "  9: Add(3, 5) [12]\n",  "  16: IntConstant [12]\n" },
+    { "  12: Return(9)\n",      "  12: Return(16)\n" }
+  };
+  std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  3: IntConstant\n", removed },
+    { "  5: IntConstant\n", removed }
+  };
+  std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+  TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Small three-register program exercising int constant folding on addition.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v0 <- 1                  0.      const/4 v0, #+1
+ *     v1 <- 2                  1.      const/4 v1, #+2
+ *     v0 <- v0 + v1            2.      add-int/2addr v0, v1
+ *     v1 <- 3                  3.      const/4 v1, #+3
+ *     v2 <- 4                  4.      const/4 v2, #+4
+ *     v1 <- v1 + v2            5.      add-int/2addr v1, v2
+ *     v2 <- v0 + v1            6.      add-int v2, v0, v1
+ *     return v2                8.      return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingOnAddition2) {
+  const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 << 8 | 1 << 12,
+    Instruction::CONST_4 | 1 << 8 | 2 << 12,
+    Instruction::ADD_INT_2ADDR | 0 << 8 | 1 << 12,
+    Instruction::CONST_4 | 1 << 8 | 3 << 12,
+    Instruction::CONST_4 | 2 << 8 | 4 << 12,
+    Instruction::ADD_INT_2ADDR | 1 << 8 | 2 << 12,
+    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+    Instruction::RETURN | 2 << 8);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  3: IntConstant [9]\n"
+    "  5: IntConstant [9]\n"
+    "  11: IntConstant [17]\n"
+    "  13: IntConstant [17]\n"
+    "  26: SuspendCheck\n"
+    "  27: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 2\n"
+    "  9: Add(3, 5) [21]\n"
+    "  17: Add(11, 13) [21]\n"
+    "  21: Add(9, 17) [24]\n"
+    "  24: Return(21)\n"
+    "BasicBlock 2, pred: 1\n"
+    "  25: Exit\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_cp_diff = {
+    { "  3: IntConstant [9]\n",   "  3: IntConstant\n" },
+    { "  5: IntConstant [9]\n",   "  5: IntConstant\n" },
+    { "  11: IntConstant [17]\n", "  11: IntConstant\n" },
+    { "  13: IntConstant [17]\n", "  13: IntConstant\n" },
+    { "  9: Add(3, 5) [21]\n",    "  28: IntConstant\n" },
+    { "  17: Add(11, 13) [21]\n", "  29: IntConstant\n" },
+    { "  21: Add(9, 17) [24]\n",  "  30: IntConstant [24]\n" },
+    { "  24: Return(21)\n",       "  24: Return(30)\n" }
+  };
+  std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  3: IntConstant\n",  removed },
+    { "  5: IntConstant\n",  removed },
+    { "  11: IntConstant\n", removed },
+    { "  13: IntConstant\n", removed },
+    { "  28: IntConstant\n", removed },
+    { "  29: IntConstant\n", removed }
+  };
+  std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+  TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Tiny three-register program exercising int constant folding on subtraction.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v0 <- 3                  0.      const/4 v0, #+3
+ *     v1 <- 2                  1.      const/4 v1, #+2
+ *     v2 <- v0 - v1            2.      sub-int v2, v0, v1
+ *     return v2                4.      return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) {
+  const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 << 8 | 3 << 12,
+    Instruction::CONST_4 | 1 << 8 | 2 << 12,
+    Instruction::SUB_INT | 2 << 8, 0 | 1 << 8,
+    Instruction::RETURN | 2 << 8);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  3: IntConstant [9]\n"
+    "  5: IntConstant [9]\n"
+    "  14: SuspendCheck\n"
+    "  15: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 2\n"
+    "  9: Sub(3, 5) [12]\n"
+    "  12: Return(9)\n"
+    "BasicBlock 2, pred: 1\n"
+    "  13: Exit\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_cp_diff = {
+    { "  3: IntConstant [9]\n", "  3: IntConstant\n" },
+    { "  5: IntConstant [9]\n", "  5: IntConstant\n" },
+    { "  9: Sub(3, 5) [12]\n",  "  16: IntConstant [12]\n" },
+    { "  12: Return(9)\n",      "  12: Return(16)\n" }
+  };
+  std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  3: IntConstant\n", removed },
+    { "  5: IntConstant\n", removed }
+  };
+  std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+  TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+#define SIX_REGISTERS_CODE_ITEM(...)                                     \
+    { 6, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
+
+/**
+ * Tiny three-register-pair program exercising long constant folding
+ * on addition.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     (v0, v1) <- 1            0.      const-wide/16 v0, #+1
+ *     (v2, v3) <- 2            2.      const-wide/16 v2, #+2
+ *     (v4, v5) <-
+ *       (v0, v1) + (v1, v2)    4.      add-long v4, v0, v2
+ *     return (v4, v5)          6.      return-wide v4
+ */
+TEST(ConstantPropagation, LongConstantFoldingOnAddition) {
+  const uint16_t data[] = SIX_REGISTERS_CODE_ITEM(
+    Instruction::CONST_WIDE_16 | 0 << 8, 1,
+    Instruction::CONST_WIDE_16 | 2 << 8, 2,
+    Instruction::ADD_LONG | 4 << 8, 0 | 2 << 8,
+    Instruction::RETURN_WIDE | 4 << 8);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  6: LongConstant [12]\n"
+    "  8: LongConstant [12]\n"
+    "  17: SuspendCheck\n"
+    "  18: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 2\n"
+    "  12: Add(6, 8) [15]\n"
+    "  15: Return(12)\n"
+    "BasicBlock 2, pred: 1\n"
+    "  16: Exit\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_cp_diff = {
+    { "  6: LongConstant [12]\n", "  6: LongConstant\n" },
+    { "  8: LongConstant [12]\n", "  8: LongConstant\n" },
+    { "  12: Add(6, 8) [15]\n",   "  19: LongConstant [15]\n" },
+    { "  15: Return(12)\n",       "  15: Return(19)\n" }
+  };
+  std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  6: LongConstant\n", removed },
+    { "  8: LongConstant\n", removed }
+  };
+  std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+  TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Tiny three-register-pair program exercising long constant folding
+ * on subtraction.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     (v0, v1) <- 3            0.      const-wide/16 v0, #+3
+ *     (v2, v3) <- 2            2.      const-wide/16 v2, #+2
+ *     (v4, v5) <-
+ *       (v0, v1) - (v1, v2)    4.      sub-long v4, v0, v2
+ *     return (v4, v5)          6.      return-wide v4
+ */
+TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) {
+  const uint16_t data[] = SIX_REGISTERS_CODE_ITEM(
+    Instruction::CONST_WIDE_16 | 0 << 8, 3,
+    Instruction::CONST_WIDE_16 | 2 << 8, 2,
+    Instruction::SUB_LONG | 4 << 8, 0 | 2 << 8,
+    Instruction::RETURN_WIDE | 4 << 8);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  6: LongConstant [12]\n"
+    "  8: LongConstant [12]\n"
+    "  17: SuspendCheck\n"
+    "  18: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 2\n"
+    "  12: Sub(6, 8) [15]\n"
+    "  15: Return(12)\n"
+    "BasicBlock 2, pred: 1\n"
+    "  16: Exit\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_cp_diff = {
+    { "  6: LongConstant [12]\n", "  6: LongConstant\n" },
+    { "  8: LongConstant [12]\n", "  8: LongConstant\n" },
+    { "  12: Sub(6, 8) [15]\n",   "  19: LongConstant [15]\n" },
+    { "  15: Return(12)\n",       "  15: Return(19)\n" }
+  };
+  std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  6: LongConstant\n", removed },
+    { "  8: LongConstant\n", removed }
+  };
+  std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+  TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Three-register program with jumps leading to the creation of many
+ * blocks.
+ *
+ * The intent of this test is to ensure that all constant expressions
+ * are actually evaluated at compile-time, thanks to the reverse
+ * (forward) post-order traversal of the the dominator tree.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v0 <- 0                   0.     const/4 v0, #+0
+ *     v1 <- 1                   1.     const/4 v1, #+1
+ *     v2 <- v0 + v1             2.     add-int v2, v0, v1
+ *     goto L2                   4.     goto +4
+ * L1: v1 <- v0 + 3              5.     add-int/lit16 v1, v0, #+3
+ *     goto L3                   7.     goto +4
+ * L2: v0 <- v2 + 2              8.     add-int/lit16 v0, v2, #+2
+ *     goto L1                  10.     goto +(-5)
+ * L3: v2 <- v1 + 4             11.     add-int/lit16 v2, v1, #+4
+ *     return v2                13.     return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingAndJumps) {
+  const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 << 8 | 0 << 12,
+    Instruction::CONST_4 | 1 << 8 | 1 << 12,
+    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+    Instruction::GOTO | 4 << 8,
+    Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 3,
+    Instruction::GOTO | 4 << 8,
+    Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 2,
+    static_cast<uint16_t>(Instruction::GOTO | -5 << 8),
+    Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 4,
+    Instruction::RETURN | 2 << 8);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  3: IntConstant [9]\n"
+    "  5: IntConstant [9]\n"
+    "  13: IntConstant [14]\n"
+    "  18: IntConstant [19]\n"
+    "  24: IntConstant [25]\n"
+    "  30: SuspendCheck\n"
+    "  31: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 3\n"
+    "  9: Add(3, 5) [19]\n"
+    "  11: Goto 3\n"
+    "BasicBlock 2, pred: 3, succ: 4\n"
+    "  14: Add(19, 13) [25]\n"
+    "  16: Goto 4\n"
+    "BasicBlock 3, pred: 1, succ: 2\n"
+    "  19: Add(9, 18) [14]\n"
+    "  21: SuspendCheck\n"
+    "  22: Goto 2\n"
+    "BasicBlock 4, pred: 2, succ: 5\n"
+    "  25: Add(14, 24) [28]\n"
+    "  28: Return(25)\n"
+    "BasicBlock 5, pred: 4\n"
+    "  29: Exit\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_cp_diff = {
+    { "  3: IntConstant [9]\n",   "  3: IntConstant\n" },
+    { "  5: IntConstant [9]\n",   "  5: IntConstant []\n" },
+    { "  13: IntConstant [14]\n", "  13: IntConstant\n" },
+    { "  18: IntConstant [19]\n", "  18: IntConstant\n" },
+    { "  24: IntConstant [25]\n", "  24: IntConstant\n" },
+    { "  9: Add(3, 5) [19]\n",    "  32: IntConstant []\n" },
+    { "  14: Add(19, 13) [25]\n", "  34: IntConstant\n" },
+    { "  19: Add(9, 18) [14]\n",  "  33: IntConstant []\n" },
+    { "  25: Add(14, 24) [28]\n", "  35: IntConstant [28]\n" },
+    { "  28: Return(25)\n",       "  28: Return(35)\n"}
+  };
+  std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  3: IntConstant\n",     removed },
+    { "  13: IntConstant\n",    removed },
+    { "  18: IntConstant\n",    removed },
+    { "  24: IntConstant\n",    removed },
+    { "  34: IntConstant\n",    removed },
+  };
+  std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+  TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+
+/**
+ * Three-register program with a constant (static) condition.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v1 <- 1                  0.      const/4 v1, #+1
+ *     v0 <- 0                  1.      const/4 v0, #+0
+ *     if v1 >= 0 goto L1       2.      if-gez v1, +3
+ *     v0 <- v1                 4.      move v0, v1
+ * L1: v2 <- v0 + v1            5.      add-int v2, v0, v1
+ *     return-void              7.      return
+ */
+TEST(ConstantPropagation, ConstantCondition) {
+  const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 1 << 8 | 1 << 12,
+    Instruction::CONST_4 | 0 << 8 | 0 << 12,
+    Instruction::IF_GEZ | 1 << 8, 3,
+    Instruction::MOVE | 0 << 8 | 1 << 12,
+    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+    Instruction::RETURN_VOID);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  3: IntConstant [15, 22, 8]\n"
+    "  5: IntConstant [22, 8]\n"
+    "  19: SuspendCheck\n"
+    "  20: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 5, 2\n"
+    "  8: GreaterThanOrEqual(3, 5) [9]\n"
+    "  9: If(8)\n"
+    "BasicBlock 2, pred: 1, succ: 3\n"
+    "  12: Goto 3\n"
+    "BasicBlock 3, pred: 2, 5, succ: 4\n"
+    "  22: Phi(3, 5) [15]\n"
+    "  15: Add(22, 3)\n"
+    "  17: ReturnVoid\n"
+    "BasicBlock 4, pred: 3\n"
+    "  18: Exit\n"
+    "BasicBlock 5, pred: 1, succ: 3\n"
+    "  21: Goto 3\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_cp_diff = {
+    { "  3: IntConstant [15, 22, 8]\n",      "  3: IntConstant [15, 22]\n" },
+    { "  5: IntConstant [22, 8]\n",          "  5: IntConstant [22]\n" },
+    { "  8: GreaterThanOrEqual(3, 5) [9]\n", "  23: IntConstant [9]\n" },
+    { "  9: If(8)\n",                        "  9: If(23)\n" }
+  };
+  std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+  // Expected difference after dead code elimination.
+  diff_t expected_dce_diff = {
+    { "  3: IntConstant [15, 22]\n", "  3: IntConstant [22]\n" },
+    { "  22: Phi(3, 5) [15]\n",      "  22: Phi(3, 5)\n" },
+    { "  15: Add(22, 3)\n",          removed }
+  };
+  std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+  TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
new file mode 100644
index 0000000..fe2adc7
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dead_code_elimination.h"
+
+#include "base/bit_vector-inl.h"
+
+namespace art {
+
+void DeadCodeElimination::Run() {
+  // Process basic blocks in post-order in the dominator tree, so that
+  // a dead instruction depending on another dead instruction is
+  // removed.
+  for (HPostOrderIterator b(*graph_); !b.Done(); b.Advance()) {
+    HBasicBlock* block = b.Current();
+    // Traverse this block's instructions in backward order and remove
+    // the unused ones.
+    HBackwardInstructionIterator i(block->GetInstructions());
+    // Skip the first iteration, as the last instruction of a block is
+    // a branching instruction.
+    DCHECK(i.Current()->IsControlFlow());
+    for (i.Advance(); !i.Done(); i.Advance()) {
+      HInstruction* inst = i.Current();
+      DCHECK(!inst->IsControlFlow());
+      if (!inst->HasSideEffects() && !inst->HasUses() && !inst->IsSuspendCheck()) {
+        block->RemoveInstruction(inst);
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h
new file mode 100644
index 0000000..48739be
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+#define ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * Optimization pass performing dead code elimination (removal of
+ * unused variables/instructions) on the SSA form.
+ */
+class DeadCodeElimination : public ValueObject {
+ public:
+  explicit DeadCodeElimination(HGraph* graph)
+      : graph_(graph) {}
+
+  void Run();
+
+ private:
+  HGraph* const graph_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
new file mode 100644
index 0000000..245bcb2
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dead_code_elimination.h"
+#include "pretty_printer.h"
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void TestCode(const uint16_t* data,
+                     const std::string& expected_before,
+                     const std::string& expected_after) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = CreateCFG(&allocator, data);
+  ASSERT_NE(graph, nullptr);
+
+  graph->BuildDominatorTree();
+  graph->TransformToSSA();
+
+  StringPrettyPrinter printer_before(graph);
+  printer_before.VisitInsertionOrder();
+  std::string actual_before = printer_before.str();
+  ASSERT_EQ(actual_before, expected_before);
+
+  DeadCodeElimination(graph).Run();
+
+  StringPrettyPrinter printer_after(graph);
+  printer_after.VisitInsertionOrder();
+  std::string actual_after = printer_after.str();
+  ASSERT_EQ(actual_after, expected_after);
+
+  SSAChecker ssa_checker(&allocator, graph);
+  ssa_checker.VisitInsertionOrder();
+  ASSERT_TRUE(ssa_checker.IsValid());
+}
+
+
+/**
+ * Small three-register program.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v1 <- 1                  0.      const/4 v1, #+1
+ *     v0 <- 0                  1.      const/4 v0, #+0
+ *     if v1 >= 0 goto L1       2.      if-gez v1, +3
+ *     v0 <- v1                 4.      move v0, v1
+ * L1: v2 <- v0 + v1            5.      add-int v2, v0, v1
+ *     return-void              7.      return
+ */
+TEST(DeadCodeElimination, AdditionAndConditionalJump) {
+  const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 1 << 8 | 1 << 12,
+    Instruction::CONST_4 | 0 << 8 | 0 << 12,
+    Instruction::IF_GEZ | 1 << 8, 3,
+    Instruction::MOVE | 0 << 8 | 1 << 12,
+    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+    Instruction::RETURN_VOID);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  3: IntConstant [15, 22, 8]\n"
+    "  5: IntConstant [22, 8]\n"
+    "  19: SuspendCheck\n"
+    "  20: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 5, 2\n"
+    "  8: GreaterThanOrEqual(3, 5) [9]\n"
+    "  9: If(8)\n"
+    "BasicBlock 2, pred: 1, succ: 3\n"
+    "  12: Goto 3\n"
+    "BasicBlock 3, pred: 2, 5, succ: 4\n"
+    "  22: Phi(3, 5) [15]\n"
+    "  15: Add(22, 3)\n"
+    "  17: ReturnVoid\n"
+    "BasicBlock 4, pred: 3\n"
+    "  18: Exit\n"
+    "BasicBlock 5, pred: 1, succ: 3\n"
+    "  21: Goto 3\n";
+
+  diff_t expected_diff = {
+    { "  3: IntConstant [15, 22, 8]\n", "  3: IntConstant [22, 8]\n" },
+    { "  22: Phi(3, 5) [15]\n",         "  22: Phi(3, 5)\n" },
+    { "  15: Add(22, 3)\n",             removed }
+  };
+  std::string expected_after = Patch(expected_before, expected_diff);
+
+  TestCode(data, expected_before, expected_after);
+}
+
+/**
+ * Three-register program with jumps leading to the creation of many
+ * blocks.
+ *
+ * The intent of this test is to ensure that all dead instructions are
+ * actually pruned at compile-time, thanks to the (backward)
+ * post-order traversal of the the dominator tree.
+ *
+ *                              16-bit
+ *                              offset
+ *                              ------
+ *     v0 <- 0                   0.     const/4 v0, #+0
+ *     v1 <- 1                   1.     const/4 v1, #+1
+ *     v2 <- v0 + v1             2.     add-int v2, v0, v1
+ *     goto L2                   4.     goto +4
+ * L1: v1 <- v0 + 3              5.     add-int/lit16 v1, v0, #+3
+ *     goto L3                   7.     goto +4
+ * L2: v0 <- v2 + 2              8.     add-int/lit16 v0, v2, #+2
+ *     goto L1                  10.     goto +(-5)
+ * L3: v2 <- v1 + 4             11.     add-int/lit16 v2, v1, #+4
+ *     return                   13.     return-void
+ */
+TEST(DeadCodeElimination, AdditionsAndInconditionalJumps) {
+  const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 << 8 | 0 << 12,
+    Instruction::CONST_4 | 1 << 8 | 1 << 12,
+    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+    Instruction::GOTO | 4 << 8,
+    Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 3,
+    Instruction::GOTO | 4 << 8,
+    Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 2,
+    static_cast<uint16_t>(Instruction::GOTO | -5 << 8),
+    Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 4,
+    Instruction::RETURN_VOID);
+
+  std::string expected_before =
+    "BasicBlock 0, succ: 1\n"
+    "  3: IntConstant [9]\n"
+    "  5: IntConstant [9]\n"
+    "  13: IntConstant [14]\n"
+    "  18: IntConstant [19]\n"
+    "  24: IntConstant [25]\n"
+    "  29: SuspendCheck\n"
+    "  30: Goto 1\n"
+    "BasicBlock 1, pred: 0, succ: 3\n"
+    "  9: Add(3, 5) [19]\n"
+    "  11: Goto 3\n"
+    "BasicBlock 2, pred: 3, succ: 4\n"
+    "  14: Add(19, 13) [25]\n"
+    "  16: Goto 4\n"
+    "BasicBlock 3, pred: 1, succ: 2\n"
+    "  19: Add(9, 18) [14]\n"
+    "  21: SuspendCheck\n"
+    "  22: Goto 2\n"
+    "BasicBlock 4, pred: 2, succ: 5\n"
+    "  25: Add(14, 24)\n"
+    "  27: ReturnVoid\n"
+    "BasicBlock 5, pred: 4\n"
+    "  28: Exit\n";
+
+  // Expected difference after constant propagation.
+  diff_t expected_diff = {
+    { "  13: IntConstant [14]\n", removed },
+    { "  24: IntConstant [25]\n", removed },
+    { "  14: Add(19, 13) [25]\n", removed },
+    // The SuspendCheck instruction following this Add instruction
+    // inserts the latter in an environment, thus making it "used" and
+    // therefore non removable.  It ensues that some other Add and
+    // IntConstant instructions cannot be removed, as they are direct
+    // or indirect inputs of the initial Add instruction.
+    { "  19: Add(9, 18) [14]\n",  "  19: Add(9, 18) []\n" },
+    { "  25: Add(14, 24)\n",      removed },
+  };
+  std::string expected_after = Patch(expected_before, expected_diff);
+
+  TestCode(data, expected_before, expected_after);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index fab9f7a..c36b143 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -27,9 +27,8 @@
 
 namespace art {
 
-static HGraph* TestCode(const uint16_t* data, ArenaPool* pool) {
-  ArenaAllocator allocator(pool);
-  HGraphBuilder builder(&allocator);
+static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) {
+  HGraphBuilder builder(allocator);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
   HGraph* graph = builder.BuildGraph(*item);
   graph->BuildDominatorTree();
@@ -44,7 +43,8 @@
     Instruction::RETURN_VOID);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
   for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
     ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
   }
@@ -56,7 +56,8 @@
     Instruction::RETURN);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
   for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
     ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
   }
@@ -71,7 +72,8 @@
     Instruction::RETURN);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
   for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
     ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
   }
@@ -87,7 +89,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
   for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
     ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
   }
@@ -101,7 +104,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
   for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
     ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
   }
@@ -146,7 +150,8 @@
     Instruction::RETURN_VOID);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // pre header
@@ -173,7 +178,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // goto block
@@ -197,7 +203,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // goto block
@@ -222,7 +229,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // pre header
@@ -248,7 +256,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // pre header
@@ -271,9 +280,9 @@
     Instruction::GOTO | 0xFB00,
     Instruction::RETURN | 0 << 8);
 
-
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // pre header of outer loop
@@ -302,9 +311,9 @@
     Instruction::GOTO | 0xFE00,  // second loop
     Instruction::RETURN | 0 << 8);
 
-
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // pre header of first loop
@@ -333,7 +342,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
   ASSERT_TRUE(graph->GetBlocks().Get(3)->IsLoopHeader());
   HLoopInformation* info = graph->GetBlocks().Get(3)->GetLoopInformation();
   ASSERT_FALSE(info->GetHeader()->Dominates(info->GetBackEdges().Get(0)));
@@ -347,7 +357,8 @@
     Instruction::RETURN | 0 << 8);
 
   ArenaPool arena;
-  HGraph* graph = TestCode(data, &arena);
+  ArenaAllocator allocator(&arena);
+  HGraph* graph = TestCode(data, &allocator);
 
   TestBlock(graph, 0, false, -1);            // entry block
   TestBlock(graph, 1, false, -1);            // pre header of first loop
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
new file mode 100644
index 0000000..589b44a
--- /dev/null
+++ b/compiler/optimizing/graph_checker.cc
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graph_checker.h"
+
+#include <string>
+#include <map>
+#include <sstream>
+
+#include "base/bit_vector-inl.h"
+
+namespace art {
+
+void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
+  current_block_ = block;
+
+  // Check consistency with respect to predecessors of `block`.
+  const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
+  std::map<HBasicBlock*, size_t> predecessors_count;
+  for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
+    HBasicBlock* p = predecessors.Get(i);
+    ++predecessors_count[p];
+  }
+  for (auto& pc : predecessors_count) {
+    HBasicBlock* p = pc.first;
+    size_t p_count_in_block_predecessors = pc.second;
+    const GrowableArray<HBasicBlock*>& p_successors = p->GetSuccessors();
+    size_t block_count_in_p_successors = 0;
+    for (size_t j = 0, f = p_successors.Size(); j < f; ++j) {
+      if (p_successors.Get(j) == block) {
+        ++block_count_in_p_successors;
+      }
+    }
+    if (p_count_in_block_predecessors != block_count_in_p_successors) {
+      std::stringstream error;
+      error << "Block " << block->GetBlockId()
+            << " lists " << p_count_in_block_predecessors
+            << " occurrences of block " << p->GetBlockId()
+            << " in its predecessors, whereas block " << p->GetBlockId()
+            << " lists " << block_count_in_p_successors
+            << " occurrences of block " << block->GetBlockId()
+            << " in its successors.";
+      errors_.Insert(error.str());
+    }
+  }
+
+  // Check consistency with respect to successors of `block`.
+  const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
+  std::map<HBasicBlock*, size_t> successors_count;
+  for (size_t i = 0, e = successors.Size(); i < e; ++i) {
+    HBasicBlock* s = successors.Get(i);
+    ++successors_count[s];
+  }
+  for (auto& sc : successors_count) {
+    HBasicBlock* s = sc.first;
+    size_t s_count_in_block_successors = sc.second;
+    const GrowableArray<HBasicBlock*>& s_predecessors = s->GetPredecessors();
+    size_t block_count_in_s_predecessors = 0;
+    for (size_t j = 0, f = s_predecessors.Size(); j < f; ++j) {
+      if (s_predecessors.Get(j) == block) {
+        ++block_count_in_s_predecessors;
+      }
+    }
+    if (s_count_in_block_successors != block_count_in_s_predecessors) {
+      std::stringstream error;
+      error << "Block " << block->GetBlockId()
+            << " lists " << s_count_in_block_successors
+            << " occurrences of block " << s->GetBlockId()
+            << " in its successors, whereas block " << s->GetBlockId()
+            << " lists " << block_count_in_s_predecessors
+            << " occurrences of block " << block->GetBlockId()
+            << " in its predecessors.";
+      errors_.Insert(error.str());
+    }
+  }
+
+  // Ensure `block` ends with a branch instruction.
+  HInstruction* last_inst = block->GetLastInstruction();
+  if (last_inst == nullptr || !last_inst->IsControlFlow()) {
+    std::stringstream error;
+    error  << "Block " << block->GetBlockId()
+           << " does not end with a branch instruction.";
+    errors_.Insert(error.str());
+  }
+
+  // Visit this block's list of phis.
+  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+    // Ensure this block's list of phis contains only phis.
+    if (!it.Current()->IsPhi()) {
+      std::stringstream error;
+      error << "Block " << current_block_->GetBlockId()
+            << " has a non-phi in its phi list.";
+      errors_.Insert(error.str());
+    }
+    it.Current()->Accept(this);
+  }
+
+  // Visit this block's list of instructions.
+  for (HInstructionIterator it(block->GetInstructions()); !it.Done();
+       it.Advance()) {
+    // Ensure this block's list of instructions does not contains phis.
+    if (it.Current()->IsPhi()) {
+      std::stringstream error;
+      error << "Block " << current_block_->GetBlockId()
+            << " has a phi in its non-phi list.";
+      errors_.Insert(error.str());
+    }
+    it.Current()->Accept(this);
+  }
+}
+
+void GraphChecker::VisitInstruction(HInstruction* instruction) {
+  // Ensure `instruction` is associated with `current_block_`.
+  if (instruction->GetBlock() != current_block_) {
+    std::stringstream error;
+    if (instruction->IsPhi()) {
+      error << "Phi ";
+    } else {
+      error << "Instruction ";
+    }
+    error << instruction->GetId() << " in block "
+          << current_block_->GetBlockId();
+    if (instruction->GetBlock() != nullptr) {
+      error << " associated with block "
+            << instruction->GetBlock()->GetBlockId() << ".";
+    } else {
+      error << " not associated with any block.";
+    }
+    errors_.Insert(error.str());
+  }
+
+  // Ensure the inputs of `instruction` are defined in a block of the graph.
+  for (HInputIterator input_it(instruction); !input_it.Done();
+       input_it.Advance()) {
+    HInstruction* input = input_it.Current();
+    const HInstructionList& list = input->IsPhi()
+        ? input->GetBlock()->GetPhis()
+        : input->GetBlock()->GetInstructions();
+    if (!list.Contains(input)) {
+      std::stringstream error;
+      error << "Input " << input->GetId()
+            << " of instruction " << instruction->GetId()
+            << " is not defined in a basic block of the control-flow graph.";
+      errors_.Insert(error.str());
+    }
+  }
+
+  // Ensure the uses of `instruction` are defined in a block of the graph.
+  for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+       !use_it.Done(); use_it.Advance()) {
+    HInstruction* use = use_it.Current()->GetUser();
+    const HInstructionList& list = use->IsPhi()
+        ? use->GetBlock()->GetPhis()
+        : use->GetBlock()->GetInstructions();
+    if (!list.Contains(use)) {
+      std::stringstream error;
+      error << "User " << use->GetId()
+            << " of instruction " << instruction->GetId()
+            << " is not defined in a basic block of the control-flow graph.";
+      errors_.Insert(error.str());
+    }
+  }
+}
+
+void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
+  super_type::VisitBasicBlock(block);
+
+  // Ensure there is no critical edge (i.e., an edge connecting a
+  // block with multiple successors to a block with multiple
+  // predecessors).
+  if (block->GetSuccessors().Size() > 1) {
+    for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
+      HBasicBlock* successor = block->GetSuccessors().Get(j);
+      if (successor->GetPredecessors().Size() > 1) {
+        std::stringstream error;
+        error << "Critical edge between blocks " << block->GetBlockId()
+              << " and "  << successor->GetBlockId() << ".";
+        errors_.Insert(error.str());
+      }
+    }
+  }
+
+  if (block->IsLoopHeader()) {
+    CheckLoop(block);
+  }
+}
+
+void SSAChecker::CheckLoop(HBasicBlock* loop_header) {
+  int id = loop_header->GetBlockId();
+
+  // Ensure the pre-header block is first in the list of
+  // predecessors of a loop header.
+  if (!loop_header->IsLoopPreHeaderFirstPredecessor()) {
+    std::stringstream error;
+    error << "Loop pre-header is not the first predecessor of the loop header "
+          << id << ".";
+    errors_.Insert(error.str());
+  }
+
+  // Ensure the loop header has only two predecessors and that only the
+  // second one is a back edge.
+  if (loop_header->GetPredecessors().Size() < 2) {
+    std::stringstream error;
+    error << "Loop header " << id << " has less than two predecessors.";
+    errors_.Insert(error.str());
+  } else if (loop_header->GetPredecessors().Size() > 2) {
+    std::stringstream error;
+    error << "Loop header " << id << " has more than two predecessors.";
+    errors_.Insert(error.str());
+  } else {
+    HLoopInformation* loop_information = loop_header->GetLoopInformation();
+    HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
+    if (loop_information->IsBackEdge(first_predecessor)) {
+      std::stringstream error;
+      error << "First predecessor of loop header " << id << " is a back edge.";
+      errors_.Insert(error.str());
+    }
+    HBasicBlock* second_predecessor = loop_header->GetPredecessors().Get(1);
+    if (!loop_information->IsBackEdge(second_predecessor)) {
+      std::stringstream error;
+      error << "Second predecessor of loop header " << id
+            << " is not a back edge.";
+      errors_.Insert(error.str());
+    }
+  }
+
+  // Ensure there is only one back edge per loop.
+  size_t num_back_edges =
+    loop_header->GetLoopInformation()->GetBackEdges().Size();
+  if (num_back_edges != 1) {
+      std::stringstream error;
+      error << "Loop defined by header " << id << " has "
+            << num_back_edges << " back edge(s).";
+      errors_.Insert(error.str());
+  }
+
+  // Ensure all blocks in the loop are dominated by the loop header.
+  const ArenaBitVector& loop_blocks =
+    loop_header->GetLoopInformation()->GetBlocks();
+  for (uint32_t i : loop_blocks.Indexes()) {
+    HBasicBlock* loop_block = GetGraph()->GetBlocks().Get(i);
+    if (!loop_header->Dominates(loop_block)) {
+      std::stringstream error;
+      error << "Loop block " << loop_block->GetBlockId()
+            << " not dominated by loop header " << id;
+      errors_.Insert(error.str());
+    }
+  }
+}
+
+void SSAChecker::VisitInstruction(HInstruction* instruction) {
+  super_type::VisitInstruction(instruction);
+
+  // Ensure an instruction dominates all its uses (or in the present
+  // case, that all uses of an instruction (used as input) are
+  // dominated by its definition).
+  for (HInputIterator input_it(instruction); !input_it.Done();
+       input_it.Advance()) {
+    HInstruction* input = input_it.Current();
+    if (!input->Dominates(instruction)) {
+      std::stringstream error;
+      error << "Instruction " << input->GetId()
+            << " in block " << input->GetBlock()->GetBlockId()
+            << " does not dominate use " << instruction->GetId()
+            << " in block " << current_block_->GetBlockId() << ".";
+      errors_.Insert(error.str());
+    }
+  }
+}
+
+void SSAChecker::VisitPhi(HPhi* phi) {
+  VisitInstruction(phi);
+
+  // Ensure the first input of a phi is not itself.
+  if (phi->InputAt(0) == phi) {
+      std::stringstream error;
+      error << "Loop phi " << phi->GetId()
+            << " in block " << phi->GetBlock()->GetBlockId()
+            << " is its own first input.";
+      errors_.Insert(error.str());
+  }
+
+  // Ensure the number of phi inputs is the same as the number of
+  // its predecessors.
+  const GrowableArray<HBasicBlock*>& predecessors =
+    phi->GetBlock()->GetPredecessors();
+  if (phi->InputCount() != predecessors.Size()) {
+    std::stringstream error;
+    error << "Phi " << phi->GetId()
+          << " in block " << phi->GetBlock()->GetBlockId()
+          << " has " << phi->InputCount() << " inputs, but block "
+          << phi->GetBlock()->GetBlockId() << " has "
+          << predecessors.Size() << " predecessors.";
+    errors_.Insert(error.str());
+  } else {
+    // Ensure phi input at index I either comes from the Ith
+    // predecessor or from a block that dominates this predecessor.
+    for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+      HInstruction* input = phi->InputAt(i);
+      HBasicBlock* predecessor = predecessors.Get(i);
+      if (!(input->GetBlock() == predecessor
+            || input->GetBlock()->Dominates(predecessor))) {
+        std::stringstream error;
+        error << "Input " << input->GetId() << " at index " << i
+              << " of phi " << phi->GetId()
+              << " from block " << phi->GetBlock()->GetBlockId()
+              << " is not defined in predecessor number " << i
+              << " nor in a block dominating it.";
+        errors_.Insert(error.str());
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
new file mode 100644
index 0000000..34a770b
--- /dev/null
+++ b/compiler/optimizing/graph_checker.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_
+#define ART_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_
+
+#include "nodes.h"
+
+namespace art {
+
+// A control-flow graph visitor performing various checks.
+class GraphChecker : public HGraphVisitor {
+ public:
+  GraphChecker(ArenaAllocator* allocator, HGraph* graph)
+    : HGraphVisitor(graph),
+      allocator_(allocator),
+      errors_(allocator, 0) {}
+
+  // Check `block`.
+  virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+  // Check `instruction`.
+  virtual void VisitInstruction(HInstruction* instruction) OVERRIDE;
+
+  // Was the last visit of the graph valid?
+  bool IsValid() const {
+    return errors_.IsEmpty();
+  }
+
+  // Get the list of detected errors.
+  const GrowableArray<std::string>& GetErrors() const {
+    return errors_;
+  }
+
+ protected:
+  ArenaAllocator* const allocator_;
+  // The block currently visited.
+  HBasicBlock* current_block_ = nullptr;
+  // Errors encountered while checking the graph.
+  GrowableArray<std::string> errors_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GraphChecker);
+};
+
+
+// An SSA graph visitor performing various checks.
+class SSAChecker : public GraphChecker {
+ public:
+  typedef GraphChecker super_type;
+
+  SSAChecker(ArenaAllocator* allocator, HGraph* graph)
+    : GraphChecker(allocator, graph) {}
+
+  // Perform SSA form checks on `block`.
+  virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+  // Loop-related checks from block `loop_header`.
+  void CheckLoop(HBasicBlock* loop_header);
+
+  // Perform SSA form checks on instructions.
+  virtual void VisitInstruction(HInstruction* instruction) OVERRIDE;
+  virtual void VisitPhi(HPhi* phi) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SSAChecker);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
new file mode 100644
index 0000000..ea06920
--- /dev/null
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+/**
+ * Create a simple control-flow graph composed of two blocks:
+ *
+ *   BasicBlock 0, succ: 1
+ *     0: Goto 1
+ *   BasicBlock 1, pred: 0
+ *     1: Exit
+ */
+HGraph* CreateSimpleCFG(ArenaAllocator* allocator) {
+  HGraph* graph = new (allocator) HGraph(allocator);
+  HBasicBlock* entry_block = new (allocator) HBasicBlock(graph);
+  entry_block->AddInstruction(new (allocator) HGoto());
+  graph->AddBlock(entry_block);
+  graph->SetEntryBlock(entry_block);
+  HBasicBlock* exit_block = new (allocator) HBasicBlock(graph);
+  exit_block->AddInstruction(new (allocator) HExit());
+  graph->AddBlock(exit_block);
+  graph->SetExitBlock(exit_block);
+  entry_block->AddSuccessor(exit_block);
+  return graph;
+}
+
+
+static void TestCode(const uint16_t* data) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = CreateCFG(&allocator, data);
+  ASSERT_NE(graph, nullptr);
+
+  GraphChecker graph_checker(&allocator, graph);
+  graph_checker.VisitInsertionOrder();
+  ASSERT_TRUE(graph_checker.IsValid());
+}
+
+static void TestCodeSSA(const uint16_t* data) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = CreateCFG(&allocator, data);
+  ASSERT_NE(graph, nullptr);
+
+  graph->BuildDominatorTree();
+  graph->TransformToSSA();
+
+  SSAChecker ssa_checker(&allocator, graph);
+  ssa_checker.VisitInsertionOrder();
+  ASSERT_TRUE(ssa_checker.IsValid());
+}
+
+
+TEST(GraphChecker, ReturnVoid) {
+  const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+      Instruction::RETURN_VOID);
+
+  TestCode(data);
+}
+
+TEST(GraphChecker, CFG1) {
+  const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+      Instruction::GOTO | 0x100,
+      Instruction::RETURN_VOID);
+
+  TestCode(data);
+}
+
+TEST(GraphChecker, CFG2) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_EQ, 3,
+    Instruction::GOTO | 0x100,
+    Instruction::RETURN_VOID);
+
+  TestCode(data);
+}
+
+TEST(GraphChecker, CFG3) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_EQ, 3,
+    Instruction::GOTO | 0x100,
+    Instruction::GOTO | 0xFF00);
+
+  TestCode(data);
+}
+
+// Test case with an invalid graph containing inconsistent
+// predecessor/successor arcs in CFG.
+TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = CreateSimpleCFG(&allocator);
+  GraphChecker graph_checker(&allocator, graph);
+  graph_checker.VisitInsertionOrder();
+  ASSERT_TRUE(graph_checker.IsValid());
+
+  // Remove the entry block from the exit block's predecessors, to create an
+  // inconsistent successor/predecessor relation.
+  graph->GetExitBlock()->RemovePredecessor(graph->GetEntryBlock());
+  graph_checker.VisitInsertionOrder();
+  ASSERT_FALSE(graph_checker.IsValid());
+}
+
+// Test case with an invalid graph containing a non-branch last
+// instruction in a block.
+TEST(GraphChecker, BlockEndingWithNonBranchInstruction) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = CreateSimpleCFG(&allocator);
+  GraphChecker graph_checker(&allocator, graph);
+  graph_checker.VisitInsertionOrder();
+  ASSERT_TRUE(graph_checker.IsValid());
+
+  // Remove the sole instruction of the exit block (composed of a
+  // single Exit instruction) to make it invalid (i.e. not ending by a
+  // branch instruction).
+  HBasicBlock* exit_block = graph->GetExitBlock();
+  HInstruction* last_inst = exit_block->GetLastInstruction();
+  exit_block->RemoveInstruction(last_inst);
+
+  graph_checker.VisitInsertionOrder();
+  ASSERT_FALSE(graph_checker.IsValid());
+}
+
+TEST(SSAChecker, SSAPhi) {
+  // This code creates one Phi function during the conversion to SSA form.
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_EQ, 3,
+    Instruction::CONST_4 | 4 << 12 | 0,
+    Instruction::RETURN | 0 << 8);
+
+  TestCodeSSA(data);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f011e85..0fb4737 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -81,6 +81,25 @@
     }
   }
 
+  char GetTypeId(Primitive::Type type) {
+    // Note that Primitive::Descriptor would not work for us
+    // because it does not handle reference types (that is kPrimNot).
+    switch (type) {
+      case Primitive::kPrimBoolean: return 'z';
+      case Primitive::kPrimByte: return 'b';
+      case Primitive::kPrimChar: return 'c';
+      case Primitive::kPrimShort: return 's';
+      case Primitive::kPrimInt: return 'i';
+      case Primitive::kPrimLong: return 'j';
+      case Primitive::kPrimFloat: return 'f';
+      case Primitive::kPrimDouble: return 'd';
+      case Primitive::kPrimNot: return 'l';
+      case Primitive::kPrimVoid: return 'v';
+    }
+    LOG(FATAL) << "Unreachable";
+    return 'v';
+  }
+
   void PrintPredecessors(HBasicBlock* block) {
     AddIndent();
     output_ << "predecessors";
@@ -110,6 +129,12 @@
       }
     } else if (location.IsConstant()) {
       output_ << "constant";
+      HConstant* constant = location.GetConstant();
+      if (constant->IsIntConstant()) {
+        output_ << " " << constant->AsIntConstant()->GetValue();
+      } else if (constant->IsLongConstant()) {
+        output_ << " " << constant->AsLongConstant()->GetValue();
+      }
     } else if (location.IsInvalid()) {
       output_ << "invalid";
     } else if (location.IsStackSlot()) {
@@ -140,7 +165,7 @@
     if (instruction->InputCount() > 0) {
       output_ << " [ ";
       for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
-        output_ << "v" << inputs.Current()->GetId() << " ";
+        output_ << GetTypeId(inputs.Current()->GetType()) << inputs.Current()->GetId() << " ";
       }
       output_ << "]";
     }
@@ -175,7 +200,8 @@
       HInstruction* instruction = it.Current();
       AddIndent();
       int bci = 0;
-      output_ << bci << " " << instruction->NumberOfUses() << " v" << instruction->GetId() << " ";
+      output_ << bci << " " << instruction->NumberOfUses()
+              << " " << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
       instruction->Accept(this);
       output_ << kEndInstructionMarker << std::endl;
     }
@@ -214,7 +240,8 @@
     for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
       AddIndent();
       HInstruction* instruction = it.Current();
-      output_ << instruction->GetId() << " v" << instruction->GetId() << "[ ";
+      output_ << instruction->GetId() << " " << GetTypeId(instruction->GetType())
+              << instruction->GetId() << "[ ";
       for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
         output_ << inputs.Current()->GetId() << " ";
       }
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 7cd74e9..6e2c6fd 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -25,8 +25,10 @@
 class DexCompilationUnit;
 class HGraph;
 
+// TODO: Create an analysis/optimization abstraction.
 static const char* kLivenessPassName = "liveness";
 static const char* kRegisterAllocatorPassName = "register";
+static const char* kGVNPassName = "gvn";
 
 /**
  * If enabled, emits compilation information suitable for the c1visualizer tool
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
new file mode 100644
index 0000000..027b3d4
--- /dev/null
+++ b/compiler/optimizing/gvn.cc
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gvn.h"
+
+namespace art {
+
+void GlobalValueNumberer::Run() {
+  ComputeSideEffects();
+
+  sets_.Put(graph_->GetEntryBlock()->GetBlockId(), new (allocator_) ValueSet(allocator_));
+
+  // Do reverse post order to ensure the non back-edge predecessors of a block are
+  // visited before the block itself.
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    VisitBasicBlock(it.Current());
+  }
+}
+
+void GlobalValueNumberer::UpdateLoopEffects(HLoopInformation* info, SideEffects effects) {
+  int id = info->GetHeader()->GetBlockId();
+  loop_effects_.Put(id, loop_effects_.Get(id).Union(effects));
+}
+
+void GlobalValueNumberer::ComputeSideEffects() {
+  if (kIsDebugBuild) {
+    for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+      HBasicBlock* block = it.Current();
+      SideEffects effects = GetBlockEffects(block);
+      DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+      if (block->IsLoopHeader()) {
+        effects = GetLoopEffects(block);
+        DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+      }
+    }
+  }
+
+  // Do a post order visit to ensure we visit a loop header after its loop body.
+  for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+
+    SideEffects effects = SideEffects::None();
+    // Update `effects` with the side effects of all instructions in this block.
+    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+      HInstruction* instruction = it.Current();
+      effects = effects.Union(instruction->GetSideEffects());
+      if (effects.HasAllSideEffects()) {
+        break;
+      }
+    }
+
+    block_effects_.Put(block->GetBlockId(), effects);
+
+    if (block->IsLoopHeader()) {
+      // The side effects of the loop header are part of the loop.
+      UpdateLoopEffects(block->GetLoopInformation(), effects);
+      HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
+      if (pre_header->IsInLoop()) {
+        // Update the side effects of the outer loop with the side effects of the inner loop.
+        // Note that this works because we know all the blocks of the inner loop are visited
+        // before the loop header of the outer loop.
+        UpdateLoopEffects(pre_header->GetLoopInformation(), GetLoopEffects(block));
+      }
+    } else if (block->IsInLoop()) {
+      // Update the side effects of the loop with the side effects of this block.
+      UpdateLoopEffects(block->GetLoopInformation(), effects);
+    }
+  }
+}
+
+SideEffects GlobalValueNumberer::GetLoopEffects(HBasicBlock* block) const {
+  DCHECK(block->IsLoopHeader());
+  return loop_effects_.Get(block->GetBlockId());
+}
+
+SideEffects GlobalValueNumberer::GetBlockEffects(HBasicBlock* block) const {
+  return block_effects_.Get(block->GetBlockId());
+}
+
+static bool IsLoopExit(HBasicBlock* block, HBasicBlock* successor) {
+  HLoopInformation* block_info = block->GetLoopInformation();
+  HLoopInformation* other_info = successor->GetLoopInformation();
+  return block_info != other_info && (other_info == nullptr || block_info->IsIn(*other_info));
+}
+
+void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
+  if (kIsDebugBuild) {
+    // Check that all non back-edge processors have been visited.
+    for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
+      HBasicBlock* predecessor = block->GetPredecessors().Get(i);
+      DCHECK(visited_.Get(predecessor->GetBlockId())
+             || (block->GetLoopInformation() != nullptr
+                 && (block->GetLoopInformation()->GetBackEdges().Get(0) == predecessor)));
+    }
+    visited_.Put(block->GetBlockId(), true);
+  }
+
+  ValueSet* set = sets_.Get(block->GetBlockId());
+
+  if (block->IsLoopHeader()) {
+    set->Kill(GetLoopEffects(block));
+  }
+
+  HInstruction* current = block->GetFirstInstruction();
+  while (current != nullptr) {
+    set->Kill(current->GetSideEffects());
+    // Save the next instruction in case `current` is removed from the graph.
+    HInstruction* next = current->GetNext();
+    if (current->CanBeMoved()) {
+      HInstruction* existing = set->Lookup(current);
+      if (existing != nullptr) {
+        current->ReplaceWith(existing);
+        current->GetBlock()->RemoveInstruction(current);
+      } else {
+        set->Add(current);
+      }
+    }
+    current = next;
+  }
+
+  if (block == graph_->GetEntryBlock()) {
+    // The entry block should only accumulate constant instructions, and
+    // the builder puts constants only in the entry block.
+    // Therefore, there is no need to propagate the value set to the next block.
+    DCHECK_EQ(block->GetDominatedBlocks().Size(), 1u);
+    HBasicBlock* dominated = block->GetDominatedBlocks().Get(0);
+    sets_.Put(dominated->GetBlockId(), new (allocator_) ValueSet(allocator_));
+    return;
+  }
+
+  // Copy the value set to dominated blocks. We can re-use
+  // the current set for the last dominated block because we are done visiting
+  // this block.
+  for (size_t i = 0, e = block->GetDominatedBlocks().Size(); i < e; ++i) {
+    HBasicBlock* dominated = block->GetDominatedBlocks().Get(i);
+    sets_.Put(dominated->GetBlockId(), i == e - 1 ? set : set->Copy());
+  }
+
+  // Kill instructions in the value set of each successor. If the successor
+  // is a loop exit, then we use the side effects of the loop. If not, we use
+  // the side effects of this block.
+  for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) {
+    HBasicBlock* successor = block->GetSuccessors().Get(i);
+    if (successor->IsLoopHeader()
+        && successor->GetLoopInformation()->GetBackEdges().Get(0) == block) {
+      // In case of a back edge, we already have visited the loop header.
+      // We should not update its value set, because the last dominated block
+      // of the loop header uses the same value set.
+      DCHECK(visited_.Get(successor->GetBlockId()));
+      continue;
+    }
+    DCHECK(!visited_.Get(successor->GetBlockId()));
+    ValueSet* successor_set = sets_.Get(successor->GetBlockId());
+    // The dominator sets the set, and we are guaranteed to have visited it already.
+    DCHECK(successor_set != nullptr);
+
+    // If this block dominates this successor there is nothing to do.
+    // Also if the set is empty, there is nothing to kill.
+    if (successor->GetDominator() != block && !successor_set->IsEmpty()) {
+      if (block->IsInLoop() && IsLoopExit(block, successor)) {
+        // All instructions killed in the loop must be killed for a loop exit.
+        SideEffects effects = GetLoopEffects(block->GetLoopInformation()->GetHeader());
+        sets_.Get(successor->GetBlockId())->Kill(effects);
+      } else {
+        // Following block (that might be in the same loop).
+        // Just kill instructions based on this block's side effects.
+        sets_.Get(successor->GetBlockId())->Kill(GetBlockEffects(block));
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
new file mode 100644
index 0000000..41b3ceb
--- /dev/null
+++ b/compiler/optimizing/gvn.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_GVN_H_
+#define ART_COMPILER_OPTIMIZING_GVN_H_
+
+#include <gtest/gtest.h>
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * A node in the collision list of a ValueSet. Encodes the instruction,
+ * the hash code, and the next node in the collision list.
+ */
+class ValueSetNode : public ArenaObject {
+ public:
+  ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
+      : instruction_(instruction), hash_code_(hash_code), next_(next) {}
+
+  size_t GetHashCode() const { return hash_code_; }
+  HInstruction* GetInstruction() const { return instruction_; }
+  ValueSetNode* GetNext() const { return next_; }
+  void SetNext(ValueSetNode* node) { next_ = node; }
+
+ private:
+  HInstruction* const instruction_;
+  const size_t hash_code_;
+  ValueSetNode* next_;
+
+  DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
+};
+
+/**
+ * A ValueSet holds instructions that can replace other instructions. It is updated
+ * through the `Add` method, and the `Kill` method. The `Kill` method removes
+ * instructions that are affected by the given side effect.
+ *
+ * The `Lookup` method returns an equivalent instruction to the given instruction
+ * if there is one in the set. In GVN, we would say those instructions have the
+ * same "number".
+ */
+class ValueSet : public ArenaObject {
+ public:
+  explicit ValueSet(ArenaAllocator* allocator)
+      : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
+    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+      table_[i] = nullptr;
+    }
+  }
+
+  // Adds an instruction in the set.
+  void Add(HInstruction* instruction) {
+    DCHECK(Lookup(instruction) == nullptr);
+    size_t hash_code = instruction->ComputeHashCode();
+    size_t index = hash_code % kDefaultNumberOfEntries;
+    if (table_[index] == nullptr) {
+      table_[index] = instruction;
+    } else {
+      collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
+    }
+    ++number_of_entries_;
+  }
+
+  // If in the set, returns an equivalent instruction to the given instruction. Returns
+  // null otherwise.
+  HInstruction* Lookup(HInstruction* instruction) const {
+    size_t hash_code = instruction->ComputeHashCode();
+    size_t index = hash_code % kDefaultNumberOfEntries;
+    HInstruction* existing = table_[index];
+    if (existing != nullptr && existing->Equals(instruction)) {
+      return existing;
+    }
+
+    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+      if (node->GetHashCode() == hash_code) {
+        existing = node->GetInstruction();
+        if (existing->Equals(instruction)) {
+          return existing;
+        }
+      }
+    }
+    return nullptr;
+  }
+
+  // Removes all instructions in the set that are affected by the given side effects.
+  void Kill(SideEffects side_effects) {
+    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+      HInstruction* instruction = table_[i];
+      if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
+        table_[i] = nullptr;
+        --number_of_entries_;
+      }
+    }
+
+    ValueSetNode* current = collisions_;
+    ValueSetNode* previous = nullptr;
+    while (current != nullptr) {
+      HInstruction* instruction = current->GetInstruction();
+      if (instruction->GetSideEffects().DependsOn(side_effects)) {
+        if (previous == nullptr) {
+          collisions_ = current->GetNext();
+        } else {
+          previous->SetNext(current->GetNext());
+        }
+        --number_of_entries_;
+      } else {
+        previous = current;
+      }
+      current = current->GetNext();
+    }
+  }
+
+  // Returns a copy of this set.
+  ValueSet* Copy() const {
+    ValueSet* copy = new (allocator_) ValueSet(allocator_);
+
+    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+      copy->table_[i] = table_[i];
+    }
+
+    // Note that the order will be inverted in the copy. This is fine, as the order is not
+    // relevant for a ValueSet.
+    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+      copy->collisions_ = new (allocator_) ValueSetNode(
+          node->GetInstruction(), node->GetHashCode(), copy->collisions_);
+    }
+
+    copy->number_of_entries_ = number_of_entries_;
+    return copy;
+  }
+
+  bool IsEmpty() const { return number_of_entries_ == 0; }
+  size_t GetNumberOfEntries() const { return number_of_entries_; }
+
+ private:
+  static constexpr size_t kDefaultNumberOfEntries = 8;
+
+  ArenaAllocator* const allocator_;
+
+  // The number of entries in the set.
+  size_t number_of_entries_;
+
+  // The internal implementation of the set. It uses a combination of a hash code based
+  // fixed-size list, and a linked list to handle hash code collisions.
+  // TODO: Tune the fixed size list original size, and support growing it.
+  ValueSetNode* collisions_;
+  HInstruction* table_[kDefaultNumberOfEntries];
+
+  DISALLOW_COPY_AND_ASSIGN(ValueSet);
+};
+
+/**
+ * Optimization phase that removes redundant instruction.
+ */
+class GlobalValueNumberer : public ValueObject {
+ public:
+  GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph)
+      : allocator_(allocator),
+        graph_(graph),
+        block_effects_(allocator, graph->GetBlocks().Size()),
+        loop_effects_(allocator, graph->GetBlocks().Size()),
+        sets_(allocator, graph->GetBlocks().Size()),
+        visited_(allocator, graph->GetBlocks().Size()) {
+    size_t number_of_blocks = graph->GetBlocks().Size();
+    block_effects_.SetSize(number_of_blocks);
+    loop_effects_.SetSize(number_of_blocks);
+    sets_.SetSize(number_of_blocks);
+    visited_.SetSize(number_of_blocks);
+
+    for (size_t i = 0; i < number_of_blocks; ++i) {
+      block_effects_.Put(i, SideEffects::None());
+      loop_effects_.Put(i, SideEffects::None());
+    }
+  }
+
+  void Run();
+
+ private:
+  // Per-block GVN. Will also update the ValueSet of the dominated and
+  // successor blocks.
+  void VisitBasicBlock(HBasicBlock* block);
+
+  // Compute side effects of individual blocks and loops. The GVN algorithm
+  // will use these side effects to update the ValueSet of individual blocks.
+  void ComputeSideEffects();
+
+  void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
+  SideEffects GetLoopEffects(HBasicBlock* block) const;
+  SideEffects GetBlockEffects(HBasicBlock* block) const;
+
+  ArenaAllocator* const allocator_;
+  HGraph* const graph_;
+
+  // Side effects of individual blocks, that is the union of the side effects
+  // of the instructions in the block.
+  GrowableArray<SideEffects> block_effects_;
+
+  // Side effects of loops, that is the union of the side effects of the
+  // blocks contained in that loop.
+  GrowableArray<SideEffects> loop_effects_;
+
+  // ValueSet for blocks. Initially null, but for an individual block they
+  // are allocated and populated by the dominator, and updated by all blocks
+  // in the path from the dominator to the block.
+  GrowableArray<ValueSet*> sets_;
+
+  // Mark visisted blocks. Only used for debugging.
+  GrowableArray<bool> visited_;
+
+  FRIEND_TEST(GVNTest, LoopSideEffects);
+  DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_GVN_H_
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
new file mode 100644
index 0000000..ad6e338
--- /dev/null
+++ b/compiler/optimizing/gvn_test.cc
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "builder.h"
+#include "gvn.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "utils/arena_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(GVNTest, LocalFieldElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  entry->AddInstruction(parameter);
+
+  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+
+  block->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+  block->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+  HInstruction* to_remove = block->GetLastInstruction();
+  block->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(43)));
+  HInstruction* different_offset = block->GetLastInstruction();
+  // Kill the value.
+  block->AddInstruction(new (&allocator) HInstanceFieldSet(
+      parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+  block->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+  HInstruction* use_after_kill = block->GetLastInstruction();
+  block->AddInstruction(new (&allocator) HExit());
+
+  ASSERT_EQ(to_remove->GetBlock(), block);
+  ASSERT_EQ(different_offset->GetBlock(), block);
+  ASSERT_EQ(use_after_kill->GetBlock(), block);
+
+  graph->BuildDominatorTree();
+  graph->TransformToSSA();
+  GlobalValueNumberer(&allocator, graph).Run();
+
+  ASSERT_TRUE(to_remove->GetBlock() == nullptr);
+  ASSERT_EQ(different_offset->GetBlock(), block);
+  ASSERT_EQ(use_after_kill->GetBlock(), block);
+}
+
+TEST(GVNTest, GlobalFieldElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  entry->AddInstruction(parameter);
+
+  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  block->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+
+  block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
+  HBasicBlock* then = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* else_ = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* join = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(then);
+  graph->AddBlock(else_);
+  graph->AddBlock(join);
+
+  block->AddSuccessor(then);
+  block->AddSuccessor(else_);
+  then->AddSuccessor(join);
+  else_->AddSuccessor(join);
+
+  then->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+  then->AddInstruction(new (&allocator) HGoto());
+  else_->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+  else_->AddInstruction(new (&allocator) HGoto());
+  join->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+  join->AddInstruction(new (&allocator) HExit());
+
+  graph->BuildDominatorTree();
+  graph->TransformToSSA();
+  GlobalValueNumberer(&allocator, graph).Run();
+
+  // Check that all field get instructions have been GVN'ed.
+  ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
+  ASSERT_TRUE(else_->GetFirstInstruction()->IsGoto());
+  ASSERT_TRUE(join->GetFirstInstruction()->IsExit());
+}
+
+TEST(GVNTest, LoopFieldElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+
+  HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  entry->AddInstruction(parameter);
+
+  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  block->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+  block->AddInstruction(new (&allocator) HGoto());
+
+  HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* loop_body = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+
+  graph->AddBlock(loop_header);
+  graph->AddBlock(loop_body);
+  graph->AddBlock(exit);
+  block->AddSuccessor(loop_header);
+  loop_header->AddSuccessor(loop_body);
+  loop_header->AddSuccessor(exit);
+  loop_body->AddSuccessor(loop_header);
+
+  loop_header->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+  HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
+  loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
+
+  // Kill inside the loop body to prevent field gets inside the loop header
+  // and the body to be GVN'ed.
+  loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(
+      parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+  HInstruction* field_set = loop_body->GetLastInstruction();
+  loop_body->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+  HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
+  loop_body->AddInstruction(new (&allocator) HGoto());
+
+  exit->AddInstruction(
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+  HInstruction* field_get_in_exit = exit->GetLastInstruction();
+  exit->AddInstruction(new (&allocator) HExit());
+
+  ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
+  ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
+  ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
+
+  graph->BuildDominatorTree();
+  graph->TransformToSSA();
+  graph->FindNaturalLoops();
+  GlobalValueNumberer(&allocator, graph).Run();
+
+  // Check that all field get instructions are still there.
+  ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
+  ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
+  // The exit block is dominated by the loop header, whose field get
+  // does not get killed by the loop flags.
+  ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
+
+  // Now remove the field set, and check that all field get instructions have been GVN'ed.
+  loop_body->RemoveInstruction(field_set);
+  GlobalValueNumberer(&allocator, graph).Run();
+
+  ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
+  ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
+  ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
+}
+
+// Test that inner loops affect the side effects of the outer loop.
+TEST(GVNTest, LoopSideEffects) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+
+  HBasicBlock* outer_loop_header = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* outer_loop_body = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* outer_loop_exit = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* inner_loop_header = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* inner_loop_body = new (&allocator) HBasicBlock(graph);
+  HBasicBlock* inner_loop_exit = new (&allocator) HBasicBlock(graph);
+
+  graph->AddBlock(outer_loop_header);
+  graph->AddBlock(outer_loop_body);
+  graph->AddBlock(outer_loop_exit);
+  graph->AddBlock(inner_loop_header);
+  graph->AddBlock(inner_loop_body);
+  graph->AddBlock(inner_loop_exit);
+
+  entry->AddSuccessor(outer_loop_header);
+  outer_loop_header->AddSuccessor(outer_loop_body);
+  outer_loop_header->AddSuccessor(outer_loop_exit);
+  outer_loop_body->AddSuccessor(inner_loop_header);
+  inner_loop_header->AddSuccessor(inner_loop_body);
+  inner_loop_header->AddSuccessor(inner_loop_exit);
+  inner_loop_body->AddSuccessor(inner_loop_header);
+  inner_loop_exit->AddSuccessor(outer_loop_header);
+
+  HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimBoolean);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(new (&allocator) HGoto());
+  outer_loop_header->AddInstruction(new (&allocator) HIf(parameter));
+  outer_loop_body->AddInstruction(new (&allocator) HGoto());
+  inner_loop_header->AddInstruction(new (&allocator) HIf(parameter));
+  inner_loop_body->AddInstruction(new (&allocator) HGoto());
+  inner_loop_exit->AddInstruction(new (&allocator) HGoto());
+  outer_loop_exit->AddInstruction(new (&allocator) HExit());
+
+  graph->BuildDominatorTree();
+  graph->TransformToSSA();
+  graph->FindNaturalLoops();
+
+  ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
+      *outer_loop_header->GetLoopInformation()));
+
+  // Check that the loops don't have side effects.
+  {
+    // Make one block with a side effect.
+    entry->AddInstruction(new (&allocator) HInstanceFieldSet(
+        parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+
+    GlobalValueNumberer gvn(&allocator, graph);
+    gvn.Run();
+
+    ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
+    ASSERT_FALSE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
+    ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+  }
+
+  // Check that the side effects of the outer loop does not affect the inner loop.
+  {
+    outer_loop_body->InsertInstructionBefore(
+        new (&allocator) HInstanceFieldSet(
+            parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+        outer_loop_body->GetLastInstruction());
+
+    GlobalValueNumberer gvn(&allocator, graph);
+    gvn.Run();
+
+    ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
+    ASSERT_TRUE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
+    ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
+    ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+  }
+
+  // Check that the side effects of the inner loop affects the outer loop.
+  {
+    outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
+    inner_loop_body->InsertInstructionBefore(
+        new (&allocator) HInstanceFieldSet(
+            parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+        inner_loop_body->GetLastInstruction());
+
+    GlobalValueNumberer gvn(&allocator, graph);
+    gvn.Run();
+
+    ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
+    ASSERT_FALSE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
+    ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
+    ASSERT_TRUE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+  }
+}
+}  // namespace art
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
new file mode 100644
index 0000000..a0de73d
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_simplifier.h"
+
+namespace art {
+
+void InstructionSimplifier::Run() {
+  VisitInsertionOrder();
+}
+
+void InstructionSimplifier::VisitSuspendCheck(HSuspendCheck* check) {
+  HBasicBlock* block = check->GetBlock();
+  // Currently always keep the suspend check at entry.
+  if (block->IsEntryBlock()) return;
+
+  // Currently always keep suspend checks at loop entry.
+  if (block->IsLoopHeader() && block->GetFirstInstruction() == check) {
+    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == check);
+    return;
+  }
+
+  // Remove the suspend check that was added at build time for the baseline
+  // compiler.
+  block->RemoveInstruction(check);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
new file mode 100644
index 0000000..b2f3f52
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
+
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * Implements optimizations specific to each instruction.
+ */
+class InstructionSimplifier : public HGraphVisitor {
+ public:
+  explicit InstructionSimplifier(HGraph* graph) : HGraphVisitor(graph) {}
+
+  void Run();
+
+ private:
+  virtual void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE;
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index e4f9371..6dd4207 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -19,6 +19,7 @@
 #include "base/stringprintf.h"
 #include "builder.h"
 #include "code_generator.h"
+#include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
 #include "graph_visualizer.h"
@@ -45,8 +46,8 @@
   graph->TransformToSSA();
   graph->FindNaturalLoops();
 
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   ASSERT_EQ(liveness.GetLinearPostOrder().Size(), number_of_blocks);
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index a6e5ca9..03f8625 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -16,6 +16,7 @@
 
 #include "builder.h"
 #include "code_generator.h"
+#include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
 #include "nodes.h"
@@ -31,6 +32,9 @@
   HGraphBuilder builder(allocator);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
   HGraph* graph = builder.BuildGraph(*item);
+  // Suspend checks implementation may change in the future, and this test relies
+  // on how instructions are ordered.
+  RemoveSuspendChecks(graph);
   graph->BuildDominatorTree();
   graph->TransformToSSA();
   graph->FindNaturalLoops();
@@ -58,8 +62,8 @@
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
 
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
@@ -104,8 +108,8 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
@@ -142,7 +146,7 @@
    *       22: phi
    *       24: return
    *         |
-   *       38: exit
+   *       28: exit
    */
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
     Instruction::CONST_4 | 0 | 0,
@@ -153,8 +157,8 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   // Test for the 4 constant.
@@ -190,7 +194,7 @@
   ASSERT_TRUE(range->GetNext() == nullptr);
 }
 
-TEST(LiveRangesTest, Loop) {
+TEST(LiveRangesTest, Loop1) {
   /*
    * Test the following snippet:
    *  var a = 0;
@@ -229,8 +233,9 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  RemoveSuspendChecks(graph);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   // Test for the 0 constant.
@@ -267,4 +272,168 @@
   ASSERT_TRUE(range->GetNext() == nullptr);
 }
 
+TEST(LiveRangesTest, Loop2) {
+  /*
+   * Test the following snippet:
+   *  var a = 0;
+   *  while (a == a) {
+   *    a = a + a;
+   *  }
+   *  return a;
+   *
+   * Which becomes the following graph (numbered by lifetime position):
+   *       2: constant0
+   *       4: goto
+   *           |
+   *       8: goto
+   *           |
+   *       10: phi
+   *       12: equal
+   *       14: if +++++
+   *        |       \ +
+   *        |     18: suspend
+   *        |     20: add
+   *        |     22: goto
+   *        |
+   *       26: return
+   *         |
+   *       30: exit
+   *
+   * We want to make sure the phi at 10 has a lifetime hole after the add at 20.
+   */
+
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_EQ, 6,
+    Instruction::ADD_INT, 0, 0,
+    Instruction::GOTO | 0xFB00,
+    Instruction::RETURN | 0 << 8);
+
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = BuildGraph(data, &allocator);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
+  liveness.Analyze();
+
+  // Test for the 0 constant.
+  HIntConstant* constant = liveness.GetInstructionFromSsaIndex(0)->AsIntConstant();
+  LiveInterval* interval = constant->GetLiveInterval();
+  LiveRange* range = interval->GetFirstRange();
+  ASSERT_EQ(2u, range->GetStart());
+  // Last use is the loop phi so instruction is live until
+  // the end of the pre loop header.
+  ASSERT_EQ(10u, range->GetEnd());
+  ASSERT_TRUE(range->GetNext() == nullptr);
+
+  // Test for the loop phi.
+  HPhi* phi = liveness.GetInstructionFromSsaIndex(1)->AsPhi();
+  interval = phi->GetLiveInterval();
+  range = interval->GetFirstRange();
+  ASSERT_EQ(10u, range->GetStart());
+  ASSERT_EQ(21u, range->GetEnd());
+  range = range->GetNext();
+  ASSERT_TRUE(range != nullptr);
+  ASSERT_EQ(24u, range->GetStart());
+  ASSERT_EQ(27u, range->GetEnd());
+
+  // Test for the add instruction.
+  HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd();
+  interval = add->GetLiveInterval();
+  range = interval->GetFirstRange();
+  ASSERT_EQ(20u, range->GetStart());
+  ASSERT_EQ(24u, range->GetEnd());
+  ASSERT_TRUE(range->GetNext() == nullptr);
+}
+
+TEST(LiveRangesTest, CFG4) {
+  /*
+   * Test the following snippet:
+   *  var a = 0;
+   *  var b = 4;
+   *  if (a == a) {
+   *    a = b + a;
+   *  } else {
+   *    a = b + a
+   *  }
+   *  return b;
+   *
+   * Which becomes the following graph (numbered by lifetime position):
+   *       2: constant0
+   *       4: constant4
+   *       6: goto
+   *           |
+   *       10: equal
+   *       12: if
+   *       /       \
+   *   16: add    22: add
+   *   18: goto   24: goto
+   *       \       /
+   *       26: phi
+   *       28: return
+   *         |
+   *       32: exit
+   *
+   * We want to make sure the constant0 has a lifetime hole after the 16: add.
+   */
+  const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::CONST_4 | 4 << 12 | 1 << 8,
+    Instruction::IF_EQ, 5,
+    Instruction::ADD_INT, 1 << 8,
+    Instruction::GOTO | 0x300,
+    Instruction::ADD_INT, 1 << 8,
+    Instruction::RETURN | 1 << 8);
+
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = BuildGraph(data, &allocator);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
+  liveness.Analyze();
+
+  // Test for the 0 constant.
+  LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
+  LiveRange* range = interval->GetFirstRange();
+  ASSERT_EQ(2u, range->GetStart());
+  ASSERT_EQ(16u, range->GetEnd());
+  range = range->GetNext();
+  ASSERT_TRUE(range != nullptr);
+  ASSERT_EQ(20u, range->GetStart());
+  ASSERT_EQ(22u, range->GetEnd());
+  ASSERT_TRUE(range->GetNext() == nullptr);
+
+  // Test for the 4 constant.
+  interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval();
+  range = interval->GetFirstRange();
+  ASSERT_EQ(4u, range->GetStart());
+  ASSERT_EQ(29u, range->GetEnd());
+  ASSERT_TRUE(range->GetNext() == nullptr);
+
+  // Test for the first add.
+  HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd();
+  interval = add->GetLiveInterval();
+  range = interval->GetFirstRange();
+  ASSERT_EQ(16u, range->GetStart());
+  ASSERT_EQ(20u, range->GetEnd());
+  ASSERT_TRUE(range->GetNext() == nullptr);
+
+  // Test for the second add.
+  add = liveness.GetInstructionFromSsaIndex(3)->AsAdd();
+  interval = add->GetLiveInterval();
+  range = interval->GetFirstRange();
+  ASSERT_EQ(22u, range->GetStart());
+  ASSERT_EQ(26u, range->GetEnd());
+  ASSERT_TRUE(range->GetNext() == nullptr);
+
+  // Test for the phi, which is unused.
+  HPhi* phi = liveness.GetInstructionFromSsaIndex(4)->AsPhi();
+  ASSERT_EQ(phi->NumberOfUses(), 0u);
+  interval = phi->GetLiveInterval();
+  range = interval->GetFirstRange();
+  ASSERT_EQ(26u, range->GetStart());
+  ASSERT_EQ(28u, range->GetEnd());
+  ASSERT_TRUE(range->GetNext() == nullptr);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 1a4d745..2d86169 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -16,6 +16,7 @@
 
 #include "builder.h"
 #include "code_generator.h"
+#include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
 #include "nodes.h"
@@ -49,8 +50,8 @@
   graph->BuildDominatorTree();
   graph->TransformToSSA();
   graph->FindNaturalLoops();
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   std::ostringstream buffer;
@@ -545,4 +546,51 @@
   TestCode(data, expected);
 }
 
+TEST(LivenessTest, Loop8) {
+  // var a = 0;
+  // while (a == a) {
+  //   a = a + a;
+  // }
+  // return a;
+  //
+  // We want to test that the ins of the loop exit
+  // does contain the phi.
+  // Bitsets are made of:
+  // (constant0, phi, add)
+  const char* expected =
+    "Block 0\n"
+    "  live in: (000)\n"
+    "  live out: (100)\n"
+    "  kill: (100)\n"
+    "Block 1\n"  // pre loop header
+    "  live in: (100)\n"
+    "  live out: (000)\n"
+    "  kill: (000)\n"
+    "Block 2\n"  // loop header
+    "  live in: (000)\n"
+    "  live out: (010)\n"
+    "  kill: (010)\n"
+    "Block 3\n"  // back edge
+    "  live in: (010)\n"
+    "  live out: (000)\n"
+    "  kill: (001)\n"
+    "Block 4\n"  // return block
+    "  live in: (010)\n"
+    "  live out: (000)\n"
+    "  kill: (000)\n"
+    "Block 5\n"  // exit block
+    "  live in: (000)\n"
+    "  live out: (000)\n"
+    "  kill: (000)\n";
+
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_EQ, 6,
+    Instruction::ADD_INT, 0, 0,
+    Instruction::GOTO | 0xFB00,
+    Instruction::RETURN | 0 << 8);
+
+  TestCode(data, expected);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 468cfb7..1c36cdf 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -20,13 +20,29 @@
 
 namespace art {
 
-LocationSummary::LocationSummary(HInstruction* instruction)
+LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind)
     : inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
-      temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0) {
+      temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0),
+      environment_(instruction->GetBlock()->GetGraph()->GetArena(),
+                   instruction->EnvironmentSize()),
+      call_kind_(call_kind),
+      stack_mask_(nullptr),
+      register_mask_(0),
+      live_registers_() {
   inputs_.SetSize(instruction->InputCount());
-  for (size_t i = 0; i < instruction->InputCount(); i++) {
+  for (size_t i = 0; i < instruction->InputCount(); ++i) {
     inputs_.Put(i, Location());
   }
+  environment_.SetSize(instruction->EnvironmentSize());
+  for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) {
+    environment_.Put(i, Location());
+  }
+  instruction->SetLocations(this);
+
+  if (NeedsSafepoint()) {
+    ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetArena();
+    stack_mask_ = new (arena) ArenaBitVector(arena, 0, true);
+  }
 }
 
 
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index aaddb09..f358e05 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_OPTIMIZING_LOCATIONS_H_
 
 #include "base/bit_field.h"
+#include "base/bit_vector.h"
 #include "utils/allocation.h"
 #include "utils/growable_array.h"
 #include "utils/managed_register.h"
@@ -43,13 +44,13 @@
     // low bits are in the last parameter register, and the high
     // bits are in a stack slot. The kQuickParameter kind is for
     // handling this special case.
-    kQuickParameter = 5,
+    kQuickParameter = 6,
 
     // Unallocated location represents a location that is not fixed and can be
     // allocated by a register allocator.  Each unallocated location has
     // a policy that specifies what kind of location is suitable. Payload
     // contains register allocation policy.
-    kUnallocated = 6,
+    kUnallocated = 7,
   };
 
   Location() : value_(kInvalid) {
@@ -59,8 +60,8 @@
     COMPILE_ASSERT((kStackSlot & kLocationTagMask) != kConstant, TagError);
     COMPILE_ASSERT((kDoubleStackSlot & kLocationTagMask) != kConstant, TagError);
     COMPILE_ASSERT((kRegister & kLocationTagMask) != kConstant, TagError);
+    COMPILE_ASSERT((kQuickParameter & kLocationTagMask) != kConstant, TagError);
     COMPILE_ASSERT((kConstant & kLocationTagMask) == kConstant, TagError);
-    COMPILE_ASSERT((kQuickParameter & kLocationTagMask) == kConstant, TagError);
 
     DCHECK(!IsValid());
   }
@@ -173,7 +174,7 @@
   x86_64::X86_64ManagedRegister AsX86_64() const;
 
   Kind GetKind() const {
-    return KindField::Decode(value_);
+    return IsConstant() ? kConstant : KindField::Decode(value_);
   }
 
   bool Equals(Location other) const {
@@ -265,6 +266,34 @@
   uword value_;
 };
 
+class RegisterSet : public ValueObject {
+ public:
+  RegisterSet() : core_registers_(0), floating_point_registers_(0) {}
+
+  void Add(Location loc) {
+    // TODO: floating point registers.
+    core_registers_ |= (1 << loc.reg().RegId());
+  }
+
+  bool ContainsCoreRegister(uint32_t id) {
+    return Contains(core_registers_, id);
+  }
+
+  bool ContainsFloatingPointRegister(uint32_t id) {
+    return Contains(floating_point_registers_, id);
+  }
+
+  static bool Contains(uint32_t register_set, uint32_t reg) {
+    return (register_set & (1 << reg)) != 0;
+  }
+
+ private:
+  uint32_t core_registers_;
+  uint32_t floating_point_registers_;
+
+  DISALLOW_COPY_AND_ASSIGN(RegisterSet);
+};
+
 /**
  * The code generator computes LocationSummary for each instruction so that
  * the instruction itself knows what code to generate: where to find the inputs
@@ -275,7 +304,13 @@
  */
 class LocationSummary : public ArenaObject {
  public:
-  explicit LocationSummary(HInstruction* instruction);
+  enum CallKind {
+    kNoCall,
+    kCallOnSlowPath,
+    kCall
+  };
+
+  LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall);
 
   void SetInAt(uint32_t at, Location location) {
     inputs_.Put(at, location);
@@ -309,12 +344,74 @@
     return temps_.Size();
   }
 
+  void SetEnvironmentAt(uint32_t at, Location location) {
+    environment_.Put(at, location);
+  }
+
+  Location GetEnvironmentAt(uint32_t at) const {
+    return environment_.Get(at);
+  }
+
   Location Out() const { return output_; }
 
+  bool CanCall() const { return call_kind_ != kNoCall; }
+  bool WillCall() const { return call_kind_ == kCall; }
+  bool OnlyCallsOnSlowPath() const { return call_kind_ == kCallOnSlowPath; }
+  bool NeedsSafepoint() const { return CanCall(); }
+
+  void SetStackBit(uint32_t index) {
+    stack_mask_->SetBit(index);
+  }
+
+  void ClearStackBit(uint32_t index) {
+    stack_mask_->ClearBit(index);
+  }
+
+  void SetRegisterBit(uint32_t reg_id) {
+    register_mask_ |= (1 << reg_id);
+  }
+
+  bool RegisterContainsObject(uint32_t reg_id) {
+    return RegisterSet::Contains(register_mask_, reg_id);
+  }
+
+  void AddLiveRegister(Location location) {
+    live_registers_.Add(location);
+  }
+
+  BitVector* GetStackMask() const {
+    return stack_mask_;
+  }
+
+  RegisterSet* GetLiveRegisters() {
+    return &live_registers_;
+  }
+
+  bool InputOverlapsWithOutputOrTemp(uint32_t input, bool is_environment) const {
+    if (is_environment) return true;
+    Location location = Out();
+    // TODO: Add more policies.
+    if (input == 0 && location.IsUnallocated() && location.GetPolicy() == Location::kSameAsFirstInput) {
+      return false;
+    }
+    return true;
+  }
+
  private:
   GrowableArray<Location> inputs_;
   GrowableArray<Location> temps_;
+  GrowableArray<Location> environment_;
   Location output_;
+  const CallKind call_kind_;
+
+  // Mask of objects that live in the stack.
+  BitVector* stack_mask_;
+
+  // Mask of objects that live in register.
+  uint32_t register_mask_;
+
+  // Registers that are in use at this position.
+  RegisterSet live_registers_;
 
   DISALLOW_COPY_AND_ASSIGN(LocationSummary);
 };
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 490d345..5c4ab8e 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -124,6 +124,7 @@
   // dominator of the block. We can then start visiting its successors.
   if (visits->Get(block->GetBlockId()) ==
       block->GetPredecessors().Size() - block->NumberOfBackEdges()) {
+    block->GetDominator()->AddDominatedBlock(block);
     reverse_post_order_.Add(block);
     for (size_t i = 0; i < block->GetSuccessors().Size(); i++) {
       VisitBlockForDominatorTree(block->GetSuccessors().Get(i), block, visits);
@@ -140,7 +141,7 @@
 void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) {
   // Insert a new node between `block` and `successor` to split the
   // critical edge.
-  HBasicBlock* new_block = new (arena_) HBasicBlock(this);
+  HBasicBlock* new_block = new (arena_) HBasicBlock(this, successor->GetDexPc());
   AddBlock(new_block);
   new_block->AddInstruction(new (arena_) HGoto());
   block->ReplaceSuccessor(successor, new_block);
@@ -161,8 +162,10 @@
   // If there are more than one back edge, make them branch to the same block that
   // will become the only back edge. This simplifies finding natural loops in the
   // graph.
-  if (info->NumberOfBackEdges() > 1) {
-    HBasicBlock* new_back_edge = new (arena_) HBasicBlock(this);
+  // Also, if the loop is a do/while (that is the back edge is an if), change the
+  // back edge to be a goto. This simplifies code generation of suspend cheks.
+  if (info->NumberOfBackEdges() > 1 || info->GetBackEdges().Get(0)->GetLastInstruction()->IsIf()) {
+    HBasicBlock* new_back_edge = new (arena_) HBasicBlock(this, header->GetDexPc());
     AddBlock(new_back_edge);
     new_back_edge->AddInstruction(new (arena_) HGoto());
     for (size_t pred = 0, e = info->GetBackEdges().Size(); pred < e; ++pred) {
@@ -179,7 +182,7 @@
   // loop.
   size_t number_of_incomings = header->GetPredecessors().Size() - info->NumberOfBackEdges();
   if (number_of_incomings != 1) {
-    HBasicBlock* pre_header = new (arena_) HBasicBlock(this);
+    HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc());
     AddBlock(pre_header);
     pre_header->AddInstruction(new (arena_) HGoto());
 
@@ -194,6 +197,23 @@
     }
     pre_header->AddSuccessor(header);
   }
+
+  // Make sure the second predecessor of a loop header is the back edge.
+  if (header->GetPredecessors().Get(1) != info->GetBackEdges().Get(0)) {
+    header->SwapPredecessors();
+  }
+
+  // Place the suspend check at the beginning of the header, so that live registers
+  // will be known when allocating registers. Note that code generation can still
+  // generate the suspend check at the back edge, but needs to be careful with
+  // loop phi spill slots (which are not written to at back edge).
+  HInstruction* first_instruction = header->GetFirstInstruction();
+  if (!first_instruction->IsSuspendCheck()) {
+    HSuspendCheck* check = new (arena_) HSuspendCheck(header->GetDexPc());
+    header->InsertInstructionBefore(check, first_instruction);
+    first_instruction = check;
+  }
+  info->SetSuspendCheck(first_instruction->AsSuspendCheck());
 }
 
 void HGraph::SimplifyCFG() {
@@ -307,6 +327,14 @@
   instruction->SetId(GetGraph()->GetNextInstructionId());
 }
 
+void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial,
+                                                  HInstruction* replacement) {
+  DCHECK(initial->GetBlock() == this);
+  InsertInstructionBefore(replacement, initial);
+  initial->ReplaceWith(replacement);
+  RemoveInstruction(initial);
+}
+
 static void Add(HInstructionList* instruction_list,
                 HBasicBlock* block,
                 HInstruction* instruction) {
@@ -337,6 +365,16 @@
   for (size_t i = 0; i < instruction->InputCount(); i++) {
     instruction->InputAt(i)->RemoveUser(instruction, i);
   }
+
+  HEnvironment* environment = instruction->GetEnvironment();
+  if (environment != nullptr) {
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* vreg = environment->GetInstructionAt(i);
+      if (vreg != nullptr) {
+        vreg->RemoveEnvironmentUser(environment, i);
+      }
+    }
+  }
 }
 
 void HBasicBlock::RemoveInstruction(HInstruction* instruction) {
@@ -347,13 +385,16 @@
   Remove(&phis_, this, phi);
 }
 
-void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
-  HUseListNode<HInstruction>* previous = nullptr;
-  HUseListNode<HInstruction>* current = uses_;
+template <typename T>
+static void RemoveFromUseList(T* user,
+                              size_t input_index,
+                              HUseListNode<T>** list) {
+  HUseListNode<T>* previous = nullptr;
+  HUseListNode<T>* current = *list;
   while (current != nullptr) {
     if (current->GetUser() == user && current->GetIndex() == input_index) {
       if (previous == NULL) {
-        uses_ = current->GetTail();
+        *list = current->GetTail();
       } else {
         previous->SetTail(current->GetTail());
       }
@@ -363,6 +404,14 @@
   }
 }
 
+void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
+  RemoveFromUseList(user, input_index, &uses_);
+}
+
+void HInstruction::RemoveEnvironmentUser(HEnvironment* user, size_t input_index) {
+  RemoveFromUseList(user, input_index, &env_uses_);
+}
+
 void HInstructionList::AddInstruction(HInstruction* instruction) {
   if (first_instruction_ == nullptr) {
     DCHECK(last_instruction_ == nullptr);
@@ -392,6 +441,63 @@
   }
 }
 
+bool HInstructionList::Contains(HInstruction* instruction) const {
+  for (HInstructionIterator it(*this); !it.Done(); it.Advance()) {
+    if (it.Current() == instruction) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool HInstructionList::FoundBefore(const HInstruction* instruction1,
+                                   const HInstruction* instruction2) const {
+  DCHECK_EQ(instruction1->GetBlock(), instruction2->GetBlock());
+  for (HInstructionIterator it(*this); !it.Done(); it.Advance()) {
+    if (it.Current() == instruction1) {
+      return true;
+    }
+    if (it.Current() == instruction2) {
+      return false;
+    }
+  }
+  LOG(FATAL) << "Did not find an order between two instructions of the same block.";
+  return true;
+}
+
+bool HInstruction::Dominates(HInstruction* other_instruction) const {
+  HBasicBlock* block = GetBlock();
+  HBasicBlock* other_block = other_instruction->GetBlock();
+  if (block != other_block) {
+    return GetBlock()->Dominates(other_instruction->GetBlock());
+  } else {
+    // If both instructions are in the same block, ensure this
+    // instruction comes before `other_instruction`.
+    if (IsPhi()) {
+      if (!other_instruction->IsPhi()) {
+        // Phis appear before non phi-instructions so this instruction
+        // dominates `other_instruction`.
+        return true;
+      } else {
+        // There is no order among phis.
+        LOG(FATAL) << "There is no dominance between phis of a same block.";
+        return false;
+      }
+    } else {
+      // `this` is not a phi.
+      if (other_instruction->IsPhi()) {
+        // Phis appear before non phi-instructions so this instruction
+        // does not dominate `other_instruction`.
+        return false;
+      } else {
+        // Check whether this instruction comes before
+        // `other_instruction` in the instruction list.
+        return block->GetInstructions().FoundBefore(this, other_instruction);
+      }
+    }
+  }
+}
+
 void HInstruction::ReplaceWith(HInstruction* other) {
   DCHECK(other != nullptr);
   for (HUseIterator<HInstruction> it(GetUses()); !it.Done(); it.Advance()) {
@@ -414,6 +520,10 @@
   env_uses_ = nullptr;
 }
 
+size_t HInstruction::EnvironmentSize() const {
+  return HasEnvironment() ? environment_->Size() : 0;
+}
+
 void HPhi::AddInput(HInstruction* input) {
   DCHECK(input->GetBlock() != nullptr);
   inputs_.Add(input);
@@ -445,6 +555,18 @@
   }
 }
 
+HConstant* HBinaryOperation::TryStaticEvaluation(ArenaAllocator* allocator) const {
+  if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) {
+    int32_t value = Evaluate(GetLeft()->AsIntConstant()->GetValue(),
+                             GetRight()->AsIntConstant()->GetValue());
+    return new(allocator) HIntConstant(value);
+  } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
+    int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
+                             GetRight()->AsLongConstant()->GetValue());
+    return new(allocator) HLongConstant(value);
+  }
+  return nullptr;
+}
 
 bool HCondition::NeedsMaterialization() const {
   if (!HasOnlyOneUse()) {
@@ -456,12 +578,34 @@
     return true;
   }
 
-  // TODO: should we allow intervening instructions with no side-effect between this condition
-  // and the If instruction?
+  // TODO: if there is no intervening instructions with side-effect between this condition
+  // and the If instruction, we should move the condition just before the If.
   if (GetNext() != user) {
     return true;
   }
   return false;
 }
 
+bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const {
+  HInstruction* previous = if_->GetPrevious();
+  while (previous != nullptr && previous->IsParallelMove()) {
+    previous = previous->GetPrevious();
+  }
+  return previous == this;
+}
+
+bool HInstruction::Equals(HInstruction* other) const {
+  if (!InstructionTypeEquals(other)) return false;
+  DCHECK_EQ(GetKind(), other->GetKind());
+  if (!InstructionDataEquals(other)) return false;
+  if (GetType() != other->GetType()) return false;
+  if (InputCount() != other->InputCount()) return false;
+
+  for (size_t i = 0, e = InputCount(); i < e; ++i) {
+    if (InputAt(i) != other->InputAt(i)) return false;
+  }
+  DCHECK_EQ(ComputeHashCode(), other->ComputeHashCode());
+  return true;
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index cb3dd0f..3d65366 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -32,12 +32,14 @@
 class HIntConstant;
 class HGraphVisitor;
 class HPhi;
+class HSuspendCheck;
 class LiveInterval;
 class LocationSummary;
 
 static const int kDefaultNumberOfBlocks = 8;
 static const int kDefaultNumberOfSuccessors = 2;
 static const int kDefaultNumberOfPredecessors = 2;
+static const int kDefaultNumberOfDominatedBlocks = 1;
 static const int kDefaultNumberOfBackEdges = 1;
 
 enum IfCondition {
@@ -56,6 +58,15 @@
   void AddInstruction(HInstruction* instruction);
   void RemoveInstruction(HInstruction* instruction);
 
+  // Return true if this list contains `instruction`.
+  bool Contains(HInstruction* instruction) const;
+
+  // Return true if `instruction1` is found before `instruction2` in
+  // this instruction list and false otherwise.  Abort if none
+  // of these instructions is found.
+  bool FoundBefore(const HInstruction* instruction1,
+                   const HInstruction* instruction2) const;
+
  private:
   HInstruction* first_instruction_;
   HInstruction* last_instruction_;
@@ -191,13 +202,19 @@
  public:
   HLoopInformation(HBasicBlock* header, HGraph* graph)
       : header_(header),
+        suspend_check_(nullptr),
         back_edges_(graph->GetArena(), kDefaultNumberOfBackEdges),
-        blocks_(graph->GetArena(), graph->GetBlocks().Size(), false) {}
+        // Make bit vector growable, as the number of blocks may change.
+        blocks_(graph->GetArena(), graph->GetBlocks().Size(), true) {}
 
   HBasicBlock* GetHeader() const {
     return header_;
   }
 
+  HSuspendCheck* GetSuspendCheck() const { return suspend_check_; }
+  void SetSuspendCheck(HSuspendCheck* check) { suspend_check_ = check; }
+  bool HasSuspendCheck() const { return suspend_check_ != nullptr; }
+
   void AddBackEdge(HBasicBlock* back_edge) {
     back_edges_.Add(back_edge);
   }
@@ -246,6 +263,7 @@
   void PopulateRecursive(HBasicBlock* block);
 
   HBasicBlock* header_;
+  HSuspendCheck* suspend_check_;
   GrowableArray<HBasicBlock*> back_edges_;
   ArenaBitVector blocks_;
 
@@ -253,19 +271,23 @@
 };
 
 static constexpr size_t kNoLifetime = -1;
+static constexpr uint32_t kNoDexPc = -1;
 
 // A block in a method. Contains the list of instructions represented
 // as a double linked list. Each block knows its predecessors and
 // successors.
+
 class HBasicBlock : public ArenaObject {
  public:
-  explicit HBasicBlock(HGraph* graph)
+  explicit HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
       : graph_(graph),
         predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors),
         successors_(graph->GetArena(), kDefaultNumberOfSuccessors),
         loop_information_(nullptr),
         dominator_(nullptr),
+        dominated_blocks_(graph->GetArena(), kDefaultNumberOfDominatedBlocks),
         block_id_(-1),
+        dex_pc_(dex_pc),
         lifetime_start_(kNoLifetime),
         lifetime_end_(kNoLifetime) {}
 
@@ -277,6 +299,18 @@
     return successors_;
   }
 
+  const GrowableArray<HBasicBlock*>& GetDominatedBlocks() const {
+    return dominated_blocks_;
+  }
+
+  bool IsEntryBlock() const {
+    return graph_->GetEntryBlock() == this;
+  }
+
+  bool IsExitBlock() const {
+    return graph_->GetExitBlock() == this;
+  }
+
   void AddBackEdge(HBasicBlock* back_edge) {
     if (loop_information_ == nullptr) {
       loop_information_ = new (graph_->GetArena()) HLoopInformation(this, graph_);
@@ -292,6 +326,7 @@
 
   HBasicBlock* GetDominator() const { return dominator_; }
   void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
+  void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); }
 
   int NumberOfBackEdges() const {
     return loop_information_ == nullptr
@@ -331,6 +366,13 @@
     block->successors_.Add(this);
   }
 
+  void SwapPredecessors() {
+    DCHECK_EQ(predecessors_.Size(), 2u);
+    HBasicBlock* temp = predecessors_.Get(0);
+    predecessors_.Put(0, predecessors_.Get(1));
+    predecessors_.Put(1, temp);
+  }
+
   size_t GetPredecessorIndexOf(HBasicBlock* predecessor) {
     for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) {
       if (predecessors_.Get(i) == predecessor) {
@@ -352,6 +394,9 @@
   void AddInstruction(HInstruction* instruction);
   void RemoveInstruction(HInstruction* instruction);
   void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
+  // Replace instruction `initial` with `replacement` within this block.
+  void ReplaceAndRemoveInstructionWith(HInstruction* initial,
+                                       HInstruction* replacement);
   void AddPhi(HPhi* phi);
   void RemovePhi(HPhi* phi);
 
@@ -359,6 +404,12 @@
     return (loop_information_ != nullptr) && (loop_information_->GetHeader() == this);
   }
 
+  bool IsLoopPreHeaderFirstPredecessor() const {
+    DCHECK(IsLoopHeader());
+    DCHECK(!GetPredecessors().IsEmpty());
+    return GetPredecessors().Get(0) == GetLoopInformation()->GetPreHeader();
+  }
+
   HLoopInformation* GetLoopInformation() const {
     return loop_information_;
   }
@@ -393,6 +444,8 @@
   void SetLifetimeStart(size_t start) { lifetime_start_ = start; }
   void SetLifetimeEnd(size_t end) { lifetime_end_ = end; }
 
+  uint32_t GetDexPc() const { return dex_pc_; }
+
  private:
   HGraph* const graph_;
   GrowableArray<HBasicBlock*> predecessors_;
@@ -401,7 +454,10 @@
   HInstructionList phis_;
   HLoopInformation* loop_information_;
   HBasicBlock* dominator_;
+  GrowableArray<HBasicBlock*> dominated_blocks_;
   int block_id_;
+  // The dex program counter of the first instruction of this block.
+  const uint32_t dex_pc_;
   size_t lifetime_start_;
   size_t lifetime_end_;
 
@@ -422,6 +478,7 @@
   M(If)                                                    \
   M(IntConstant)                                           \
   M(InvokeStatic)                                          \
+  M(InvokeVirtual)                                         \
   M(LoadLocal)                                             \
   M(Local)                                                 \
   M(LongConstant)                                          \
@@ -443,19 +500,26 @@
   M(BoundsCheck)                                           \
   M(NullCheck)                                             \
   M(Temporary)                                             \
+  M(SuspendCheck)                                          \
 
 #define FOR_EACH_INSTRUCTION(M)                            \
   FOR_EACH_CONCRETE_INSTRUCTION(M)                         \
-  M(Constant)
+  M(Constant)                                              \
+  M(BinaryOperation)
 
 #define FORWARD_DECLARATION(type) class H##type;
 FOR_EACH_INSTRUCTION(FORWARD_DECLARATION)
 #undef FORWARD_DECLARATION
 
-#define DECLARE_INSTRUCTION(type)                          \
-  virtual const char* DebugName() const { return #type; }  \
-  virtual H##type* As##type() { return this; }             \
-  virtual void Accept(HGraphVisitor* visitor)              \
+#define DECLARE_INSTRUCTION(type)                                       \
+  virtual InstructionKind GetKind() const { return k##type; }           \
+  virtual const char* DebugName() const { return #type; }               \
+  virtual const H##type* As##type() const OVERRIDE { return this; }     \
+  virtual H##type* As##type() OVERRIDE { return this; }                 \
+  virtual bool InstructionTypeEquals(HInstruction* other) const {       \
+    return other->Is##type();                                           \
+  }                                                                     \
+  virtual void Accept(HGraphVisitor* visitor)
 
 template <typename T>
 class HUseListNode : public ArenaObject {
@@ -477,9 +541,72 @@
   DISALLOW_COPY_AND_ASSIGN(HUseListNode);
 };
 
+// Represents the side effects an instruction may have.
+class SideEffects : public ValueObject {
+ public:
+  SideEffects() : flags_(0) {}
+
+  static SideEffects None() {
+    return SideEffects(0);
+  }
+
+  static SideEffects All() {
+    return SideEffects(ChangesSomething().flags_ | DependsOnSomething().flags_);
+  }
+
+  static SideEffects ChangesSomething() {
+    return SideEffects((1 << kFlagChangesCount) - 1);
+  }
+
+  static SideEffects DependsOnSomething() {
+    int count = kFlagDependsOnCount - kFlagChangesCount;
+    return SideEffects(((1 << count) - 1) << kFlagChangesCount);
+  }
+
+  SideEffects Union(SideEffects other) const {
+    return SideEffects(flags_ | other.flags_);
+  }
+
+  bool HasSideEffects() const {
+    size_t all_bits_set = (1 << kFlagChangesCount) - 1;
+    return (flags_ & all_bits_set) != 0;
+  }
+
+  bool HasAllSideEffects() const {
+    size_t all_bits_set = (1 << kFlagChangesCount) - 1;
+    return all_bits_set == (flags_ & all_bits_set);
+  }
+
+  bool DependsOn(SideEffects other) const {
+    size_t depends_flags = other.ComputeDependsFlags();
+    return (flags_ & depends_flags) != 0;
+  }
+
+  bool HasDependencies() const {
+    int count = kFlagDependsOnCount - kFlagChangesCount;
+    size_t all_bits_set = (1 << count) - 1;
+    return ((flags_ >> kFlagChangesCount) & all_bits_set) != 0;
+  }
+
+ private:
+  static constexpr int kFlagChangesSomething = 0;
+  static constexpr int kFlagChangesCount = kFlagChangesSomething + 1;
+
+  static constexpr int kFlagDependsOnSomething = kFlagChangesCount;
+  static constexpr int kFlagDependsOnCount = kFlagDependsOnSomething + 1;
+
+  explicit SideEffects(size_t flags) : flags_(flags) {}
+
+  size_t ComputeDependsFlags() const {
+    return flags_ << kFlagChangesCount;
+  }
+
+  size_t flags_;
+};
+
 class HInstruction : public ArenaObject {
  public:
-  HInstruction()
+  explicit HInstruction(SideEffects side_effects)
       : previous_(nullptr),
         next_(nullptr),
         block_(nullptr),
@@ -490,10 +617,17 @@
         environment_(nullptr),
         locations_(nullptr),
         live_interval_(nullptr),
-        lifetime_position_(kNoLifetime) {}
+        lifetime_position_(kNoLifetime),
+        side_effects_(side_effects) {}
 
   virtual ~HInstruction() {}
 
+#define DECLARE_KIND(type) k##type,
+  enum InstructionKind {
+    FOR_EACH_INSTRUCTION(DECLARE_KIND)
+  };
+#undef DECLARE_KIND
+
   HInstruction* GetNext() const { return next_; }
   HInstruction* GetPrevious() const { return previous_; }
 
@@ -501,8 +635,9 @@
   void SetBlock(HBasicBlock* block) { block_ = block; }
   bool IsInBlock() const { return block_ != nullptr; }
   bool IsInLoop() const { return block_->IsInLoop(); }
+  bool IsLoopHeaderPhi() { return IsPhi() && block_->IsLoopHeader(); }
 
-  virtual size_t InputCount() const  = 0;
+  virtual size_t InputCount() const = 0;
   virtual HInstruction* InputAt(size_t i) const = 0;
 
   virtual void Accept(HGraphVisitor* visitor) = 0;
@@ -513,17 +648,20 @@
 
   virtual bool NeedsEnvironment() const { return false; }
   virtual bool IsControlFlow() const { return false; }
+  bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
 
   void AddUseAt(HInstruction* user, size_t index) {
     uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HInstruction>(user, index, uses_);
   }
 
   void AddEnvUseAt(HEnvironment* user, size_t index) {
+    DCHECK(user != nullptr);
     env_uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HEnvironment>(
         user, index, env_uses_);
   }
 
   void RemoveUser(HInstruction* user, size_t index);
+  void RemoveEnvironmentUser(HEnvironment* user, size_t index);
 
   HUseListNode<HInstruction>* GetUses() const { return uses_; }
   HUseListNode<HEnvironment>* GetEnvUses() const { return env_uses_; }
@@ -542,6 +680,10 @@
     return result;
   }
 
+  // Does this instruction dominate `other_instruction`?  Aborts if
+  // this instruction and `other_instruction` are both phis.
+  bool Dominates(HInstruction* other_instruction) const;
+
   int GetId() const { return id_; }
   void SetId(int id) { id_ = id; }
 
@@ -553,6 +695,10 @@
   HEnvironment* GetEnvironment() const { return environment_; }
   void SetEnvironment(HEnvironment* environment) { environment_ = environment; }
 
+  // Returns the number of entries in the environment. Typically, that is the
+  // number of dex registers in a method. It could be more in case of inlining.
+  size_t EnvironmentSize() const;
+
   LocationSummary* GetLocations() const { return locations_; }
   void SetLocations(LocationSummary* locations) { locations_ = locations; }
 
@@ -563,12 +709,41 @@
   }
 
 #define INSTRUCTION_TYPE_CHECK(type)                                           \
-  bool Is##type() { return (As##type() != nullptr); }                          \
+  bool Is##type() const { return (As##type() != nullptr); }                    \
+  virtual const H##type* As##type() const { return nullptr; }                  \
   virtual H##type* As##type() { return nullptr; }
 
   FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
 #undef INSTRUCTION_TYPE_CHECK
 
+  // Returns whether the instruction can be moved within the graph.
+  virtual bool CanBeMoved() const { return false; }
+
+  // Returns whether the two instructions are of the same kind.
+  virtual bool InstructionTypeEquals(HInstruction* other) const { return false; }
+
+  // Returns whether any data encoded in the two instructions is equal.
+  // This method does not look at the inputs. Both instructions must be
+  // of the same type, otherwise the method has undefined behavior.
+  virtual bool InstructionDataEquals(HInstruction* other) const { return false; }
+
+  // Returns whether two instructions are equal, that is:
+  // 1) They have the same type and contain the same data,
+  // 2) Their inputs are identical.
+  bool Equals(HInstruction* other) const;
+
+  virtual InstructionKind GetKind() const = 0;
+
+  virtual size_t ComputeHashCode() const {
+    size_t result = GetKind();
+    for (size_t i = 0, e = InputCount(); i < e; ++i) {
+      result = (result * 31) + InputAt(i)->GetId();
+    }
+    return result;
+  }
+
+  SideEffects GetSideEffects() const { return side_effects_; }
+
   size_t GetLifetimePosition() const { return lifetime_position_; }
   void SetLifetimePosition(size_t position) { lifetime_position_ = position; }
   LiveInterval* GetLiveInterval() const { return live_interval_; }
@@ -582,7 +757,7 @@
 
   // An instruction gets an id when it is added to the graph.
   // It reflects creation order. A negative id means the instruction
-  // has not beed added to the graph.
+  // has not been added to the graph.
   int id_;
 
   // When doing liveness analysis, instructions that have uses get an SSA index.
@@ -594,6 +769,8 @@
   // List of environments that contain this instruction.
   HUseListNode<HEnvironment>* env_uses_;
 
+  // The environment associated with this instruction. Not null if the instruction
+  // might jump out of the method.
   HEnvironment* environment_;
 
   // Set by the code generator.
@@ -606,6 +783,8 @@
   // order of blocks where this instruction's live interval start.
   size_t lifetime_position_;
 
+  const SideEffects side_effects_;
+
   friend class HBasicBlock;
   friend class HInstructionList;
 
@@ -659,10 +838,16 @@
     vregs_.Put(index, instruction);
   }
 
+  HInstruction* GetInstructionAt(size_t index) const {
+    return vregs_.Get(index);
+  }
+
   GrowableArray<HInstruction*>* GetVRegs() {
     return &vregs_;
   }
 
+  size_t Size() const { return vregs_.Size(); }
+
  private:
   GrowableArray<HInstruction*> vregs_;
 
@@ -776,7 +961,8 @@
 template<intptr_t N>
 class HTemplateInstruction: public HInstruction {
  public:
-  HTemplateInstruction<N>() : inputs_() {}
+  HTemplateInstruction<N>(SideEffects side_effects)
+      : HInstruction(side_effects), inputs_() {}
   virtual ~HTemplateInstruction() {}
 
   virtual size_t InputCount() const { return N; }
@@ -794,9 +980,10 @@
 };
 
 template<intptr_t N>
-class HExpression: public HTemplateInstruction<N> {
+class HExpression : public HTemplateInstruction<N> {
  public:
-  explicit HExpression<N>(Primitive::Type type) : type_(type) {}
+  HExpression<N>(Primitive::Type type, SideEffects side_effects)
+      : HTemplateInstruction<N>(side_effects), type_(type) {}
   virtual ~HExpression() {}
 
   virtual Primitive::Type GetType() const { return type_; }
@@ -809,7 +996,7 @@
 // instruction that branches to the exit block.
 class HReturnVoid : public HTemplateInstruction<0> {
  public:
-  HReturnVoid() {}
+  HReturnVoid() : HTemplateInstruction(SideEffects::None()) {}
 
   virtual bool IsControlFlow() const { return true; }
 
@@ -823,7 +1010,7 @@
 // instruction that branches to the exit block.
 class HReturn : public HTemplateInstruction<1> {
  public:
-  explicit HReturn(HInstruction* value) {
+  explicit HReturn(HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
     SetRawInputAt(0, value);
   }
 
@@ -840,7 +1027,7 @@
 // exit block.
 class HExit : public HTemplateInstruction<0> {
  public:
-  HExit() {}
+  HExit() : HTemplateInstruction(SideEffects::None()) {}
 
   virtual bool IsControlFlow() const { return true; }
 
@@ -853,14 +1040,14 @@
 // Jumps from one block to another.
 class HGoto : public HTemplateInstruction<0> {
  public:
-  HGoto() {}
+  HGoto() : HTemplateInstruction(SideEffects::None()) {}
+
+  virtual bool IsControlFlow() const { return true; }
 
   HBasicBlock* GetSuccessor() const {
     return GetBlock()->GetSuccessors().Get(0);
   }
 
-  virtual bool IsControlFlow() const { return true; }
-
   DECLARE_INSTRUCTION(Goto);
 
  private:
@@ -872,10 +1059,12 @@
 // two successors.
 class HIf : public HTemplateInstruction<1> {
  public:
-  explicit HIf(HInstruction* input) {
+  explicit HIf(HInstruction* input) : HTemplateInstruction(SideEffects::None()) {
     SetRawInputAt(0, input);
   }
 
+  virtual bool IsControlFlow() const { return true; }
+
   HBasicBlock* IfTrueSuccessor() const {
     return GetBlock()->GetSuccessors().Get(0);
   }
@@ -884,8 +1073,6 @@
     return GetBlock()->GetSuccessors().Get(1);
   }
 
-  virtual bool IsControlFlow() const { return true; }
-
   DECLARE_INSTRUCTION(If);
 
   virtual bool IsIfInstruction() const { return true; }
@@ -898,7 +1085,7 @@
  public:
   HBinaryOperation(Primitive::Type result_type,
                    HInstruction* left,
-                   HInstruction* right) : HExpression(result_type) {
+                   HInstruction* right) : HExpression(result_type, SideEffects::None()) {
     SetRawInputAt(0, left);
     SetRawInputAt(1, right);
   }
@@ -909,6 +1096,20 @@
 
   virtual bool IsCommutative() { return false; }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
+  // Try to statically evaluate `operation` and return an HConstant
+  // containing the result of this evaluation.  If `operation` cannot
+  // be evaluated as a constant, return nullptr.
+  HConstant* TryStaticEvaluation(ArenaAllocator* allocator) const;
+
+  // Apply this operation to `x` and `y`.
+  virtual int32_t Evaluate(int32_t x, int32_t y) const = 0;
+  virtual int64_t Evaluate(int64_t x, int64_t y) const = 0;
+
+  DECLARE_INSTRUCTION(BinaryOperation);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HBinaryOperation);
 };
@@ -919,8 +1120,15 @@
       : HBinaryOperation(Primitive::kPrimBoolean, first, second) {}
 
   virtual bool IsCommutative() { return true; }
+
+  // For register allocation purposes, returns whether this instruction needs to be
+  // materialized (that is, not just be in the processor flags).
   bool NeedsMaterialization() const;
 
+  // For code generation purposes, returns whether this instruction is just before
+  // `if_`, and disregard moves in between.
+  bool IsBeforeWhenDisregardMoves(HIf* if_) const;
+
   DECLARE_INSTRUCTION(Condition);
 
   virtual IfCondition GetCondition() const = 0;
@@ -935,6 +1143,9 @@
   HEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x == y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x == y; }
+
   DECLARE_INSTRUCTION(Equal);
 
   virtual IfCondition GetCondition() const {
@@ -950,6 +1161,9 @@
   HNotEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x != y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x != y; }
+
   DECLARE_INSTRUCTION(NotEqual);
 
   virtual IfCondition GetCondition() const {
@@ -965,6 +1179,9 @@
   HLessThan(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x < y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x < y; }
+
   DECLARE_INSTRUCTION(LessThan);
 
   virtual IfCondition GetCondition() const {
@@ -980,6 +1197,9 @@
   HLessThanOrEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x <= y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x <= y; }
+
   DECLARE_INSTRUCTION(LessThanOrEqual);
 
   virtual IfCondition GetCondition() const {
@@ -995,6 +1215,9 @@
   HGreaterThan(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x > y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x > y; }
+
   DECLARE_INSTRUCTION(GreaterThan);
 
   virtual IfCondition GetCondition() const {
@@ -1010,6 +1233,9 @@
   HGreaterThanOrEqual(HInstruction* first, HInstruction* second)
       : HCondition(first, second) {}
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x >= y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x >= y; }
+
   DECLARE_INSTRUCTION(GreaterThanOrEqual);
 
   virtual IfCondition GetCondition() const {
@@ -1031,6 +1257,19 @@
     DCHECK_EQ(type, second->GetType());
   }
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const {
+    return
+      x == y ? 0 :
+      x > y ? 1 :
+      -1;
+  }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const {
+    return
+      x == y ? 0 :
+      x > y ? 1 :
+      -1;
+  }
+
   DECLARE_INSTRUCTION(Compare);
 
  private:
@@ -1040,7 +1279,8 @@
 // A local in the graph. Corresponds to a Dex register.
 class HLocal : public HTemplateInstruction<0> {
  public:
-  explicit HLocal(uint16_t reg_number) : reg_number_(reg_number) {}
+  explicit HLocal(uint16_t reg_number)
+      : HTemplateInstruction(SideEffects::None()), reg_number_(reg_number) {}
 
   DECLARE_INSTRUCTION(Local);
 
@@ -1056,7 +1296,8 @@
 // Load a given local. The local is an input of this instruction.
 class HLoadLocal : public HExpression<1> {
  public:
-  explicit HLoadLocal(HLocal* local, Primitive::Type type) : HExpression(type) {
+  HLoadLocal(HLocal* local, Primitive::Type type)
+      : HExpression(type, SideEffects::None()) {
     SetRawInputAt(0, local);
   }
 
@@ -1072,7 +1313,7 @@
 // and the local.
 class HStoreLocal : public HTemplateInstruction<2> {
  public:
-  HStoreLocal(HLocal* local, HInstruction* value) {
+  HStoreLocal(HLocal* local, HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
     SetRawInputAt(0, local);
     SetRawInputAt(1, value);
   }
@@ -1087,7 +1328,9 @@
 
 class HConstant : public HExpression<0> {
  public:
-  explicit HConstant(Primitive::Type type) : HExpression(type) {}
+  explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+
+  virtual bool CanBeMoved() const { return true; }
 
   DECLARE_INSTRUCTION(Constant);
 
@@ -1103,6 +1346,12 @@
 
   int32_t GetValue() const { return value_; }
 
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    return other->AsIntConstant()->value_ == value_;
+  }
+
+  virtual size_t ComputeHashCode() const { return GetValue(); }
+
   DECLARE_INSTRUCTION(IntConstant);
 
  private:
@@ -1117,6 +1366,12 @@
 
   int64_t GetValue() const { return value_; }
 
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    return other->AsLongConstant()->value_ == value_;
+  }
+
+  virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); }
+
   DECLARE_INSTRUCTION(LongConstant);
 
  private:
@@ -1131,7 +1386,8 @@
           uint32_t number_of_arguments,
           Primitive::Type return_type,
           uint32_t dex_pc)
-    : inputs_(arena, number_of_arguments),
+    : HInstruction(SideEffects::All()),
+      inputs_(arena, number_of_arguments),
       return_type_(return_type),
       dex_pc_(dex_pc) {
     inputs_.SetSize(number_of_arguments);
@@ -1185,10 +1441,32 @@
   DISALLOW_COPY_AND_ASSIGN(HInvokeStatic);
 };
 
+class HInvokeVirtual : public HInvoke {
+ public:
+  HInvokeVirtual(ArenaAllocator* arena,
+                 uint32_t number_of_arguments,
+                 Primitive::Type return_type,
+                 uint32_t dex_pc,
+                 uint32_t vtable_index)
+      : HInvoke(arena, number_of_arguments, return_type, dex_pc),
+        vtable_index_(vtable_index) {}
+
+  uint32_t GetVTableIndex() const { return vtable_index_; }
+
+  DECLARE_INSTRUCTION(InvokeVirtual);
+
+ private:
+  const uint32_t vtable_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual);
+};
+
 class HNewInstance : public HExpression<0> {
  public:
-  HNewInstance(uint32_t dex_pc, uint16_t type_index) : HExpression(Primitive::kPrimNot),
-    dex_pc_(dex_pc), type_index_(type_index) {}
+  HNewInstance(uint32_t dex_pc, uint16_t type_index)
+      : HExpression(Primitive::kPrimNot, SideEffects::None()),
+        dex_pc_(dex_pc),
+        type_index_(type_index) {}
 
   uint32_t GetDexPc() const { return dex_pc_; }
   uint16_t GetTypeIndex() const { return type_index_; }
@@ -1212,6 +1490,9 @@
 
   virtual bool IsCommutative() { return true; }
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x + y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x + y; }
+
   DECLARE_INSTRUCTION(Add);
 
  private:
@@ -1225,6 +1506,9 @@
 
   virtual bool IsCommutative() { return false; }
 
+  virtual int32_t Evaluate(int32_t x, int32_t y) const { return x + y; }
+  virtual int64_t Evaluate(int64_t x, int64_t y) const { return x + y; }
+
   DECLARE_INSTRUCTION(Sub);
 
  private:
@@ -1236,7 +1520,7 @@
 class HParameterValue : public HExpression<0> {
  public:
   HParameterValue(uint8_t index, Primitive::Type parameter_type)
-      : HExpression(parameter_type), index_(index) {}
+      : HExpression(parameter_type, SideEffects::None()), index_(index) {}
 
   uint8_t GetIndex() const { return index_; }
 
@@ -1252,10 +1536,13 @@
 
 class HNot : public HExpression<1> {
  public:
-  explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean) {
+  explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean, SideEffects::None()) {
     SetRawInputAt(0, input);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   DECLARE_INSTRUCTION(Not);
 
  private:
@@ -1265,7 +1552,8 @@
 class HPhi : public HInstruction {
  public:
   HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
-      : inputs_(arena, number_of_inputs),
+      : HInstruction(SideEffects::None()),
+        inputs_(arena, number_of_inputs),
         reg_number_(reg_number),
         type_(type),
         is_live_(false) {
@@ -1305,10 +1593,13 @@
 class HNullCheck : public HExpression<1> {
  public:
   HNullCheck(HInstruction* value, uint32_t dex_pc)
-      : HExpression(value->GetType()), dex_pc_(dex_pc) {
+      : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
     SetRawInputAt(0, value);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   virtual bool NeedsEnvironment() const { return true; }
 
   uint32_t GetDexPc() const { return dex_pc_; }
@@ -1323,13 +1614,15 @@
 
 class FieldInfo : public ValueObject {
  public:
-  explicit FieldInfo(MemberOffset field_offset)
-      : field_offset_(field_offset) {}
+  FieldInfo(MemberOffset field_offset, Primitive::Type field_type)
+      : field_offset_(field_offset), field_type_(field_type) {}
 
   MemberOffset GetFieldOffset() const { return field_offset_; }
+  Primitive::Type GetFieldType() const { return field_type_; }
 
  private:
   const MemberOffset field_offset_;
+  const Primitive::Type field_type_;
 };
 
 class HInstanceFieldGet : public HExpression<1> {
@@ -1337,11 +1630,23 @@
   HInstanceFieldGet(HInstruction* value,
                     Primitive::Type field_type,
                     MemberOffset field_offset)
-      : HExpression(field_type), field_info_(field_offset) {
+      : HExpression(field_type, SideEffects::DependsOnSomething()),
+        field_info_(field_offset, field_type) {
     SetRawInputAt(0, value);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    size_t other_offset = other->AsInstanceFieldGet()->GetFieldOffset().SizeValue();
+    return other_offset == GetFieldOffset().SizeValue();
+  }
+
+  virtual size_t ComputeHashCode() const {
+    return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
+  }
+
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+  Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
 
   DECLARE_INSTRUCTION(InstanceFieldGet);
 
@@ -1355,13 +1660,16 @@
  public:
   HInstanceFieldSet(HInstruction* object,
                     HInstruction* value,
+                    Primitive::Type field_type,
                     MemberOffset field_offset)
-      : field_info_(field_offset) {
+      : HTemplateInstruction(SideEffects::ChangesSomething()),
+        field_info_(field_offset, field_type) {
     SetRawInputAt(0, object);
     SetRawInputAt(1, value);
   }
 
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+  Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
 
   DECLARE_INSTRUCTION(InstanceFieldSet);
 
@@ -1374,11 +1682,14 @@
 class HArrayGet : public HExpression<2> {
  public:
   HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
-      : HExpression(type) {
+      : HExpression(type, SideEffects::DependsOnSomething()) {
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   DECLARE_INSTRUCTION(ArrayGet);
 
  private:
@@ -1390,7 +1701,11 @@
   HArraySet(HInstruction* array,
             HInstruction* index,
             HInstruction* value,
-            uint32_t dex_pc) : dex_pc_(dex_pc) {
+            Primitive::Type component_type,
+            uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::ChangesSomething()),
+        dex_pc_(dex_pc),
+        component_type_(component_type) {
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
     SetRawInputAt(2, value);
@@ -1404,20 +1719,29 @@
 
   uint32_t GetDexPc() const { return dex_pc_; }
 
+  Primitive::Type GetComponentType() const { return component_type_; }
+
   DECLARE_INSTRUCTION(ArraySet);
 
  private:
   const uint32_t dex_pc_;
+  const Primitive::Type component_type_;
 
   DISALLOW_COPY_AND_ASSIGN(HArraySet);
 };
 
 class HArrayLength : public HExpression<1> {
  public:
-  explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) {
+  explicit HArrayLength(HInstruction* array)
+      : HExpression(Primitive::kPrimInt, SideEffects::None()) {
+    // Note that arrays do not change length, so the instruction does not
+    // depend on any write.
     SetRawInputAt(0, array);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   DECLARE_INSTRUCTION(ArrayLength);
 
  private:
@@ -1427,12 +1751,15 @@
 class HBoundsCheck : public HExpression<2> {
  public:
   HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
-      : HExpression(index->GetType()), dex_pc_(dex_pc) {
+      : HExpression(index->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
     DCHECK(index->GetType() == Primitive::kPrimInt);
     SetRawInputAt(0, index);
     SetRawInputAt(1, length);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   virtual bool NeedsEnvironment() const { return true; }
 
   uint32_t GetDexPc() const { return dex_pc_; }
@@ -1454,7 +1781,7 @@
  */
 class HTemporary : public HTemplateInstruction<0> {
  public:
-  explicit HTemporary(size_t index) : index_(index) {}
+  explicit HTemporary(size_t index) : HTemplateInstruction(SideEffects::None()), index_(index) {}
 
   size_t GetIndex() const { return index_; }
 
@@ -1466,10 +1793,29 @@
   DISALLOW_COPY_AND_ASSIGN(HTemporary);
 };
 
+class HSuspendCheck : public HTemplateInstruction<0> {
+ public:
+  explicit HSuspendCheck(uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc) {}
+
+  virtual bool NeedsEnvironment() const {
+    return true;
+  }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  DECLARE_INSTRUCTION(SuspendCheck);
+
+ private:
+  const uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HSuspendCheck);
+};
+
 class MoveOperands : public ArenaObject {
  public:
-  MoveOperands(Location source, Location destination)
-      : source_(source), destination_(destination) {}
+  MoveOperands(Location source, Location destination, HInstruction* instruction)
+      : source_(source), destination_(destination), instruction_(instruction) {}
 
   Location GetSource() const { return source_; }
   Location GetDestination() const { return destination_; }
@@ -1517,9 +1863,16 @@
     return source_.IsInvalid();
   }
 
+  HInstruction* GetInstruction() const { return instruction_; }
+
  private:
   Location source_;
   Location destination_;
+  // The instruction this move is assocatied with. Null when this move is
+  // for moving an input in the expected locations of user (including a phi user).
+  // This is only used in debug mode, to ensure we do not connect interval siblings
+  // in the same parallel move.
+  HInstruction* instruction_;
 
   DISALLOW_COPY_AND_ASSIGN(MoveOperands);
 };
@@ -1528,9 +1881,16 @@
 
 class HParallelMove : public HTemplateInstruction<0> {
  public:
-  explicit HParallelMove(ArenaAllocator* arena) : moves_(arena, kDefaultNumberOfMoves) {}
+  explicit HParallelMove(ArenaAllocator* arena)
+      : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
 
   void AddMove(MoveOperands* move) {
+    if (kIsDebugBuild && move->GetInstruction() != nullptr) {
+      for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
+        DCHECK_NE(moves_.Get(i)->GetInstruction(), move->GetInstruction())
+          << "Doing parallel moves for the same instruction.";
+      }
+    }
     moves_.Add(move);
   }
 
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
new file mode 100644
index 0000000..b75bacb
--- /dev/null
+++ b/compiler/optimizing/nodes_test.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nodes.h"
+#include "utils/arena_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+/**
+ * Test that removing instruction from the graph removes itself from user lists
+ * and environment lists.
+ */
+TEST(Node, RemoveInstruction) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(new (&allocator) HGoto());
+
+  HBasicBlock* first_block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(first_block);
+  entry->AddSuccessor(first_block);
+  HInstruction* null_check = new (&allocator) HNullCheck(parameter, 0);
+  first_block->AddInstruction(null_check);
+  first_block->AddInstruction(new (&allocator) HReturnVoid());
+
+  HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(exit_block);
+  first_block->AddSuccessor(exit_block);
+  exit_block->AddInstruction(new (&allocator) HExit());
+
+  HEnvironment* environment = new (&allocator) HEnvironment(&allocator, 1);
+  null_check->SetEnvironment(environment);
+  environment->SetRawEnvAt(0, parameter);
+  parameter->AddEnvUseAt(null_check->GetEnvironment(), 0);
+
+  ASSERT_TRUE(parameter->HasEnvironmentUses());
+  ASSERT_TRUE(parameter->HasUses());
+
+  first_block->RemoveInstruction(null_check);
+
+  ASSERT_FALSE(parameter->HasEnvironmentUses());
+  ASSERT_FALSE(parameter->HasUses());
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8a5077b..65bdb18 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -14,15 +14,19 @@
  * limitations under the License.
  */
 
+#include "optimizing_compiler.h"
+
 #include <fstream>
 #include <stdint.h>
 
 #include "builder.h"
 #include "code_generator.h"
-#include "compilers.h"
+#include "compiler.h"
 #include "driver/compiler_driver.h"
 #include "driver/dex_compilation_unit.h"
 #include "graph_visualizer.h"
+#include "gvn.h"
+#include "instruction_simplifier.h"
 #include "nodes.h"
 #include "register_allocator.h"
 #include "ssa_phi_elimination.h"
@@ -36,7 +40,7 @@
  */
 class CodeVectorAllocator FINAL : public CodeAllocator {
  public:
-  CodeVectorAllocator() { }
+  CodeVectorAllocator() {}
 
   virtual uint8_t* Allocate(size_t size) {
     size_ = size;
@@ -65,12 +69,133 @@
  */
 static const char* kStringFilter = "";
 
-OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver) : QuickCompiler(driver) {
+class OptimizingCompiler FINAL : public Compiler {
+ public:
+  explicit OptimizingCompiler(CompilerDriver* driver);
+  ~OptimizingCompiler();
+
+  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+      OVERRIDE;
+
+  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+                          uint32_t access_flags,
+                          InvokeType invoke_type,
+                          uint16_t class_def_idx,
+                          uint32_t method_idx,
+                          jobject class_loader,
+                          const DexFile& dex_file) const OVERRIDE;
+
+  CompiledMethod* TryCompile(const DexFile::CodeItem* code_item,
+                             uint32_t access_flags,
+                             InvokeType invoke_type,
+                             uint16_t class_def_idx,
+                             uint32_t method_idx,
+                             jobject class_loader,
+                             const DexFile& dex_file) const;
+
+  // For the following methods we will use the fallback. This is a delegation pattern.
+  CompiledMethod* JniCompile(uint32_t access_flags,
+                             uint32_t method_idx,
+                             const DexFile& dex_file) const OVERRIDE;
+
+  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool WriteElf(art::File* file,
+                OatWriter* oat_writer,
+                const std::vector<const art::DexFile*>& dex_files,
+                const std::string& android_root,
+                bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
+
+  void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
+
+  void Init() const OVERRIDE;
+
+  void UnInit() const OVERRIDE;
+
+ private:
+  // Whether we should run any optimization or register allocation. If false, will
+  // just run the code generation after the graph was built.
+  const bool run_optimizations_;
+  mutable AtomicInteger total_compiled_methods_;
+  mutable AtomicInteger unoptimized_compiled_methods_;
+  mutable AtomicInteger optimized_compiled_methods_;
+
+  std::unique_ptr<std::ostream> visualizer_output_;
+
+  // Delegate to another compiler in case the optimizing compiler cannot compile a method.
+  // Currently the fallback is the quick compiler.
+  std::unique_ptr<Compiler> delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
+};
+
+static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */
+
+OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
+    : Compiler(driver, kMaximumCompilationTimeBeforeWarning),
+      run_optimizations_(
+          driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
+      total_compiled_methods_(0),
+      unoptimized_compiled_methods_(0),
+      optimized_compiled_methods_(0),
+      delegate_(Create(driver, Compiler::Kind::kQuick)) {
   if (kIsVisualizerEnabled) {
     visualizer_output_.reset(new std::ofstream("art.cfg"));
   }
 }
 
+void OptimizingCompiler::Init() const {
+  delegate_->Init();
+}
+
+void OptimizingCompiler::UnInit() const {
+  delegate_->UnInit();
+}
+
+OptimizingCompiler::~OptimizingCompiler() {
+  if (total_compiled_methods_ == 0) {
+    LOG(INFO) << "Did not compile any method.";
+  } else {
+    size_t unoptimized_percent = (unoptimized_compiled_methods_ * 100 / total_compiled_methods_);
+    size_t optimized_percent = (optimized_compiled_methods_ * 100 / total_compiled_methods_);
+    LOG(INFO) << "Compiled " << total_compiled_methods_ << " methods: "
+              << unoptimized_percent << "% (" << unoptimized_compiled_methods_ << ") unoptimized, "
+              << optimized_percent << "% (" << optimized_compiled_methods_ << ") optimized.";
+  }
+}
+
+bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
+                                          CompilationUnit* cu) const {
+  return delegate_->CanCompileMethod(method_idx, dex_file, cu);
+}
+
+CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags,
+                                               uint32_t method_idx,
+                                               const DexFile& dex_file) const {
+  return delegate_->JniCompile(access_flags, method_idx, dex_file);
+}
+
+uintptr_t OptimizingCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
+  return delegate_->GetEntryPointOf(method);
+}
+
+bool OptimizingCompiler::WriteElf(art::File* file, OatWriter* oat_writer,
+                                  const std::vector<const art::DexFile*>& dex_files,
+                                  const std::string& android_root, bool is_host) const {
+  return delegate_->WriteElf(file, oat_writer, dex_files, android_root, is_host);
+}
+
+Backend* OptimizingCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+  return delegate_->GetCodeGenerator(cu, compilation_unit);
+}
+
+void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu) const {
+  delegate_->InitCompilationUnit(cu);
+}
+
 CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item,
                                                uint32_t access_flags,
                                                InvokeType invoke_type,
@@ -78,6 +203,7 @@
                                                uint32_t method_idx,
                                                jobject class_loader,
                                                const DexFile& dex_file) const {
+  total_compiled_methods_++;
   InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
   // Always use the thumb2 assembler: some runtime functionality (like implicit stack
   // overflow checks) assume thumb2.
@@ -127,7 +253,8 @@
 
   CodeVectorAllocator allocator;
 
-  if (RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
+  if (run_optimizations_ && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
+    optimized_compiled_methods_++;
     graph->BuildDominatorTree();
     graph->TransformToSSA();
     visualizer.DumpGraph("ssa");
@@ -135,6 +262,9 @@
 
     SsaRedundantPhiElimination(graph).Run();
     SsaDeadPhiElimination(graph).Run();
+    InstructionSimplifier(graph).Run();
+    GlobalValueNumberer(graph->GetArena(), graph).Run();
+    visualizer.DumpGraph(kGVNPassName);
 
     SsaLivenessAnalysis liveness(*graph, codegen);
     liveness.Analyze();
@@ -145,9 +275,29 @@
 
     visualizer.DumpGraph(kRegisterAllocatorPassName);
     codegen->CompileOptimized(&allocator);
+
+    std::vector<uint8_t> mapping_table;
+    SrcMap src_mapping_table;
+    codegen->BuildMappingTable(&mapping_table,
+            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+                 &src_mapping_table : nullptr);
+
+    std::vector<uint8_t> stack_map;
+    codegen->BuildStackMaps(&stack_map);
+
+    return new CompiledMethod(GetCompilerDriver(),
+                              instruction_set,
+                              allocator.GetMemory(),
+                              codegen->GetFrameSize(),
+                              codegen->GetCoreSpillMask(),
+                              0, /* FPR spill mask, unused */
+                              mapping_table,
+                              stack_map);
   } else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
     LOG(FATAL) << "Could not allocate registers in optimizing compiler";
+    return nullptr;
   } else {
+    unoptimized_compiled_methods_++;
     codegen->CompileBaseline(&allocator);
 
     // Run these phases to get some test coverage.
@@ -155,28 +305,56 @@
     graph->TransformToSSA();
     visualizer.DumpGraph("ssa");
     graph->FindNaturalLoops();
+    SsaRedundantPhiElimination(graph).Run();
+    SsaDeadPhiElimination(graph).Run();
+    GlobalValueNumberer(graph->GetArena(), graph).Run();
     SsaLivenessAnalysis liveness(*graph, codegen);
     liveness.Analyze();
     visualizer.DumpGraph(kLivenessPassName);
+
+    std::vector<uint8_t> mapping_table;
+    SrcMap src_mapping_table;
+    codegen->BuildMappingTable(&mapping_table,
+            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+                 &src_mapping_table : nullptr);
+    std::vector<uint8_t> vmap_table;
+    codegen->BuildVMapTable(&vmap_table);
+    std::vector<uint8_t> gc_map;
+    codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+
+    return new CompiledMethod(GetCompilerDriver(),
+                              instruction_set,
+                              allocator.GetMemory(),
+                              codegen->GetFrameSize(),
+                              codegen->GetCoreSpillMask(),
+                              0, /* FPR spill mask, unused */
+                              &src_mapping_table,
+                              mapping_table,
+                              vmap_table,
+                              gc_map,
+                              nullptr);
+  }
+}
+
+CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
+                                            uint32_t access_flags,
+                                            InvokeType invoke_type,
+                                            uint16_t class_def_idx,
+                                            uint32_t method_idx,
+                                            jobject class_loader,
+                                            const DexFile& dex_file) const {
+  CompiledMethod* method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
+                                      method_idx, class_loader, dex_file);
+  if (method != nullptr) {
+    return method;
   }
 
-  std::vector<uint8_t> mapping_table;
-  codegen->BuildMappingTable(&mapping_table);
-  std::vector<uint8_t> vmap_table;
-  codegen->BuildVMapTable(&vmap_table);
-  std::vector<uint8_t> gc_map;
-  codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+  return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
+                            class_loader, dex_file);
+}
 
-  return new CompiledMethod(GetCompilerDriver(),
-                            instruction_set,
-                            allocator.GetMemory(),
-                            codegen->GetFrameSize(),
-                            codegen->GetCoreSpillMask(),
-                            0, /* FPR spill mask, unused */
-                            mapping_table,
-                            vmap_table,
-                            gc_map,
-                            nullptr);
+Compiler* CreateOptimizingCompiler(CompilerDriver* driver) {
+  return new OptimizingCompiler(driver);
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
new file mode 100644
index 0000000..a415eca
--- /dev/null
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
+#define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
+
+namespace art {
+
+class Compiler;
+class CompilerDriver;
+
+Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
+
+}
+
+#endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 36a6a21..6dd53e5 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -17,8 +17,14 @@
 #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
 #define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
 
+#include "nodes.h"
+#include "builder.h"
+#include "dex_file.h"
+#include "dex_instruction.h"
 #include "ssa_liveness_analysis.h"
 
+#include "gtest/gtest.h"
+
 namespace art {
 
 #define NUM_INSTRUCTIONS(...)  \
@@ -48,6 +54,46 @@
   return interval;
 }
 
+void RemoveSuspendChecks(HGraph* graph) {
+  for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
+    for (HInstructionIterator it(graph->GetBlocks().Get(i)->GetInstructions());
+         !it.Done();
+         it.Advance()) {
+      HInstruction* current = it.Current();
+      if (current->IsSuspendCheck()) {
+        current->GetBlock()->RemoveInstruction(current);
+      }
+    }
+  }
+}
+
+// Create a control-flow graph from Dex instructions.
+inline HGraph* CreateCFG(ArenaAllocator* allocator, const uint16_t* data) {
+  HGraphBuilder builder(allocator);
+  const DexFile::CodeItem* item =
+    reinterpret_cast<const DexFile::CodeItem*>(data);
+  HGraph* graph = builder.BuildGraph(*item);
+  return graph;
+}
+
+// Naive string diff data type.
+typedef std::list<std::pair<std::string, std::string>> diff_t;
+
+// An alias for the empty string used to make it clear that a line is
+// removed in a diff.
+static const std::string removed = "";
+
+// Naive patch command: apply a diff to a string.
+inline std::string Patch(const std::string& original, const diff_t& diff) {
+  std::string result = original;
+  for (const auto& p : diff) {
+    std::string::size_type pos = result.find(p.first);
+    EXPECT_NE(pos, std::string::npos);
+    result.replace(pos, p.first.size(), p.second);
+  }
+  return result;
+}
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 093856d..863e107 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -71,7 +71,8 @@
   for (size_t i = 0; i < number_of_moves; ++i) {
     moves->AddMove(new (allocator) MoveOperands(
         Location::RegisterLocation(ManagedRegister(operands[i][0])),
-        Location::RegisterLocation(ManagedRegister(operands[i][1]))));
+        Location::RegisterLocation(ManagedRegister(operands[i][1])),
+        nullptr));
   }
   return moves;
 }
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 7e604e9..da6b294 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -45,7 +45,8 @@
 
   const char* expected =
       "BasicBlock 0, succ: 1\n"
-      "  2: Goto 1\n"
+      "  2: SuspendCheck\n"
+      "  3: Goto 1\n"
       "BasicBlock 1, pred: 0, succ: 2\n"
       "  0: ReturnVoid\n"
       "BasicBlock 2, pred: 1\n"
@@ -57,7 +58,8 @@
 TEST(PrettyPrinterTest, CFG1) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  3: Goto 1\n"
+    "  3: SuspendCheck\n"
+    "  4: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 2\n"
     "  0: Goto 2\n"
     "BasicBlock 2, pred: 1, succ: 3\n"
@@ -76,7 +78,8 @@
 TEST(PrettyPrinterTest, CFG2) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  4: Goto 1\n"
+    "  4: SuspendCheck\n"
+    "  5: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 2\n"
     "  0: Goto 2\n"
     "BasicBlock 2, pred: 1, succ: 3\n"
@@ -97,15 +100,17 @@
 TEST(PrettyPrinterTest, CFG3) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  4: Goto 1\n"
+    "  5: SuspendCheck\n"
+    "  6: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 3\n"
     "  0: Goto 3\n"
     "BasicBlock 2, pred: 3, succ: 4\n"
     "  1: ReturnVoid\n"
     "BasicBlock 3, pred: 1, succ: 2\n"
-    "  2: Goto 2\n"
+    "  2: SuspendCheck\n"
+    "  3: Goto 2\n"
     "BasicBlock 4, pred: 2\n"
-    "  3: Exit\n";
+    "  4: Exit\n";
 
   const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::GOTO | 0x200,
@@ -132,11 +137,13 @@
 TEST(PrettyPrinterTest, CFG4) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  2: Goto 1\n"
+    "  3: SuspendCheck\n"
+    "  4: Goto 1\n"
     "BasicBlock 1, pred: 0, 1, succ: 1\n"
-    "  0: Goto 1\n"
+    "  0: SuspendCheck\n"
+    "  1: Goto 1\n"
     "BasicBlock 2\n"
-    "  1: Exit\n";
+    "  2: Exit\n";
 
   const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::NOP,
@@ -153,13 +160,15 @@
 TEST(PrettyPrinterTest, CFG5) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  3: Goto 1\n"
+    "  4: SuspendCheck\n"
+    "  5: Goto 1\n"
     "BasicBlock 1, pred: 0, 2, succ: 3\n"
     "  0: ReturnVoid\n"
     "BasicBlock 2, succ: 1\n"
-    "  1: Goto 1\n"
+    "  1: SuspendCheck\n"
+    "  2: Goto 1\n"
     "BasicBlock 3, pred: 1\n"
-    "  2: Exit\n";
+    "  3: Exit\n";
 
   const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::RETURN_VOID,
@@ -174,7 +183,8 @@
     "BasicBlock 0, succ: 1\n"
     "  0: Local [4, 3, 2]\n"
     "  1: IntConstant [2]\n"
-    "  10: Goto 1\n"
+    "  10: SuspendCheck\n"
+    "  11: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 3, 2\n"
     "  2: StoreLocal(0, 1)\n"
     "  3: LoadLocal(0) [5]\n"
@@ -202,7 +212,8 @@
     "BasicBlock 0, succ: 1\n"
     "  0: Local [4, 3, 2]\n"
     "  1: IntConstant [2]\n"
-    "  10: Goto 1\n"
+    "  11: SuspendCheck\n"
+    "  12: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 3, 2\n"
     "  2: StoreLocal(0, 1)\n"
     "  3: LoadLocal(0) [5]\n"
@@ -212,9 +223,10 @@
     "BasicBlock 2, pred: 1, 3, succ: 3\n"
     "  7: Goto 3\n"
     "BasicBlock 3, pred: 1, 2, succ: 2\n"
-    "  8: Goto 2\n"
+    "  8: SuspendCheck\n"
+    "  9: Goto 2\n"
     "BasicBlock 4\n"
-    "  9: Exit\n";
+    "  10: Exit\n";
 
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
     Instruction::CONST_4 | 0 | 0,
@@ -230,7 +242,8 @@
     "BasicBlock 0, succ: 1\n"
     "  0: Local [2]\n"
     "  1: IntConstant [2]\n"
-    "  5: Goto 1\n"
+    "  5: SuspendCheck\n"
+    "  6: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 2\n"
     "  2: StoreLocal(0, 1)\n"
     "  3: ReturnVoid\n"
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index bd3a7d9..1d1d694 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -16,6 +16,7 @@
 
 #include "register_allocator.h"
 
+#include "base/bit_vector-inl.h"
 #include "code_generator.h"
 #include "ssa_liveness_analysis.h"
 
@@ -30,18 +31,27 @@
       : allocator_(allocator),
         codegen_(codegen),
         liveness_(liveness),
-        unhandled_(allocator, 0),
+        unhandled_core_intervals_(allocator, 0),
+        unhandled_fp_intervals_(allocator, 0),
+        unhandled_(nullptr),
         handled_(allocator, 0),
         active_(allocator, 0),
         inactive_(allocator, 0),
         physical_register_intervals_(allocator, codegen->GetNumberOfRegisters()),
+        temp_intervals_(allocator, 4),
         spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+        safepoints_(allocator, 0),
         processing_core_registers_(false),
         number_of_registers_(-1),
         registers_array_(nullptr),
-        blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())) {
+        blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())),
+        reserved_out_slots_(0),
+        maximum_number_of_live_registers_(0) {
   codegen->SetupBlockedRegisters(blocked_registers_);
   physical_register_intervals_.SetSize(codegen->GetNumberOfRegisters());
+  // Always reserve for the current method and the graph's max out registers.
+  // TODO: compute it instead.
+  reserved_out_slots_ = 1 + codegen->GetGraph()->GetMaximumNumberOfOutVRegs();
 }
 
 bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph,
@@ -54,7 +64,6 @@
          !it.Done();
          it.Advance()) {
       HInstruction* current = it.Current();
-      if (current->NeedsEnvironment()) return false;
       if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false;
       if (current->GetType() == Primitive::kPrimFloat) return false;
       if (current->GetType() == Primitive::kPrimDouble) return false;
@@ -64,17 +73,14 @@
 }
 
 static bool ShouldProcess(bool processing_core_registers, LiveInterval* interval) {
+  if (interval == nullptr) return false;
   bool is_core_register = (interval->GetType() != Primitive::kPrimDouble)
       && (interval->GetType() != Primitive::kPrimFloat);
   return processing_core_registers == is_core_register;
 }
 
 void RegisterAllocator::AllocateRegisters() {
-  processing_core_registers_ = true;
   AllocateRegistersInternal();
-  processing_core_registers_ = false;
-  AllocateRegistersInternal();
-
   Resolve();
 
   if (kIsDebugBuild) {
@@ -100,78 +106,139 @@
   interval->AddRange(start, end);
 }
 
-// TODO: make the register allocator understand instructions like HCondition
-// that may not need to be materialized.  It doesn't need to allocate any
-// registers for it.
 void RegisterAllocator::AllocateRegistersInternal() {
-  number_of_registers_ = processing_core_registers_
-      ? codegen_->GetNumberOfCoreRegisters()
-      : codegen_->GetNumberOfFloatingPointRegisters();
-
-  registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
-
   // Iterate post-order, to ensure the list is sorted, and the last added interval
   // is the one with the lowest start position.
-  for (size_t i = liveness_.GetNumberOfSsaValues(); i > 0; --i) {
-    HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i - 1);
-    LiveInterval* current = instruction->GetLiveInterval();
-    if (ShouldProcess(processing_core_registers_, current)) {
-      DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
-
-      LocationSummary* locations = instruction->GetLocations();
-      if (locations->GetTempCount() != 0) {
-        // Note that we already filtered out instructions requiring temporaries in
-        // RegisterAllocator::CanAllocateRegistersFor.
-        LOG(FATAL) << "Unimplemented";
-      }
-
-      // Some instructions define their output in fixed register/stack slot. We need
-      // to ensure we know these locations before doing register allocation. For a
-      // given register, we create an interval that covers these locations. The register
-      // will be unavailable at these locations when trying to allocate one for an
-      // interval.
-      //
-      // The backwards walking ensures the ranges are ordered on increasing start positions.
-      Location output = locations->Out();
-      size_t position = instruction->GetLifetimePosition();
-      if (output.IsRegister()) {
-        // Shift the interval's start by one to account for the blocked register.
-        current->SetFrom(position + 1);
-        current->SetRegister(output.reg().RegId());
-        BlockRegister(output, position, position + 1, instruction->GetType());
-      } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
-        current->SetSpillSlot(output.GetStackIndex());
-      }
-      for (size_t i = 0; i < instruction->InputCount(); ++i) {
-        Location input = locations->InAt(i);
-        if (input.IsRegister()) {
-          BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
-        }
-      }
-
-      // Add the interval to the correct list.
-      if (current->HasRegister()) {
-        DCHECK(instruction->IsParameterValue());
-        inactive_.Add(current);
-      } else if (current->HasSpillSlot() || instruction->IsConstant()) {
-        // Split before first register use.
-        size_t first_register_use = current->FirstRegisterUse();
-        if (first_register_use != kNoLifetime) {
-          LiveInterval* split = Split(current, first_register_use - 1);
-          // Don't add direclty to `unhandled_`, it needs to be sorted and the start
-          // of this new interval might be after intervals already in the list.
-          AddToUnhandled(split);
-        } else {
-          // Nothing to do, we won't allocate a register for this value.
-        }
-      } else {
-        DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
-        unhandled_.Add(current);
-      }
+  for (HLinearPostOrderIterator it(liveness_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+      ProcessInstruction(it.Current());
+    }
+    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+      ProcessInstruction(it.Current());
     }
   }
 
+  number_of_registers_ = codegen_->GetNumberOfCoreRegisters();
+  registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+  processing_core_registers_ = true;
+  unhandled_ = &unhandled_core_intervals_;
   LinearScan();
+
+  inactive_.Reset();
+  active_.Reset();
+  handled_.Reset();
+
+  number_of_registers_ = codegen_->GetNumberOfFloatingPointRegisters();
+  registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+  processing_core_registers_ = false;
+  unhandled_ = &unhandled_fp_intervals_;
+  // TODO: Enable FP register allocation.
+  DCHECK(unhandled_->IsEmpty());
+  LinearScan();
+}
+
+void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  size_t position = instruction->GetLifetimePosition();
+
+  if (locations == nullptr) return;
+
+  // Create synthesized intervals for temporaries.
+  for (size_t i = 0; i < locations->GetTempCount(); ++i) {
+    Location temp = locations->GetTemp(i);
+    if (temp.IsRegister()) {
+      BlockRegister(temp, position, position + 1, Primitive::kPrimInt);
+    } else {
+      LiveInterval* interval =
+          LiveInterval::MakeTempInterval(allocator_, instruction, Primitive::kPrimInt);
+      temp_intervals_.Add(interval);
+      interval->AddRange(position, position + 1);
+      unhandled_core_intervals_.Add(interval);
+    }
+  }
+
+  bool core_register = (instruction->GetType() != Primitive::kPrimDouble)
+      && (instruction->GetType() != Primitive::kPrimFloat);
+
+  GrowableArray<LiveInterval*>& unhandled = core_register
+      ? unhandled_core_intervals_
+      : unhandled_fp_intervals_;
+
+  if (locations->CanCall()) {
+    if (!instruction->IsSuspendCheck()) {
+      codegen_->MarkNotLeaf();
+    }
+    safepoints_.Add(instruction);
+    if (locations->OnlyCallsOnSlowPath()) {
+      // We add a synthesized range at this position to record the live registers
+      // at this position. Ideally, we could just update the safepoints when locations
+      // are updated, but we currently need to know the full stack size before updating
+      // locations (because of parameters and the fact that we don't have a frame pointer).
+      // And knowing the full stack size requires to know the maximum number of live
+      // registers at calls in slow paths.
+      // By adding the following interval in the algorithm, we can compute this
+      // maximum before updating locations.
+      LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction);
+      interval->AddRange(position, position + 1);
+      unhandled.Add(interval);
+    }
+  }
+
+  if (locations->WillCall()) {
+    // Block all registers.
+    for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
+      BlockRegister(Location::RegisterLocation(ManagedRegister(i)),
+                    position,
+                    position + 1,
+                    Primitive::kPrimInt);
+    }
+  }
+
+  for (size_t i = 0; i < instruction->InputCount(); ++i) {
+    Location input = locations->InAt(i);
+    if (input.IsRegister()) {
+      BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
+    }
+  }
+
+  LiveInterval* current = instruction->GetLiveInterval();
+  if (current == nullptr) return;
+
+  DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek()));
+  // Some instructions define their output in fixed register/stack slot. We need
+  // to ensure we know these locations before doing register allocation. For a
+  // given register, we create an interval that covers these locations. The register
+  // will be unavailable at these locations when trying to allocate one for an
+  // interval.
+  //
+  // The backwards walking ensures the ranges are ordered on increasing start positions.
+  Location output = locations->Out();
+  if (output.IsRegister()) {
+    // Shift the interval's start by one to account for the blocked register.
+    current->SetFrom(position + 1);
+    current->SetRegister(output.reg().RegId());
+    BlockRegister(output, position, position + 1, instruction->GetType());
+  } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
+    current->SetSpillSlot(output.GetStackIndex());
+  }
+
+  // If needed, add interval to the list of unhandled intervals.
+  if (current->HasSpillSlot() || instruction->IsConstant()) {
+    // Split before first register use.
+    size_t first_register_use = current->FirstRegisterUse();
+    if (first_register_use != kNoLifetime) {
+      LiveInterval* split = Split(current, first_register_use);
+      // Don't add direclty to `unhandled`, it needs to be sorted and the start
+      // of this new interval might be after intervals already in the list.
+      AddSorted(&unhandled, split);
+    } else {
+      // Nothing to do, we won't allocate a register for this value.
+    }
+  } else {
+    DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek()));
+    unhandled.Add(current);
+  }
 }
 
 class AllRangesIterator : public ValueObject {
@@ -219,12 +286,20 @@
     }
   }
 
-  return ValidateIntervals(intervals, spill_slots_.Size(), *codegen_, allocator_,
-                           processing_core_registers_, log_fatal_on_failure);
+  for (size_t i = 0, e = temp_intervals_.Size(); i < e; ++i) {
+    LiveInterval* temp = temp_intervals_.Get(i);
+    if (ShouldProcess(processing_core_registers_, temp)) {
+      intervals.Add(temp);
+    }
+  }
+
+  return ValidateIntervals(intervals, spill_slots_.Size(), reserved_out_slots_, *codegen_,
+                           allocator_, processing_core_registers_, log_fatal_on_failure);
 }
 
 bool RegisterAllocator::ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
                                           size_t number_of_spill_slots,
+                                          size_t number_of_out_slots,
                                           const CodeGenerator& codegen,
                                           ArenaAllocator* allocator,
                                           bool processing_core_registers,
@@ -248,8 +323,9 @@
       if (current->GetParent()->HasSpillSlot()
            // Parameters have their own stack slot.
            && !(defined_by != nullptr && defined_by->IsParameterValue())) {
-        BitVector* liveness_of_spill_slot = liveness_of_values.Get(
-            number_of_registers + current->GetParent()->GetSpillSlot() / kVRegSize);
+        BitVector* liveness_of_spill_slot = liveness_of_values.Get(number_of_registers
+            + current->GetParent()->GetSpillSlot() / kVRegSize
+            - number_of_out_slots);
         for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
           if (liveness_of_spill_slot->IsBitSet(j)) {
             if (log_fatal_on_failure) {
@@ -271,7 +347,11 @@
           if (liveness_of_register->IsBitSet(j)) {
             if (log_fatal_on_failure) {
               std::ostringstream message;
-              message << "Register conflict at " << j << " for ";
+              message << "Register conflict at " << j << " ";
+              if (defined_by != nullptr) {
+                message << "(" << defined_by->DebugName() << ")";
+              }
+              message << "for ";
               if (processing_core_registers) {
                 codegen.DumpCoreRegister(message, current->GetRegister());
               } else {
@@ -308,10 +388,10 @@
 
 // By the book implementation of a linear scan register allocator.
 void RegisterAllocator::LinearScan() {
-  while (!unhandled_.IsEmpty()) {
+  while (!unhandled_->IsEmpty()) {
     // (1) Remove interval with the lowest start position from unhandled.
-    LiveInterval* current = unhandled_.Pop();
-    DCHECK(!current->IsFixed() && !current->HasRegister() && !current->HasSpillSlot());
+    LiveInterval* current = unhandled_->Pop();
+    DCHECK(!current->IsFixed() && !current->HasSpillSlot());
     size_t position = current->GetStart();
 
     // (2) Remove currently active intervals that are dead at this position.
@@ -345,6 +425,14 @@
       }
     }
 
+    if (current->IsSlowPathSafepoint()) {
+      // Synthesized interval to record the maximum number of live registers
+      // at safepoints. No need to allocate a register for it.
+      maximum_number_of_live_registers_ =
+          std::max(maximum_number_of_live_registers_, active_.Size());
+      continue;
+    }
+
     // (4) Try to find an available register.
     bool success = TryAllocateFreeReg(current);
 
@@ -380,7 +468,8 @@
     DCHECK(inactive->HasRegister());
     size_t next_intersection = inactive->FirstIntersectionWith(current);
     if (next_intersection != kNoLifetime) {
-      free_until[inactive->GetRegister()] = next_intersection;
+      free_until[inactive->GetRegister()] =
+          std::min(free_until[inactive->GetRegister()], next_intersection);
     }
   }
 
@@ -391,13 +480,19 @@
     free_until[interval->GetRegister()] = 0;
   }
 
-  // Pick the register that is free the longest.
   int reg = -1;
-  for (size_t i = 0; i < number_of_registers_; ++i) {
-    if (IsBlocked(i)) continue;
-    if (reg == -1 || free_until[i] > free_until[reg]) {
-      reg = i;
-      if (free_until[i] == kMaxLifetimePosition) break;
+  if (current->HasRegister()) {
+    // Some instructions have a fixed register output.
+    reg = current->GetRegister();
+    DCHECK_NE(free_until[reg], 0u);
+  } else {
+    // Pick the register that is free the longest.
+    for (size_t i = 0; i < number_of_registers_; ++i) {
+      if (IsBlocked(i)) continue;
+      if (reg == -1 || free_until[i] > free_until[reg]) {
+        reg = i;
+        if (free_until[i] == kMaxLifetimePosition) break;
+      }
     }
   }
 
@@ -413,7 +508,7 @@
     // the register is not available anymore.
     LiveInterval* split = Split(current, free_until[reg]);
     DCHECK(split != nullptr);
-    AddToUnhandled(split);
+    AddSorted(unhandled_, split);
   }
   return true;
 }
@@ -491,8 +586,8 @@
     // If the first use of that instruction is after the last use of the found
     // register, we split this interval just before its first register use.
     AllocateSpillSlotFor(current);
-    LiveInterval* split = Split(current, first_register_use - 1);
-    AddToUnhandled(split);
+    LiveInterval* split = Split(current, first_register_use);
+    AddSorted(unhandled_, split);
     return false;
   } else {
     // Use this register and spill the active and inactives interval that
@@ -506,7 +601,7 @@
         LiveInterval* split = Split(active, current->GetStart());
         active_.DeleteAt(i);
         handled_.Add(active);
-        AddToUnhandled(split);
+        AddSorted(unhandled_, split);
         break;
       }
     }
@@ -518,12 +613,12 @@
         if (next_intersection != kNoLifetime) {
           if (inactive->IsFixed()) {
             LiveInterval* split = Split(current, next_intersection);
-            AddToUnhandled(split);
+            AddSorted(unhandled_, split);
           } else {
             LiveInterval* split = Split(inactive, current->GetStart());
             inactive_.DeleteAt(i);
             handled_.Add(inactive);
-            AddToUnhandled(split);
+            AddSorted(unhandled_, split);
             --i;
           }
         }
@@ -534,16 +629,16 @@
   }
 }
 
-void RegisterAllocator::AddToUnhandled(LiveInterval* interval) {
+void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval) {
   size_t insert_at = 0;
-  for (size_t i = unhandled_.Size(); i > 0; --i) {
-    LiveInterval* current = unhandled_.Get(i - 1);
+  for (size_t i = array->Size(); i > 0; --i) {
+    LiveInterval* current = array->Get(i - 1);
     if (current->StartsAfter(interval)) {
       insert_at = i;
       break;
     }
   }
-  unhandled_.InsertAt(insert_at, interval);
+  array->InsertAt(insert_at, interval);
 }
 
 LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
@@ -590,14 +685,6 @@
   }
   size_t end = last_sibling->GetEnd();
 
-  if (NeedTwoSpillSlot(parent->GetType())) {
-    AllocateTwoSpillSlots(parent, end);
-  } else {
-    AllocateOneSpillSlot(parent, end);
-  }
-}
-
-void RegisterAllocator::AllocateTwoSpillSlots(LiveInterval* parent, size_t end) {
   // Find an available spill slot.
   size_t slot = 0;
   for (size_t e = spill_slots_.Size(); slot < e; ++slot) {
@@ -611,38 +698,28 @@
     }
   }
 
-  if (slot == spill_slots_.Size()) {
-    // We need a new spill slot.
-    spill_slots_.Add(end);
-    spill_slots_.Add(end);
-  } else if (slot == spill_slots_.Size() - 1) {
-    spill_slots_.Put(slot, end);
-    spill_slots_.Add(end);
+  if (NeedTwoSpillSlot(parent->GetType())) {
+    if (slot == spill_slots_.Size()) {
+      // We need a new spill slot.
+      spill_slots_.Add(end);
+      spill_slots_.Add(end);
+    } else if (slot == spill_slots_.Size() - 1) {
+      spill_slots_.Put(slot, end);
+      spill_slots_.Add(end);
+    } else {
+      spill_slots_.Put(slot, end);
+      spill_slots_.Put(slot + 1, end);
+    }
   } else {
-    spill_slots_.Put(slot, end);
-    spill_slots_.Put(slot + 1, end);
-  }
-
-  parent->SetSpillSlot(slot * kVRegSize);
-}
-
-void RegisterAllocator::AllocateOneSpillSlot(LiveInterval* parent, size_t end) {
-  // Find an available spill slot.
-  size_t slot = 0;
-  for (size_t e = spill_slots_.Size(); slot < e; ++slot) {
-    if (spill_slots_.Get(slot) <= parent->GetStart()) {
-      break;
+    if (slot == spill_slots_.Size()) {
+      // We need a new spill slot.
+      spill_slots_.Add(end);
+    } else {
+      spill_slots_.Put(slot, end);
     }
   }
 
-  if (slot == spill_slots_.Size()) {
-    // We need a new spill slot.
-    spill_slots_.Add(end);
-  } else {
-    spill_slots_.Put(slot, end);
-  }
-
-  parent->SetSpillSlot(slot * kVRegSize);
+  parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
 }
 
 static Location ConvertToLocation(LiveInterval* interval) {
@@ -672,31 +749,38 @@
   return instruction->GetLifetimePosition() == kInputMoveLifetimePosition;
 }
 
-void RegisterAllocator::AddInputMoveFor(HInstruction* instruction,
+static bool IsValidDestination(Location destination) {
+  return destination.IsRegister() || destination.IsStackSlot() || destination.IsDoubleStackSlot();
+}
+
+void RegisterAllocator::AddInputMoveFor(HInstruction* user,
                                         Location source,
                                         Location destination) const {
+  DCHECK(IsValidDestination(destination));
   if (source.Equals(destination)) return;
 
-  DCHECK(instruction->AsPhi() == nullptr);
+  DCHECK(user->AsPhi() == nullptr);
 
-  HInstruction* previous = instruction->GetPrevious();
+  HInstruction* previous = user->GetPrevious();
   HParallelMove* move = nullptr;
   if (previous == nullptr
       || previous->AsParallelMove() == nullptr
       || !IsInputMove(previous)) {
     move = new (allocator_) HParallelMove(allocator_);
     move->SetLifetimePosition(kInputMoveLifetimePosition);
-    instruction->GetBlock()->InsertInstructionBefore(move, instruction);
+    user->GetBlock()->InsertInstructionBefore(move, user);
   } else {
     move = previous->AsParallelMove();
   }
   DCHECK(IsInputMove(move));
-  move->AddMove(new (allocator_) MoveOperands(source, destination));
+  move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr));
 }
 
 void RegisterAllocator::InsertParallelMoveAt(size_t position,
+                                             HInstruction* instruction,
                                              Location source,
                                              Location destination) const {
+  DCHECK(IsValidDestination(destination));
   if (source.Equals(destination)) return;
 
   HInstruction* at = liveness_.GetInstructionFromPosition(position / 2);
@@ -719,14 +803,26 @@
   } else {
     // Move must happen before the instruction.
     HInstruction* previous = at->GetPrevious();
-    if (previous != nullptr && previous->AsParallelMove() != nullptr) {
+    if (previous != nullptr && previous->IsParallelMove()) {
       // This is a parallel move for connecting siblings in a same block. We need to
       // differentiate it with moves for connecting blocks, and input moves.
       if (previous->GetLifetimePosition() != position) {
+        // If the previous instruction of the previous instruction is not a parallel
+        // move, we have to insert the new parallel move before the input or connecting
+        // block moves.
+        at = previous;
         previous = previous->GetPrevious();
       }
     }
-    if (previous == nullptr || previous->AsParallelMove() == nullptr) {
+    if (previous == nullptr
+        || !previous->IsParallelMove()
+        || previous->GetLifetimePosition() != position) {
+      // If the previous is a parallel move, then its position must be lower
+      // than the given `position`: it was added just after the non-parallel
+      // move instruction that precedes `instruction`.
+      DCHECK(previous == nullptr
+             || !previous->IsParallelMove()
+             || previous->GetLifetimePosition() < position);
       move = new (allocator_) HParallelMove(allocator_);
       move->SetLifetimePosition(position);
       at->GetBlock()->InsertInstructionBefore(move, at);
@@ -734,12 +830,14 @@
       move = previous->AsParallelMove();
     }
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination));
+  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
 }
 
 void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
+                                                   HInstruction* instruction,
                                                    Location source,
                                                    Location destination) const {
+  DCHECK(IsValidDestination(destination));
   if (source.Equals(destination)) return;
 
   DCHECK_EQ(block->GetSuccessors().Size(), 1u);
@@ -748,7 +846,7 @@
   HParallelMove* move;
   // This is a parallel move for connecting blocks. We need to differentiate
   // it with moves for connecting siblings in a same block, and output moves.
-  if (previous == nullptr || previous->AsParallelMove() == nullptr
+  if (previous == nullptr || !previous->IsParallelMove()
       || previous->AsParallelMove()->GetLifetimePosition() != block->GetLifetimeEnd()) {
     move = new (allocator_) HParallelMove(allocator_);
     move->SetLifetimePosition(block->GetLifetimeEnd());
@@ -756,12 +854,14 @@
   } else {
     move = previous->AsParallelMove();
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination));
+  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
 }
 
 void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
+                                                    HInstruction* instruction,
                                                     Location source,
                                                     Location destination) const {
+  DCHECK(IsValidDestination(destination));
   if (source.Equals(destination)) return;
 
   HInstruction* first = block->GetFirstInstruction();
@@ -773,16 +873,17 @@
     move->SetLifetimePosition(block->GetLifetimeStart());
     block->InsertInstructionBefore(move, first);
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination));
+  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
 }
 
 void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
                                         Location source,
                                         Location destination) const {
+  DCHECK(IsValidDestination(destination));
   if (source.Equals(destination)) return;
 
   if (instruction->AsPhi() != nullptr) {
-    InsertParallelMoveAtEntryOf(instruction->GetBlock(), source, destination);
+    InsertParallelMoveAtEntryOf(instruction->GetBlock(), instruction, source, destination);
     return;
   }
 
@@ -796,7 +897,7 @@
     move->SetLifetimePosition(position);
     instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination));
+  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
 }
 
 void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
@@ -819,12 +920,14 @@
     // Walk over all uses covered by this interval, and update the location
     // information.
     while (use != nullptr && use->GetPosition() <= current->GetEnd()) {
-      if (!use->GetIsEnvironment()) {
-        LocationSummary* locations = use->GetUser()->GetLocations();
+      LocationSummary* locations = use->GetUser()->GetLocations();
+      if (use->GetIsEnvironment()) {
+        locations->SetEnvironmentAt(use->GetInputIndex(), source);
+      } else {
         Location expected_location = locations->InAt(use->GetInputIndex());
         if (expected_location.IsUnallocated()) {
           locations->SetInAt(use->GetInputIndex(), source);
-        } else {
+        } else if (!expected_location.IsConstant()) {
           AddInputMoveFor(use->GetUser(), source, expected_location);
         }
       }
@@ -838,7 +941,38 @@
         && next_sibling->HasRegister()
         && current->GetEnd() == next_sibling->GetStart()) {
       Location destination = ConvertToLocation(next_sibling);
-      InsertParallelMoveAt(current->GetEnd(), source, destination);
+      InsertParallelMoveAt(current->GetEnd(), interval->GetDefinedBy(), source, destination);
+    }
+
+    // At each safepoint, we record stack and register information.
+    for (size_t i = 0, e = safepoints_.Size(); i < e; ++i) {
+      HInstruction* safepoint = safepoints_.Get(i);
+      size_t position = safepoint->GetLifetimePosition();
+      LocationSummary* locations = safepoint->GetLocations();
+      if (!current->Covers(position)) continue;
+
+      if ((current->GetType() == Primitive::kPrimNot) && current->GetParent()->HasSpillSlot()) {
+        locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize);
+      }
+
+      switch (source.GetKind()) {
+        case Location::kRegister: {
+          locations->AddLiveRegister(source);
+          if (current->GetType() == Primitive::kPrimNot) {
+            locations->SetRegisterBit(source.reg().RegId());
+          }
+          break;
+        }
+        case Location::kStackSlot:  // Fall-through
+        case Location::kDoubleStackSlot:  // Fall-through
+        case Location::kConstant: {
+          // Nothing to do.
+          break;
+        }
+        default: {
+          LOG(FATAL) << "Unexpected location for object";
+        }
+      }
     }
     current = next_sibling;
   } while (current != nullptr);
@@ -854,7 +988,11 @@
   }
 
   size_t from_position = from->GetLifetimeEnd() - 1;
-  size_t to_position = to->GetLifetimeStart();
+  // When an instructions dies at entry of another, and the latter is the beginning
+  // of a block, the register allocator ensures the former has a register
+  // at block->GetLifetimeStart() + 1. Since this is at a block boundary, it must
+  // must be handled in this method.
+  size_t to_position = to->GetLifetimeStart() + 1;
 
   LiveInterval* destination = nullptr;
   LiveInterval* source = nullptr;
@@ -880,6 +1018,8 @@
     return;
   }
 
+  DCHECK(destination != nullptr && source != nullptr);
+
   if (!destination->HasRegister()) {
     // Values are eagerly spilled. Spill slot already contains appropriate value.
     return;
@@ -888,10 +1028,16 @@
   // If `from` has only one successor, we can put the moves at the exit of it. Otherwise
   // we need to put the moves at the entry of `to`.
   if (from->GetSuccessors().Size() == 1) {
-    InsertParallelMoveAtExitOf(from, ConvertToLocation(source), ConvertToLocation(destination));
+    InsertParallelMoveAtExitOf(from,
+                               interval->GetParent()->GetDefinedBy(),
+                               ConvertToLocation(source),
+                               ConvertToLocation(destination));
   } else {
     DCHECK_EQ(to->GetPredecessors().Size(), 1u);
-    InsertParallelMoveAtEntryOf(to, ConvertToLocation(source), ConvertToLocation(destination));
+    InsertParallelMoveAtEntryOf(to,
+                                interval->GetParent()->GetDefinedBy(),
+                                ConvertToLocation(source),
+                                ConvertToLocation(destination));
   }
 }
 
@@ -906,7 +1052,8 @@
 }
 
 void RegisterAllocator::Resolve() {
-  codegen_->ComputeFrameSize(spill_slots_.Size());
+  codegen_->ComputeFrameSize(
+      spill_slots_.Size(), maximum_number_of_live_registers_, reserved_out_slots_);
 
   // Adjust the Out Location of instructions.
   // TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
@@ -973,10 +1120,24 @@
         Location source = FindLocationAt(input->GetLiveInterval(),
                                          predecessor->GetLastInstruction()->GetLifetimePosition());
         Location destination = ConvertToLocation(phi->GetLiveInterval());
-        InsertParallelMoveAtExitOf(predecessor, source, destination);
+        InsertParallelMoveAtExitOf(predecessor, nullptr, source, destination);
       }
     }
   }
+
+  // Assign temp locations.
+  HInstruction* current = nullptr;
+  size_t temp_index = 0;
+  for (size_t i = 0; i < temp_intervals_.Size(); ++i) {
+    LiveInterval* temp = temp_intervals_.Get(i);
+    if (temp->GetDefinedBy() != current) {
+      temp_index = 0;
+      current = temp->GetDefinedBy();
+    }
+    LocationSummary* locations = current->GetLocations();
+    locations->SetTempAt(
+        temp_index++, Location::RegisterLocation(ManagedRegister(temp->GetRegister())));
+  }
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index be1c7ec..d4c233a 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -21,6 +21,8 @@
 #include "primitive.h"
 #include "utils/growable_array.h"
 
+#include "gtest/gtest.h"
+
 namespace art {
 
 class CodeGenerator;
@@ -59,6 +61,7 @@
   // Helper method for validation. Used by unit testing.
   static bool ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
                                 size_t number_of_spill_slots,
+                                size_t number_of_out_slots,
                                 const CodeGenerator& codegen,
                                 ArenaAllocator* allocator,
                                 bool processing_core_registers,
@@ -83,8 +86,8 @@
   bool AllocateBlockedReg(LiveInterval* interval);
   void Resolve();
 
-  // Add `interval` in the sorted list of unhandled intervals.
-  void AddToUnhandled(LiveInterval* interval);
+  // Add `interval` in the given sorted list.
+  static void AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval);
 
   // Split `interval` at the position `at`. The new interval starts at `at`.
   LiveInterval* Split(LiveInterval* interval, size_t at);
@@ -97,8 +100,6 @@
 
   // Allocate a spill slot for the given interval.
   void AllocateSpillSlotFor(LiveInterval* interval);
-  void AllocateOneSpillSlot(LiveInterval* interval, size_t end);
-  void AllocateTwoSpillSlots(LiveInterval* interval, size_t end);
 
   // Connect adjacent siblings within blocks.
   void ConnectSiblings(LiveInterval* interval);
@@ -107,14 +108,24 @@
   void ConnectSplitSiblings(LiveInterval* interval, HBasicBlock* from, HBasicBlock* to) const;
 
   // Helper methods to insert parallel moves in the graph.
-  void InsertParallelMoveAtExitOf(HBasicBlock* block, Location source, Location destination) const;
-  void InsertParallelMoveAtEntryOf(HBasicBlock* block, Location source, Location destination) const;
+  void InsertParallelMoveAtExitOf(HBasicBlock* block,
+                                  HInstruction* instruction,
+                                  Location source,
+                                  Location destination) const;
+  void InsertParallelMoveAtEntryOf(HBasicBlock* block,
+                                   HInstruction* instruction,
+                                   Location source,
+                                   Location destination) const;
   void InsertMoveAfter(HInstruction* instruction, Location source, Location destination) const;
-  void AddInputMoveFor(HInstruction* instruction, Location source, Location destination) const;
-  void InsertParallelMoveAt(size_t position, Location source, Location destination) const;
+  void AddInputMoveFor(HInstruction* user, Location source, Location destination) const;
+  void InsertParallelMoveAt(size_t position,
+                            HInstruction* instruction,
+                            Location source,
+                            Location destination) const;
 
   // Helper methods.
   void AllocateRegistersInternal();
+  void ProcessInstruction(HInstruction* instruction);
   bool ValidateInternal(bool log_fatal_on_failure) const;
   void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
 
@@ -122,9 +133,17 @@
   CodeGenerator* const codegen_;
   const SsaLivenessAnalysis& liveness_;
 
-  // List of intervals that must be processed, ordered by start position. Last entry
-  // is the interval that has the lowest start position.
-  GrowableArray<LiveInterval*> unhandled_;
+  // List of intervals for core registers that must be processed, ordered by start
+  // position. Last entry is the interval that has the lowest start position.
+  // This list is initially populated before doing the linear scan.
+  GrowableArray<LiveInterval*> unhandled_core_intervals_;
+
+  // List of intervals for floating-point registers. Same comments as above.
+  GrowableArray<LiveInterval*> unhandled_fp_intervals_;
+
+  // Currently processed list of unhandled intervals. Either `unhandled_core_intervals_`
+  // or `unhandled_fp_intervals_`.
+  GrowableArray<LiveInterval*>* unhandled_;
 
   // List of intervals that have been processed.
   GrowableArray<LiveInterval*> handled_;
@@ -137,13 +156,20 @@
   // That is, they have a lifetime hole that spans the start of the new interval.
   GrowableArray<LiveInterval*> inactive_;
 
-  // Fixed intervals for physical registers. Such an interval covers the positions
+  // Fixed intervals for physical registers. Such intervals cover the positions
   // where an instruction requires a specific register.
   GrowableArray<LiveInterval*> physical_register_intervals_;
 
+  // Intervals for temporaries. Such intervals cover the positions
+  // where an instruction requires a temporary.
+  GrowableArray<LiveInterval*> temp_intervals_;
+
   // The spill slots allocated for live intervals.
   GrowableArray<size_t> spill_slots_;
 
+  // Instructions that need a safepoint.
+  GrowableArray<HInstruction*> safepoints_;
+
   // True if processing core registers. False if processing floating
   // point registers.
   bool processing_core_registers_;
@@ -157,6 +183,14 @@
   // Blocked registers, as decided by the code generator.
   bool* const blocked_registers_;
 
+  // Slots reserved for out arguments.
+  size_t reserved_out_slots_;
+
+  // The maximum live registers at safepoints.
+  size_t maximum_number_of_live_registers_;
+
+  FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
+
   DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
 };
 
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index a7283ab..535a768 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -16,12 +16,14 @@
 
 #include "builder.h"
 #include "code_generator.h"
+#include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
 #include "register_allocator.h"
 #include "ssa_liveness_analysis.h"
+#include "ssa_phi_elimination.h"
 #include "utils/arena_allocator.h"
 
 #include "gtest/gtest.h"
@@ -40,10 +42,10 @@
   graph->BuildDominatorTree();
   graph->TransformToSSA();
   graph->FindNaturalLoops();
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
-  RegisterAllocator register_allocator(&allocator, codegen, liveness);
+  RegisterAllocator register_allocator(&allocator, &codegen, liveness);
   register_allocator.AllocateRegisters();
   return register_allocator.Validate(false);
 }
@@ -56,7 +58,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = new (&allocator) HGraph(&allocator);
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
+  x86::CodeGeneratorX86 codegen(graph);
   GrowableArray<LiveInterval*> intervals(&allocator, 0);
 
   // Test with two intervals of the same range.
@@ -65,11 +67,11 @@
     intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 0));
     intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -80,11 +82,11 @@
     static constexpr size_t ranges2[][2] = {{42, 43}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -95,11 +97,11 @@
     static constexpr size_t ranges2[][2] = {{42, 43}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -110,11 +112,11 @@
     static constexpr size_t ranges2[][2] = {{42, 47}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -126,16 +128,16 @@
     static constexpr size_t ranges2[][2] = {{42, 47}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     // Sibling of the first interval has no register allocated to it.
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
 
     intervals.Get(0)->GetNextSibling()->SetRegister(0);
     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, codegen, &allocator, true, false));
   }
 }
 
@@ -297,10 +299,10 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildSSAGraph(data, &allocator);
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
-  RegisterAllocator register_allocator(&allocator, codegen, liveness);
+  RegisterAllocator register_allocator(&allocator, &codegen, liveness);
   register_allocator.AllocateRegisters();
   ASSERT_TRUE(register_allocator.Validate(false));
 
@@ -329,15 +331,15 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildSSAGraph(data, &allocator);
-  CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kArm);
-  SsaLivenessAnalysis liveness(*graph, codegen);
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
   HAdd* first_add = graph->GetBlocks().Get(1)->GetFirstInstruction()->AsAdd();
   HAdd* last_add = graph->GetBlocks().Get(1)->GetLastInstruction()->GetPrevious()->AsAdd();
   ASSERT_EQ(last_add->InputAt(0), first_add);
   LiveInterval* interval = first_add->GetLiveInterval();
-  ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition() + 1);
+  ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition());
   ASSERT_TRUE(interval->GetNextSibling() == nullptr);
 
   // We need a register for the output of the instruction.
@@ -346,14 +348,103 @@
   // Split at the next instruction.
   interval = interval->SplitAt(first_add->GetLifetimePosition() + 2);
   // The user of the split is the last add.
-  ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
+  ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1);
 
   // Split before the last add.
   LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1);
   // Ensure the current interval has no register use...
   ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime);
   // And the new interval has it for the last add.
-  ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
+  ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1);
+}
+
+TEST(RegisterAllocatorTest, DeadPhi) {
+  /* Test for a dead loop phi taking as back-edge input a phi that also has
+   * this loop phi as input. Walking backwards in SsaDeadPhiElimination
+   * does not solve the problem because the loop phi will be visited last.
+   *
+   * Test the following snippet:
+   *  int a = 0
+   *  do {
+   *    if (true) {
+   *      a = 2;
+   *    }
+   *  } while (true);
+   */
+
+  const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::CONST_4 | 1 << 8 | 0,
+    Instruction::IF_NE | 1 << 8 | 1 << 12, 3,
+    Instruction::CONST_4 | 2 << 12 | 0 << 8,
+    Instruction::GOTO | 0xFD00,
+    Instruction::RETURN_VOID);
+
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = BuildSSAGraph(data, &allocator);
+  SsaDeadPhiElimination(graph).Run();
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
+  liveness.Analyze();
+  RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+  register_allocator.AllocateRegisters();
+  ASSERT_TRUE(register_allocator.Validate(false));
+}
+
+/**
+ * Test that the TryAllocateFreeReg method works in the presence of inactive intervals
+ * that share the same register. It should split the interval it is currently
+ * allocating for at the minimum lifetime position between the two inactive intervals.
+ */
+TEST(RegisterAllocatorTest, FreeUntil) {
+  const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::RETURN);
+
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = BuildSSAGraph(data, &allocator);
+  SsaDeadPhiElimination(graph).Run();
+  x86::CodeGeneratorX86 codegen(graph);
+  SsaLivenessAnalysis liveness(*graph, &codegen);
+  liveness.Analyze();
+  RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+
+  // Add an artifical range to cover the temps that will be put in the unhandled list.
+  LiveInterval* unhandled = graph->GetEntryBlock()->GetFirstInstruction()->GetLiveInterval();
+  unhandled->AddLoopRange(0, 60);
+
+  // Add three temps holding the same register, and starting at different positions.
+  // Put the one that should be picked in the middle of the inactive list to ensure
+  // we do not depend on an order.
+  LiveInterval* interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt);
+  interval->SetRegister(0);
+  interval->AddRange(40, 50);
+  register_allocator.inactive_.Add(interval);
+
+  interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt);
+  interval->SetRegister(0);
+  interval->AddRange(20, 30);
+  register_allocator.inactive_.Add(interval);
+
+  interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt);
+  interval->SetRegister(0);
+  interval->AddRange(60, 70);
+  register_allocator.inactive_.Add(interval);
+
+  register_allocator.number_of_registers_ = 1;
+  register_allocator.registers_array_ = allocator.AllocArray<size_t>(1);
+  register_allocator.processing_core_registers_ = true;
+  register_allocator.unhandled_ = &register_allocator.unhandled_core_intervals_;
+
+  register_allocator.TryAllocateFreeReg(unhandled);
+
+  // Check that we have split the interval.
+  ASSERT_EQ(1u, register_allocator.unhandled_->Size());
+  // Check that we know need to find a new register where the next interval
+  // that uses the register starts.
+  ASSERT_EQ(20u, register_allocator.unhandled_->Get(0)->GetStart());
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index fbdc0b9..cd13d81 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -16,6 +16,7 @@
 
 #include "ssa_liveness_analysis.h"
 
+#include "base/bit_vector-inl.h"
 #include "code_generator.h"
 #include "nodes.h"
 
@@ -101,13 +102,14 @@
   // to differentiate between the start and end of an instruction. Adding 2 to
   // the lifetime position for each instruction ensures the start of an
   // instruction is different than the end of the previous instruction.
+  HGraphVisitor* location_builder = codegen_->GetLocationBuilder();
   for (HLinearOrderIterator it(*this); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     block->SetLifetimeStart(lifetime_position);
 
     for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
-      current->Accept(codegen_->GetLocationBuilder());
+      current->Accept(location_builder);
       LocationSummary* locations = current->GetLocations();
       if (locations != nullptr && locations->Out().IsValid()) {
         instructions_from_ssa_index_.Add(current);
@@ -187,6 +189,7 @@
     }
 
     // Add a range that covers this block to all instructions live_in because of successors.
+    // Instructions defined in this block will have their start of the range adjusted.
     for (uint32_t idx : live_in->Indexes()) {
       HInstruction* current = instructions_from_ssa_index_.Get(idx);
       current->GetLiveInterval()->AddRange(block->GetLifetimeStart(), block->GetLifetimeEnd());
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 83035b5..c62e61b 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -47,7 +47,7 @@
 };
 
 /**
- * A live range contains the start and end of a range where an instruction
+ * A live range contains the start and end of a range where an instruction or a temporary
  * is live.
  */
 class LiveRange : public ArenaObject {
@@ -99,13 +99,16 @@
         is_environment_(is_environment),
         position_(position),
         next_(next) {
-    DCHECK(user->AsPhi() != nullptr || GetPosition() == user->GetLifetimePosition() + 1);
+    DCHECK(user->IsPhi()
+        || (GetPosition() == user->GetLifetimePosition() + 1)
+        || (GetPosition() == user->GetLifetimePosition()));
     DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition());
   }
 
   size_t GetPosition() const { return position_; }
 
   UsePosition* GetNext() const { return next_; }
+  void SetNext(UsePosition* next) { next_ = next; }
 
   HInstruction* GetUser() const { return user_; }
 
@@ -122,7 +125,7 @@
   const size_t input_index_;
   const bool is_environment_;
   const size_t position_;
-  UsePosition* const next_;
+  UsePosition* next_;
 
   DISALLOW_COPY_AND_ASSIGN(UsePosition);
 };
@@ -133,7 +136,13 @@
  */
 class LiveInterval : public ArenaObject {
  public:
-  LiveInterval(ArenaAllocator* allocator, Primitive::Type type, HInstruction* defined_by = nullptr)
+  LiveInterval(ArenaAllocator* allocator,
+               Primitive::Type type,
+               HInstruction* defined_by = nullptr,
+               bool is_fixed = false,
+               int reg = kNoRegister,
+               bool is_temp = false,
+               bool is_slow_path_safepoint = false)
       : allocator_(allocator),
         first_range_(nullptr),
         last_range_(nullptr),
@@ -141,39 +150,73 @@
         type_(type),
         next_sibling_(nullptr),
         parent_(this),
-        register_(kNoRegister),
+        register_(reg),
         spill_slot_(kNoSpillSlot),
-        is_fixed_(false),
+        is_fixed_(is_fixed),
+        is_temp_(is_temp),
+        is_slow_path_safepoint_(is_slow_path_safepoint),
         defined_by_(defined_by) {}
 
+  static LiveInterval* MakeSlowPathInterval(ArenaAllocator* allocator, HInstruction* instruction) {
+    return new (allocator) LiveInterval(
+        allocator, Primitive::kPrimVoid, instruction, false, kNoRegister, false, true);
+  }
+
   static LiveInterval* MakeFixedInterval(ArenaAllocator* allocator, int reg, Primitive::Type type) {
-    LiveInterval* interval = new (allocator) LiveInterval(allocator, type);
-    interval->SetRegister(reg);
-    interval->is_fixed_ = true;
-    return interval;
+    return new (allocator) LiveInterval(allocator, type, nullptr, true, reg, false);
+  }
+
+  static LiveInterval* MakeTempInterval(ArenaAllocator* allocator,
+                                        HInstruction* defined_by,
+                                        Primitive::Type type) {
+    return new (allocator) LiveInterval(allocator, type, defined_by, false, kNoRegister, true);
   }
 
   bool IsFixed() const { return is_fixed_; }
+  bool IsSlowPathSafepoint() const { return is_slow_path_safepoint_; }
 
   void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
     // Set the use within the instruction.
-    // TODO: Use the instruction's location to know whether the instruction can die
-    // at entry, or needs to say alive within the user.
-    size_t position = instruction->GetLifetimePosition() + 1;
+    size_t position = instruction->GetLifetimePosition();
+    if (instruction->GetLocations()->InputOverlapsWithOutputOrTemp(input_index, is_environment)) {
+      // If it overlaps, we need to make sure the user will not try to allocate a temp
+      // or its output to the same register.
+      ++position;
+    }
+    if ((first_use_ != nullptr)
+        && (first_use_->GetUser() == instruction)
+        && (first_use_->GetPosition() < position)) {
+      // The user uses the instruction multiple times, and one use dies before the other.
+      // We update the use list so that the latter is first.
+      DCHECK(first_use_->GetPosition() + 1 == position);
+      UsePosition* new_use = new (allocator_) UsePosition(
+          instruction, input_index, is_environment, position, first_use_->GetNext());
+      first_use_->SetNext(new_use);
+      if (first_range_->GetEnd() == first_use_->GetPosition()) {
+        first_range_->end_ = position;
+      }
+      return;
+    }
+
     size_t start_block_position = instruction->GetBlock()->GetLifetimeStart();
-    size_t end_block_position = instruction->GetBlock()->GetLifetimeEnd();
     if (first_range_ == nullptr) {
       // First time we see a use of that interval.
-      first_range_ = last_range_ = new (allocator_) LiveRange(start_block_position, position, nullptr);
+      first_range_ = last_range_ = new (allocator_) LiveRange(
+          start_block_position, position, nullptr);
     } else if (first_range_->GetStart() == start_block_position) {
-      // There is a use later in the same block.
+      // There is a use later in the same block or in a following block.
+      // Note that in such a case, `AddRange` for the whole blocks has been called
+      // before arriving in this method, and this is the reason the start of
+      // `first_range_` is before the given `position`.
       DCHECK_LE(position, first_range_->GetEnd());
-    } else if (first_range_->GetStart() == end_block_position) {
-      // Last use is in the following block.
-      first_range_->start_ = start_block_position;
     } else {
       DCHECK(first_range_->GetStart() > position);
       // There is a hole in the interval. Create a new range.
+      // Note that the start of `first_range_` can be equal to `end`: two blocks
+      // having adjacent lifetime positions are not necessarily
+      // predecessor/successor. When two blocks are predecessor/successor, the
+      // liveness algorithm has called `AddRange` before arriving in this method,
+      // and the check line 205 would succeed.
       first_range_ = new (allocator_) LiveRange(start_block_position, position, first_range_);
     }
     first_use_ = new (allocator_) UsePosition(
@@ -181,7 +224,7 @@
   }
 
   void AddPhiUse(HInstruction* instruction, size_t input_index, HBasicBlock* block) {
-    DCHECK(instruction->AsPhi() != nullptr);
+    DCHECK(instruction->IsPhi());
     first_use_ = new (allocator_) UsePosition(
         instruction, input_index, false, block->GetLifetimeEnd(), first_use_);
   }
@@ -192,8 +235,10 @@
     } else if (first_range_->GetStart() == end) {
       // There is a use in the following block.
       first_range_->start_ = start;
+    } else if (first_range_->GetStart() == start && first_range_->GetEnd() == end) {
+      DCHECK(is_fixed_);
     } else {
-      DCHECK(first_range_->GetStart() > end);
+      DCHECK_GT(first_range_->GetStart(), end);
       // There is a hole in the interval. Create a new range.
       first_range_ = new (allocator_) LiveRange(start, end, first_range_);
     }
@@ -215,7 +260,11 @@
   }
 
   bool HasSpillSlot() const { return spill_slot_ != kNoSpillSlot; }
-  void SetSpillSlot(int slot) { spill_slot_ = slot; }
+  void SetSpillSlot(int slot) {
+    DCHECK(!is_fixed_);
+    DCHECK(!is_temp_);
+    spill_slot_ = slot;
+  }
   int GetSpillSlot() const { return spill_slot_; }
 
   void SetFrom(size_t from) {
@@ -243,6 +292,9 @@
   }
 
   bool Covers(size_t position) const {
+    if (IsDeadAt(position)) {
+      return false;
+    }
     LiveRange* current = first_range_;
     while (current != nullptr) {
       if (position >= current->GetStart() && position < current->GetEnd()) {
@@ -288,6 +340,9 @@
   }
 
   size_t FirstRegisterUseAfter(size_t position) const {
+    if (is_temp_) {
+      return position == GetStart() ? position : kNoLifetime;
+    }
     if (position == GetStart() && defined_by_ != nullptr) {
       LocationSummary* locations = defined_by_->GetLocations();
       Location location = locations->Out();
@@ -310,7 +365,9 @@
       if (use_position >= position && !use->GetIsEnvironment()) {
         Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex());
         if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) {
-          return use_position;
+          // Return the lifetime just before the user, so that the interval has a register
+          // when entering the user.
+          return use->GetUser()->GetLifetimePosition() - 1;
         }
       }
       use = use->GetNext();
@@ -342,6 +399,7 @@
    * [position ... end)
    */
   LiveInterval* SplitAt(size_t position) {
+    DCHECK(!is_temp_);
     DCHECK(!is_fixed_);
     DCHECK_GT(position, GetStart());
 
@@ -398,12 +456,12 @@
     return nullptr;
   }
 
-  bool StartsBefore(LiveInterval* other) const {
+  bool StartsBeforeOrAt(LiveInterval* other) const {
     return GetStart() <= other->GetStart();
   }
 
   bool StartsAfter(LiveInterval* other) const {
-    return GetStart() >= other->GetStart();
+    return GetStart() > other->GetStart();
   }
 
   void Dump(std::ostream& stream) const {
@@ -453,7 +511,13 @@
   int spill_slot_;
 
   // Whether the interval is for a fixed register.
-  bool is_fixed_;
+  const bool is_fixed_;
+
+  // Whether the interval is for a temporary.
+  const bool is_temp_;
+
+  // Whether the interval is for a safepoint that calls on slow path.
+  const bool is_slow_path_safepoint_;
 
   // The instruction represented by this interval.
   HInstruction* const defined_by_;
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 13fa03f..e02a182 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -26,6 +26,8 @@
       HPhi* phi = it.Current()->AsPhi();
       if (phi->HasEnvironmentUses()) {
         // TODO: Do we want to keep that phi alive?
+        worklist_.Add(phi);
+        phi->SetLive();
         continue;
       }
       for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) {
@@ -53,8 +55,9 @@
     }
   }
 
-  // Remove phis that are not live. Visit in post order to ensure
-  // we only remove phis with no users (dead phis might use dead phis).
+  // Remove phis that are not live. Visit in post order so that phis
+  // that are not inputs of loop phis can be removed when they have
+  // no users left (dead phis might use dead phis).
   for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     HInstruction* current = block->GetFirstPhi();
@@ -62,6 +65,17 @@
     while (current != nullptr) {
       next = current->GetNext();
       if (current->AsPhi()->IsDead()) {
+        if (current->HasUses()) {
+          for (HUseIterator<HInstruction> it(current->GetUses()); !it.Done(); it.Advance()) {
+            HUseListNode<HInstruction>* user_node = it.Current();
+            HInstruction* user = user_node->GetUser();
+            DCHECK(user->IsLoopHeaderPhi());
+            DCHECK(user->AsPhi()->IsDead());
+            // Just put itself as an input. The phi will be removed in this loop anyway.
+            user->SetRawInputAt(user_node->GetIndex(), user);
+            current->RemoveUser(user, user_node->GetIndex());
+          }
+        }
         block->RemovePhi(current->AsPhi());
       }
       current = next;
@@ -88,12 +102,15 @@
 
     // Find if the inputs of the phi are the same instruction.
     HInstruction* candidate = phi->InputAt(0);
-    // A loop phi cannot have itself as the first phi.
+    // A loop phi cannot have itself as the first phi. Note that this
+    // check relies on our simplification pass ensuring the pre-header
+    // block is first in the list of predecessors of the loop header.
+    DCHECK(!phi->IsLoopHeaderPhi() || phi->GetBlock()->IsLoopPreHeaderFirstPredecessor());
     DCHECK_NE(phi, candidate);
 
     for (size_t i = 1; i < phi->InputCount(); ++i) {
       HInstruction* input = phi->InputAt(i);
-      // For a loop phi, If the input is the phi, the phi is still candidate for
+      // For a loop phi, if the input is the phi, the phi is still candidate for
       // elimination.
       if (input != candidate && input != phi) {
         candidate = nullptr;
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 088a5c4..fffe5c2 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -84,6 +84,9 @@
   ASSERT_NE(graph, nullptr);
 
   graph->BuildDominatorTree();
+  // Suspend checks implementation may change in the future, and this test relies
+  // on how instructions are ordered.
+  RemoveSuspendChecks(graph);
   graph->TransformToSSA();
   ReNumberInstructions(graph);
 
@@ -204,8 +207,8 @@
     "BasicBlock 2, pred: 3, 6, succ: 3\n"
     "  4: Phi(6, 0) [6]\n"
     "  5: Goto\n"
-    "BasicBlock 3, pred: 2, 5, succ: 2\n"
-    "  6: Phi(4, 0) [4]\n"
+    "BasicBlock 3, pred: 5, 2, succ: 2\n"
+    "  6: Phi(0, 4) [4]\n"
     "  7: Goto\n"
     "BasicBlock 4\n"
     // Synthesized blocks to avoid critical edge.
@@ -295,8 +298,8 @@
     "  2: Goto\n"
     "BasicBlock 1, pred: 0, succ: 4\n"
     "  3: Goto\n"
-    "BasicBlock 2, pred: 3, 4, succ: 5, 3\n"
-    "  4: Phi(1, 0) [9, 5, 5]\n"
+    "BasicBlock 2, pred: 4, 3, succ: 5, 3\n"
+    "  4: Phi(0, 1) [9, 5, 5]\n"
     "  5: Equal(4, 4) [6]\n"
     "  6: If(5)\n"
     "BasicBlock 3, pred: 2, succ: 2\n"
@@ -336,8 +339,8 @@
     "  6: Goto\n"
     "BasicBlock 3, pred: 1, succ: 8\n"
     "  7: Goto\n"
-    "BasicBlock 4, pred: 5, 8, succ: 6, 5\n"
-    "  8: Phi(8, 14) [8, 12, 9, 9]\n"
+    "BasicBlock 4, pred: 8, 5, succ: 6, 5\n"
+    "  8: Phi(14, 8) [8, 12, 9, 9]\n"
     "  9: Equal(8, 8) [10]\n"
     "  10: If(9)\n"
     "BasicBlock 5, pred: 4, succ: 4\n"
diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/ssa_type_propagation.cc
index 53fa74e..a860cb7 100644
--- a/compiler/optimizing/ssa_type_propagation.cc
+++ b/compiler/optimizing/ssa_type_propagation.cc
@@ -28,7 +28,11 @@
     case Primitive::kPrimNot:
       return existing;
     default:
-      return new_type;
+      // Phis are initialized with a void type, so if we are asked
+      // to merge with a void type, we should use the existing one.
+      return new_type == Primitive::kPrimVoid
+          ? existing
+          : new_type;
   }
 }
 
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 5e1329e..0ea11ad 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -26,9 +26,9 @@
 namespace art {
 
 /**
- * Collects and builds a CodeInfo for a method.
+ * Collects and builds stack maps for a method. All the stack maps
+ * for a method are placed in a CodeInfo object.
  */
-template<typename T>
 class StackMapStream : public ValueObject {
  public:
   explicit StackMapStream(ArenaAllocator* allocator)
@@ -47,7 +47,7 @@
   // See runtime/stack_map.h to know what these fields contain.
   struct StackMapEntry {
     uint32_t dex_pc;
-    T native_pc;
+    uint32_t native_pc_offset;
     uint32_t register_mask;
     BitVector* sp_mask;
     uint32_t num_dex_registers;
@@ -66,14 +66,14 @@
   };
 
   void AddStackMapEntry(uint32_t dex_pc,
-                        T native_pc,
+                        uint32_t native_pc_offset,
                         uint32_t register_mask,
                         BitVector* sp_mask,
                         uint32_t num_dex_registers,
                         uint8_t inlining_depth) {
     StackMapEntry entry;
     entry.dex_pc = dex_pc;
-    entry.native_pc = native_pc;
+    entry.native_pc_offset = native_pc_offset;
     entry.register_mask = register_mask;
     entry.sp_mask = sp_mask;
     entry.num_dex_registers = num_dex_registers;
@@ -82,7 +82,9 @@
     entry.inline_infos_start_index = inline_infos_.Size();
     stack_maps_.Add(entry);
 
-    stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+    if (sp_mask != nullptr) {
+      stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+    }
     if (inlining_depth > 0) {
       number_of_stack_maps_with_inline_info_++;
     }
@@ -102,14 +104,14 @@
   }
 
   size_t ComputeNeededSize() const {
-    return CodeInfo<T>::kFixedSize
+    return CodeInfo::kFixedSize
         + ComputeStackMapSize()
         + ComputeDexRegisterMapSize()
         + ComputeInlineInfoSize();
   }
 
   size_t ComputeStackMapSize() const {
-    return stack_maps_.Size() * (StackMap<T>::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
+    return stack_maps_.Size() * (StackMap::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
   }
 
   size_t ComputeDexRegisterMapSize() const {
@@ -130,11 +132,12 @@
   }
 
   size_t ComputeDexRegisterMapStart() const {
-    return CodeInfo<T>::kFixedSize + ComputeStackMapSize();
+    return CodeInfo::kFixedSize + ComputeStackMapSize();
   }
 
   void FillIn(MemoryRegion region) {
-    CodeInfo<T> code_info(region);
+    CodeInfo code_info(region);
+    code_info.SetOverallSize(region.size());
 
     size_t stack_mask_size = StackMaskEncodingSize(stack_mask_max_);
     uint8_t* memory_start = region.start();
@@ -153,13 +156,15 @@
     uintptr_t next_dex_register_map_offset = 0;
     uintptr_t next_inline_info_offset = 0;
     for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) {
-      StackMap<T> stack_map = code_info.GetStackMapAt(i);
+      StackMap stack_map = code_info.GetStackMapAt(i);
       StackMapEntry entry = stack_maps_.Get(i);
 
       stack_map.SetDexPc(entry.dex_pc);
-      stack_map.SetNativePc(entry.native_pc);
+      stack_map.SetNativePcOffset(entry.native_pc_offset);
       stack_map.SetRegisterMask(entry.register_mask);
-      stack_map.SetStackMask(*entry.sp_mask);
+      if (entry.sp_mask != nullptr) {
+        stack_map.SetStackMask(*entry.sp_mask);
+      }
 
       // Set the register map.
       MemoryRegion region = dex_register_maps_region.Subregion(
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index a70259e..5ee6ae0 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -34,7 +34,7 @@
 TEST(StackMapTest, Test1) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  StackMapStream<size_t> stream(&arena);
+  StackMapStream stream(&arena);
 
   ArenaBitVector sp_mask(&arena, 0, false);
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, 2, 0);
@@ -46,15 +46,15 @@
   MemoryRegion region(memory, size);
   stream.FillIn(region);
 
-  CodeInfo<size_t> code_info(region);
+  CodeInfo code_info(region);
   ASSERT_EQ(0u, code_info.GetStackMaskSize());
   ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
 
-  StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+  StackMap stack_map = code_info.GetStackMapAt(0);
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
   ASSERT_EQ(0u, stack_map.GetDexPc());
-  ASSERT_EQ(64u, stack_map.GetNativePc());
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset());
   ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
   ASSERT_FALSE(stack_map.HasInlineInfo());
 
@@ -71,7 +71,7 @@
 TEST(StackMapTest, Test2) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  StackMapStream<size_t> stream(&arena);
+  StackMapStream stream(&arena);
 
   ArenaBitVector sp_mask1(&arena, 0, true);
   sp_mask1.SetBit(2);
@@ -93,15 +93,15 @@
   MemoryRegion region(memory, size);
   stream.FillIn(region);
 
-  CodeInfo<size_t> code_info(region);
+  CodeInfo code_info(region);
   ASSERT_EQ(1u, code_info.GetStackMaskSize());
   ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
 
-  StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+  StackMap stack_map = code_info.GetStackMapAt(0);
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
   ASSERT_EQ(0u, stack_map.GetDexPc());
-  ASSERT_EQ(64u, stack_map.GetNativePc());
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset());
   ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
 
   MemoryRegion stack_mask = stack_map.GetStackMask();
@@ -120,9 +120,9 @@
 
   stack_map = code_info.GetStackMapAt(1);
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(128u)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u)));
   ASSERT_EQ(1u, stack_map.GetDexPc());
-  ASSERT_EQ(128u, stack_map.GetNativePc());
+  ASSERT_EQ(128u, stack_map.GetNativePcOffset());
   ASSERT_EQ(0xFFu, stack_map.GetRegisterMask());
 
   stack_mask = stack_map.GetStackMask();
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
new file mode 100644
index 0000000..2e48ee8
--- /dev/null
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "builder.h"
+#include "dex_instruction.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+/**
+ * Check that the HGraphBuilder adds suspend checks to backward branches.
+ */
+
+static void TestCode(const uint16_t* data) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraphBuilder builder(&allocator);
+  const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
+  HGraph* graph = builder.BuildGraph(*item);
+  ASSERT_NE(graph, nullptr);
+
+  HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors().Get(0);
+  HInstruction* first_instruction = first_block->GetFirstInstruction();
+  // Account for some tests having a store local as first instruction.
+  ASSERT_TRUE(first_instruction->IsSuspendCheck()
+              || first_instruction->GetNext()->IsSuspendCheck());
+}
+
+TEST(CodegenTest, CFG1) {
+  const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+    Instruction::NOP,
+    Instruction::GOTO | 0xFF00);
+
+  TestCode(data);
+}
+
+TEST(CodegenTest, CFG2) {
+  const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+    Instruction::GOTO_32, 0, 0);
+
+  TestCode(data);
+}
+
+TEST(CodegenTest, CFG3) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_EQ, 0xFFFF,
+    Instruction::RETURN_VOID);
+
+  TestCode(data);
+}
+
+TEST(CodegenTest, CFG4) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_NE, 0xFFFF,
+    Instruction::RETURN_VOID);
+
+  TestCode(data);
+}
+
+TEST(CodegenTest, CFG5) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_EQZ, 0xFFFF,
+    Instruction::RETURN_VOID);
+
+  TestCode(data);
+}
+
+TEST(CodegenTest, CFG6) {
+  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+    Instruction::CONST_4 | 0 | 0,
+    Instruction::IF_NEZ, 0xFFFF,
+    Instruction::RETURN_VOID);
+
+  TestCode(data);
+}
+}  // namespace art
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index d5225c1..6da375a 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -16,7 +16,7 @@
 
 #include "trampoline_compiler.h"
 
-#include "jni_internal.h"
+#include "jni_env_ext.h"
 #include "utils/arm/assembler_arm.h"
 #include "utils/arm64/assembler_arm64.h"
 #include "utils/mips/assembler_mips.h"
diff --git a/compiler/utils/arena_allocator.cc b/compiler/utils/arena_allocator.cc
index da49524..516ac2b 100644
--- a/compiler/utils/arena_allocator.cc
+++ b/compiler/utils/arena_allocator.cc
@@ -35,12 +35,23 @@
 const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
   "Misc       ",
   "BasicBlock ",
+  "BBList     "
+  "BBPreds    ",
+  "DfsPreOrd  ",
+  "DfsPostOrd ",
+  "DomPostOrd ",
+  "TopoOrd    ",
+  "Lowering   ",
   "LIR        ",
   "LIR masks  ",
+  "SwitchTbl  ",
+  "FillArray  ",
+  "SlowPaths  ",
   "MIR        ",
   "DataFlow   ",
   "GrowList   ",
   "GrowBitMap ",
+  "SSA2Dalvik ",
   "Dalvik2SSA ",
   "DebugInfo  ",
   "Successor  ",
diff --git a/compiler/utils/arena_allocator.h b/compiler/utils/arena_allocator.h
index 7bfbb6f..b2f5ca9 100644
--- a/compiler/utils/arena_allocator.h
+++ b/compiler/utils/arena_allocator.h
@@ -44,12 +44,23 @@
 enum ArenaAllocKind {
   kArenaAllocMisc,
   kArenaAllocBB,
+  kArenaAllocBBList,
+  kArenaAllocBBPredecessors,
+  kArenaAllocDfsPreOrder,
+  kArenaAllocDfsPostOrder,
+  kArenaAllocDomPostOrder,
+  kArenaAllocTopologicalSortOrder,
+  kArenaAllocLoweringInfo,
   kArenaAllocLIR,
   kArenaAllocLIRResourceMask,
+  kArenaAllocSwitchTable,
+  kArenaAllocFillArrayData,
+  kArenaAllocSlowPaths,
   kArenaAllocMIR,
   kArenaAllocDFInfo,
   kArenaAllocGrowableArray,
   kArenaAllocGrowableBitMap,
+  kArenaAllocSSAToDalvikMap,
   kArenaAllocDalvikToSSAMap,
   kArenaAllocDebugInfo,
   kArenaAllocSuccessor,
diff --git a/compiler/utils/arena_bit_vector.cc b/compiler/utils/arena_bit_vector.cc
index 39f7d18..de35f3d 100644
--- a/compiler/utils/arena_bit_vector.cc
+++ b/compiler/utils/arena_bit_vector.cc
@@ -16,11 +16,12 @@
 
 #include "arena_allocator.h"
 #include "arena_bit_vector.h"
+#include "base/allocator.h"
 
 namespace art {
 
 template <typename ArenaAlloc>
-class ArenaBitVectorAllocator : public Allocator {
+class ArenaBitVectorAllocator FINAL : public Allocator {
  public:
   explicit ArenaBitVectorAllocator(ArenaAlloc* arena) : arena_(arena) {}
   ~ArenaBitVectorAllocator() {}
@@ -37,7 +38,7 @@
   static void operator delete(void* p) {}  // Nop.
 
  private:
-  ArenaAlloc* arena_;
+  ArenaAlloc* const arena_;
   DISALLOW_COPY_AND_ASSIGN(ArenaBitVectorAllocator);
 };
 
diff --git a/compiler/utils/arena_bit_vector.h b/compiler/utils/arena_bit_vector.h
index 485ed76..c92658f 100644
--- a/compiler/utils/arena_bit_vector.h
+++ b/compiler/utils/arena_bit_vector.h
@@ -51,23 +51,23 @@
  * A BitVector implementation that uses Arena allocation.
  */
 class ArenaBitVector : public BitVector {
-  public:
-    ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
-                   OatBitMapKind kind = kBitMapMisc);
-    ArenaBitVector(ScopedArenaAllocator* arena, uint32_t start_bits, bool expandable,
-                   OatBitMapKind kind = kBitMapMisc);
-    ~ArenaBitVector() {}
+ public:
+  ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
+                 OatBitMapKind kind = kBitMapMisc);
+  ArenaBitVector(ScopedArenaAllocator* arena, uint32_t start_bits, bool expandable,
+                 OatBitMapKind kind = kBitMapMisc);
+  ~ArenaBitVector() {}
 
   static void* operator new(size_t size, ArenaAllocator* arena) {
-     return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
+    return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
   }
   static void* operator new(size_t size, ScopedArenaAllocator* arena) {
-     return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
+    return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
   }
   static void operator delete(void* p) {}  // Nop.
 
-  private:
-    const OatBitMapKind kind_;      // for memory use tuning. TODO: currently unused.
+ private:
+  const OatBitMapKind kind_;      // for memory use tuning. TODO: currently unused.
 };
 
 
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 671ccb6..637a1ff 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -130,7 +130,7 @@
             return ROR << 4 | static_cast<uint32_t>(rm_);
           } else {
             uint32_t imm3 = immed_ >> 2;
-            uint32_t imm2 = immed_ & 0b11;
+            uint32_t imm2 = immed_ & 3U /* 0b11 */;
 
             return imm3 << 12 | imm2 << 6 | shift_ << 4 |
                 static_cast<uint32_t>(rm_);
@@ -229,8 +229,8 @@
       uint32_t PUW = am >> 21;   // Move down to bottom of word.
       PUW = (PUW >> 1) | (PUW & 1);   // Bits 3, 2 and 0.
       // If P is 0 then W must be 1 (Different from ARM).
-      if ((PUW & 0b100) == 0) {
-        PUW |= 0b1;
+      if ((PUW & 4U /* 0b100 */) == 0) {
+        PUW |= 1U /* 0b1 */;
       }
       encoding |= B11 | PUW << 8 | offset;
     } else {
@@ -267,17 +267,17 @@
   uint32_t am = am_;
   // If P is 0 then W must be 1 (Different from ARM).
   uint32_t PU1W = am_ >> 21;   // Move down to bottom of word.
-  if ((PU1W & 0b1000) == 0) {
+  if ((PU1W & 8U /* 0b1000 */) == 0) {
     am |= 1 << 21;      // Set W bit.
   }
   if (offset_ < 0) {
     int32_t off = -offset_;
     CHECK_LT(off, 1024);
-    CHECK_EQ((off & 0b11), 0);    // Must be multiple of 4.
+    CHECK_EQ((off & 3 /* 0b11 */), 0);    // Must be multiple of 4.
     encoding = (am ^ (1 << kUShift)) | off >> 2;  // Flip U to adjust sign.
   } else {
     CHECK_LT(offset_, 1024);
-    CHECK_EQ((offset_ & 0b11), 0);    // Must be multiple of 4.
+    CHECK_EQ((offset_ & 3 /* 0b11 */), 0);    // Must be multiple of 4.
     encoding =  am | offset_ >> 2;
   }
   encoding |= static_cast<uint32_t>(rn_) << 16;
@@ -886,8 +886,8 @@
   /* Put it all together */
   uint32_t v = 8 + z_leading;
 
-  uint32_t i = (v & 0b10000) >> 4;
-  uint32_t imm3 = (v >> 1) & 0b111;
+  uint32_t i = (v & 16U /* 0b10000 */) >> 4;
+  uint32_t imm3 = (v >> 1) & 7U /* 0b111 */;
   uint32_t a = v & 1;
   return value | i << 26 | imm3 << 12 | a << 7;
 }
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 267bba8..6af69c8 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -955,11 +955,11 @@
   if (dbl) {
     // Encoded as D:Vd.
     D = (reg >> 4) & 1;
-    Vd = reg & 0b1111;
+    Vd = reg & 15U /* 0b1111 */;
   } else {
     // Encoded as Vd:D.
     D = reg & 1;
-    Vd = (reg >> 1) & 0b1111;
+    Vd = (reg >> 1) & 15U /* 0b1111 */;
   }
   int32_t encoding = B27 | B26 | B21 | B19 | B18 | B16 |
                     B11 | B9 |
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 4904428..7968a77 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -159,8 +159,8 @@
     Emit16(encoding);
   } else {
     // 32 bit.
-    uint32_t op1 = 0b000;
-    uint32_t op2 = 0b00;
+    uint32_t op1 = 0U /* 0b000 */;
+    uint32_t op2 = 0U /* 0b00 */;
     int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
         op1 << 20 |
         B15 | B14 | B13 | B12 |
@@ -176,8 +176,8 @@
 
 void Thumb2Assembler::mla(Register rd, Register rn, Register rm, Register ra,
                           Condition cond) {
-  uint32_t op1 = 0b000;
-  uint32_t op2 = 0b00;
+  uint32_t op1 = 0U /* 0b000 */;
+  uint32_t op2 = 0U /* 0b00 */;
   int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
       op1 << 20 |
       op2 << 4 |
@@ -192,8 +192,8 @@
 
 void Thumb2Assembler::mls(Register rd, Register rn, Register rm, Register ra,
                           Condition cond) {
-  uint32_t op1 = 0b000;
-  uint32_t op2 = 0b01;
+  uint32_t op1 = 0U /* 0b000 */;
+  uint32_t op2 = 01 /* 0b01 */;
   int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
       op1 << 20 |
       op2 << 4 |
@@ -208,8 +208,8 @@
 
 void Thumb2Assembler::umull(Register rd_lo, Register rd_hi, Register rn,
                             Register rm, Condition cond) {
-  uint32_t op1 = 0b010;
-  uint32_t op2 = 0b0000;
+  uint32_t op1 = 2U /* 0b010; */;
+  uint32_t op2 = 0U /* 0b0000 */;
   int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 |
       op1 << 20 |
       op2 << 4 |
@@ -223,8 +223,8 @@
 
 
 void Thumb2Assembler::sdiv(Register rd, Register rn, Register rm, Condition cond) {
-  uint32_t op1 = 0b001;
-  uint32_t op2 = 0b1111;
+  uint32_t op1 = 1U  /* 0b001 */;
+  uint32_t op2 = 15U /* 0b1111 */;
   int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B20 |
       op1 << 20 |
       op2 << 4 |
@@ -238,8 +238,8 @@
 
 
 void Thumb2Assembler::udiv(Register rd, Register rn, Register rm, Condition cond) {
-  uint32_t op1 = 0b001;
-  uint32_t op2 = 0b1111;
+  uint32_t op1 = 1U  /* 0b001 */;
+  uint32_t op2 = 15U /* 0b1111 */;
   int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B21 | B20 |
       op1 << 20 |
       op2 << 4 |
@@ -733,29 +733,29 @@
                                               Register rn,
                                               Register rd,
                                               const ShifterOperand& so) {
-  uint8_t thumb_opcode = 0b11111111;
+  uint8_t thumb_opcode = 255U /* 0b11111111 */;
   switch (opcode) {
-    case AND: thumb_opcode = 0b0000; break;
-    case EOR: thumb_opcode = 0b0100; break;
-    case SUB: thumb_opcode = 0b1101; break;
-    case RSB: thumb_opcode = 0b1110; break;
-    case ADD: thumb_opcode = 0b1000; break;
-    case ADC: thumb_opcode = 0b1010; break;
-    case SBC: thumb_opcode = 0b1011; break;
+    case AND: thumb_opcode =  0U /* 0b0000 */; break;
+    case EOR: thumb_opcode =  4U /* 0b0100 */; break;
+    case SUB: thumb_opcode = 13U /* 0b1101 */; break;
+    case RSB: thumb_opcode = 14U /* 0b1110 */; break;
+    case ADD: thumb_opcode =  8U /* 0b1000 */; break;
+    case ADC: thumb_opcode = 10U /* 0b1010 */; break;
+    case SBC: thumb_opcode = 11U /* 0b1011 */; break;
     case RSC: break;
-    case TST: thumb_opcode = 0b0000; set_cc = true; rd = PC; break;
-    case TEQ: thumb_opcode = 0b0100; set_cc = true; rd = PC; break;
-    case CMP: thumb_opcode = 0b1101; set_cc = true; rd = PC; break;
-    case CMN: thumb_opcode = 0b1000; set_cc = true; rd = PC; break;
-    case ORR: thumb_opcode = 0b0010; break;
-    case MOV: thumb_opcode = 0b0010; rn = PC; break;
-    case BIC: thumb_opcode = 0b0001; break;
-    case MVN: thumb_opcode = 0b0011; rn = PC; break;
+    case TST: thumb_opcode =  0U /* 0b0000 */; set_cc = true; rd = PC; break;
+    case TEQ: thumb_opcode =  4U /* 0b0100 */; set_cc = true; rd = PC; break;
+    case CMP: thumb_opcode = 13U /* 0b1101 */; set_cc = true; rd = PC; break;
+    case CMN: thumb_opcode =  8U /* 0b1000 */; set_cc = true; rd = PC; break;
+    case ORR: thumb_opcode =  2U /* 0b0010 */; break;
+    case MOV: thumb_opcode =  2U /* 0b0010 */; rn = PC; break;
+    case BIC: thumb_opcode =  1U /* 0b0001 */; break;
+    case MVN: thumb_opcode =  3U /* 0b0011 */; rn = PC; break;
     default:
       break;
   }
 
-  if (thumb_opcode == 0b11111111) {
+  if (thumb_opcode == 255U /* 0b11111111 */) {
     LOG(FATAL) << "Invalid thumb2 opcode " << opcode;
   }
 
@@ -764,14 +764,14 @@
     // Check special cases.
     if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12))) {
       if (opcode == SUB) {
-        thumb_opcode = 0b0101;
+        thumb_opcode = 5U /* 0b0101 */;
       } else {
         thumb_opcode = 0;
       }
       uint32_t imm = so.GetImmediate();
 
       uint32_t i = (imm >> 11) & 1;
-      uint32_t imm3 = (imm >> 8) & 0b111;
+      uint32_t imm3 = (imm >> 8) & 7U /* 0b111 */;
       uint32_t imm8 = imm & 0xff;
 
       encoding = B31 | B30 | B29 | B28 | B25 |
@@ -817,9 +817,9 @@
     Emit16BitAddSub(cond, opcode, set_cc, rn, rd, so);
     return;
   }
-  uint8_t thumb_opcode = 0b11111111;
+  uint8_t thumb_opcode = 255U /* 0b11111111 */;
   // Thumb1.
-  uint8_t dp_opcode = 0b01;
+  uint8_t dp_opcode = 1U /* 0b01 */;
   uint8_t opcode_shift = 6;
   uint8_t rd_shift = 0;
   uint8_t rn_shift = 3;
@@ -839,13 +839,13 @@
     rn = so.GetRegister();
 
     switch (so.GetShift()) {
-    case LSL: thumb_opcode = 0b00; break;
-    case LSR: thumb_opcode = 0b01; break;
-    case ASR: thumb_opcode = 0b10; break;
+    case LSL: thumb_opcode = 0U /* 0b00 */; break;
+    case LSR: thumb_opcode = 1U /* 0b01 */; break;
+    case ASR: thumb_opcode = 2U /* 0b10 */; break;
     case ROR:
       // ROR doesn't allow immediates.
-      thumb_opcode = 0b111;
-      dp_opcode = 0b01;
+      thumb_opcode = 7U /* 0b111 */;
+      dp_opcode = 1U /* 0b01 */;
       opcode_shift = 6;
       use_immediate = false;
       break;
@@ -860,68 +860,68 @@
     }
 
     switch (opcode) {
-      case AND: thumb_opcode = 0b0000; break;
-      case EOR: thumb_opcode = 0b0001; break;
+      case AND: thumb_opcode = 0U /* 0b0000 */; break;
+      case EOR: thumb_opcode = 1U /* 0b0001 */; break;
       case SUB: break;
-      case RSB: thumb_opcode = 0b1001; break;
+      case RSB: thumb_opcode = 9U /* 0b1001 */; break;
       case ADD: break;
-      case ADC: thumb_opcode = 0b0101; break;
-      case SBC: thumb_opcode = 0b0110; break;
+      case ADC: thumb_opcode = 5U /* 0b0101 */; break;
+      case SBC: thumb_opcode = 6U /* 0b0110 */; break;
       case RSC: break;
-      case TST: thumb_opcode = 0b1000; rn = so.GetRegister(); break;
+      case TST: thumb_opcode = 8U /* 0b1000 */; rn = so.GetRegister(); break;
       case TEQ: break;
       case CMP:
         if (use_immediate) {
           // T2 encoding.
            dp_opcode = 0;
            opcode_shift = 11;
-           thumb_opcode = 0b101;
+           thumb_opcode = 5U /* 0b101 */;
            rd_shift = 8;
            rn_shift = 8;
         } else {
-          thumb_opcode = 0b1010;
+          thumb_opcode = 10U /* 0b1010 */;
           rd = rn;
           rn = so.GetRegister();
         }
 
         break;
       case CMN: {
-        thumb_opcode = 0b1011;
+        thumb_opcode = 11U /* 0b1011 */;
         rd = rn;
         rn = so.GetRegister();
         break;
       }
-      case ORR: thumb_opcode = 0b1100; break;
+      case ORR: thumb_opcode = 12U /* 0b1100 */; break;
       case MOV:
         dp_opcode = 0;
         if (use_immediate) {
           // T2 encoding.
           opcode_shift = 11;
-          thumb_opcode = 0b100;
+          thumb_opcode = 4U /* 0b100 */;
           rd_shift = 8;
           rn_shift = 8;
         } else {
           rn = so.GetRegister();
           if (IsHighRegister(rn) || IsHighRegister(rd)) {
             // Special mov for high registers.
-            dp_opcode = 0b01;
+            dp_opcode = 1U /* 0b01 */;
             opcode_shift = 7;
             // Put the top bit of rd into the bottom bit of the opcode.
-            thumb_opcode = 0b0001100 | static_cast<uint32_t>(rd) >> 3;
-            rd = static_cast<Register>(static_cast<uint32_t>(rd) & 0b111);
+            thumb_opcode = 12U /* 0b0001100 */ | static_cast<uint32_t>(rd) >> 3;
+            rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
           } else {
             thumb_opcode = 0;
           }
         }
         break;
-      case BIC: thumb_opcode = 0b1110; break;
-      case MVN: thumb_opcode = 0b1111; rn = so.GetRegister(); break;
+      case BIC: thumb_opcode = 14U /* 0b1110 */; break;
+      case MVN: thumb_opcode = 15U /* 0b1111 */; rn = so.GetRegister(); break;
       default:
         break;
     }
   }
 
-  if (thumb_opcode == 0b11111111) {
+  if (thumb_opcode == 255U /* 0b11111111 */) {
     LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
   }
 
@@ -962,17 +962,17 @@
         Register rm = so.GetRegister();
         if (rn == rd) {
           // Can use T2 encoding (allows 4 bit registers)
-          dp_opcode = 0b01;
+          dp_opcode = 1U /* 0b01 */;
           opcode_shift = 10;
-          thumb_opcode = 0b0001;
+          thumb_opcode = 1U /* 0b0001 */;
           // Make Rn also contain the top bit of rd.
           rn = static_cast<Register>(static_cast<uint32_t>(rm) |
-                                     (static_cast<uint32_t>(rd) & 0b1000) << 1);
-          rd = static_cast<Register>(static_cast<uint32_t>(rd) & 0b111);
+                                     (static_cast<uint32_t>(rd) & 8U /* 0b1000 */) << 1);
+          rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
         } else {
           // T1.
           opcode_shift = 9;
-          thumb_opcode = 0b01100;
+          thumb_opcode = 12U /* 0b01100 */;
           immediate = static_cast<uint32_t>(so.GetRegister());
           use_immediate = true;
           immediate_shift = 6;
@@ -981,11 +981,11 @@
         // Immediate.
         if (rd == SP && rn == SP) {
           // ADD sp, sp, #imm
-          dp_opcode = 0b10;
-          thumb_opcode = 0b11;
+          dp_opcode = 2U /* 0b10 */;
+          thumb_opcode = 3U /* 0b11 */;
           opcode_shift = 12;
           CHECK_LT(immediate, (1 << 9));
-          CHECK_EQ((immediate & 0b11), 0);
+          CHECK_EQ((immediate & 3 /* 0b11 */), 0);
 
           // Remove rd and rn from instruction by orring it with immed and clearing bits.
           rn = R0;
@@ -995,11 +995,11 @@
           immediate >>= 2;
         } else if (rd != SP && rn == SP) {
           // ADD rd, SP, #imm
-          dp_opcode = 0b10;
-          thumb_opcode = 0b101;
+          dp_opcode = 2U /* 0b10 */;
+          thumb_opcode = 5U /* 0b101 */;
           opcode_shift = 11;
           CHECK_LT(immediate, (1 << 10));
-          CHECK_EQ((immediate & 0b11), 0);
+          CHECK_EQ((immediate & 3 /* 0b11 */), 0);
 
           // Remove rn from instruction.
           rn = R0;
@@ -1009,12 +1009,12 @@
         } else if (rn != rd) {
           // Must use T1.
           opcode_shift = 9;
-          thumb_opcode = 0b01110;
+          thumb_opcode = 14U /* 0b01110 */;
           immediate_shift = 6;
         } else {
           // T2 encoding.
           opcode_shift = 11;
-          thumb_opcode = 0b110;
+          thumb_opcode = 6U /* 0b110 */;
           rd_shift = 8;
           rn_shift = 8;
         }
@@ -1025,18 +1025,18 @@
       if (so.IsRegister()) {
          // T1.
          opcode_shift = 9;
-         thumb_opcode = 0b01101;
+         thumb_opcode = 13U /* 0b01101 */;
          immediate = static_cast<uint32_t>(so.GetRegister());
          use_immediate = true;
          immediate_shift = 6;
        } else {
          if (rd == SP && rn == SP) {
            // SUB sp, sp, #imm
-           dp_opcode = 0b10;
-           thumb_opcode = 0b1100001;
+           dp_opcode = 2U /* 0b10 */;
+           thumb_opcode = 0x61 /* 0b1100001 */;
            opcode_shift = 7;
            CHECK_LT(immediate, (1 << 9));
-           CHECK_EQ((immediate & 0b11), 0);
+           CHECK_EQ((immediate & 3 /* 0b11 */), 0);
 
            // Remove rd and rn from instruction by orring it with immed and clearing bits.
            rn = R0;
@@ -1047,12 +1047,12 @@
          } else if (rn != rd) {
            // Must use T1.
            opcode_shift = 9;
-           thumb_opcode = 0b01111;
+           thumb_opcode = 15U /* 0b01111 */;
            immediate_shift = 6;
          } else {
            // T2 encoding.
            opcode_shift = 11;
-           thumb_opcode = 0b111;
+           thumb_opcode = 7U /* 0b111 */;
            rd_shift = 8;
            rn_shift = 8;
          }
@@ -1094,11 +1094,11 @@
   if (IsHighRegister(rd) || IsHighRegister(rm) || shift == ROR || shift == RRX) {
     uint16_t opcode = 0;
     switch (shift) {
-      case LSL: opcode = 0b00; break;
-      case LSR: opcode = 0b01; break;
-      case ASR: opcode = 0b10; break;
-      case ROR: opcode = 0b11; break;
-      case RRX: opcode = 0b11; amount = 0; break;
+      case LSL: opcode = 0U /* 0b00 */; break;
+      case LSR: opcode = 1U /* 0b01 */; break;
+      case ASR: opcode = 2U /* 0b10 */; break;
+      case ROR: opcode = 3U /* 0b11 */; break;
+      case RRX: opcode = 3U /* 0b11 */; amount = 0; break;
       default:
         LOG(FATAL) << "Unsupported thumb2 shift opcode";
     }
@@ -1106,7 +1106,7 @@
     int32_t encoding = B31 | B30 | B29 | B27 | B25 | B22 |
         0xf << 16 | (setcc ? B20 : 0);
     uint32_t imm3 = amount >> 2;
-    uint32_t imm2 = amount & 0b11;
+    uint32_t imm2 = amount & 3U /* 0b11 */;
     encoding |= imm3 << 12 | imm2 << 6 | static_cast<int16_t>(rm) |
         static_cast<int16_t>(rd) << 8 | opcode << 4;
     Emit32(encoding);
@@ -1114,9 +1114,9 @@
     // 16 bit shift
     uint16_t opcode = 0;
     switch (shift) {
-      case LSL: opcode = 0b00; break;
-      case LSR: opcode = 0b01; break;
-      case ASR: opcode = 0b10; break;
+      case LSL: opcode = 0U /* 0b00 */; break;
+      case LSR: opcode = 1U /* 0b01 */; break;
+      case ASR: opcode = 2U /* 0b10 */; break;
       default:
          LOG(FATAL) << "Unsupported thumb2 shift opcode";
     }
@@ -1136,10 +1136,10 @@
   if (must_be_32bit) {
     uint16_t opcode = 0;
      switch (shift) {
-       case LSL: opcode = 0b00; break;
-       case LSR: opcode = 0b01; break;
-       case ASR: opcode = 0b10; break;
-       case ROR: opcode = 0b11; break;
+       case LSL: opcode = 0U /* 0b00 */; break;
+       case LSR: opcode = 1U /* 0b01 */; break;
+       case ASR: opcode = 2U /* 0b10 */; break;
+       case ROR: opcode = 3U /* 0b11 */; break;
        default:
          LOG(FATAL) << "Unsupported thumb2 shift opcode";
      }
@@ -1152,9 +1152,9 @@
   } else {
     uint16_t opcode = 0;
     switch (shift) {
-      case LSL: opcode = 0b0010; break;
-      case LSR: opcode = 0b0011; break;
-      case ASR: opcode = 0b0100; break;
+      case LSL: opcode = 2U /* 0b0010 */; break;
+      case LSR: opcode = 3U /* 0b0011 */; break;
+      case ASR: opcode = 4U /* 0b0100 */; break;
       default:
          LOG(FATAL) << "Unsupported thumb2 shift opcode";
     }
@@ -1204,7 +1204,7 @@
     if (IsCompareAndBranch()) {
       offset -= 4;
       uint16_t i = (offset >> 6) & 1;
-      uint16_t imm5 = (offset >> 1) & 0b11111;
+      uint16_t imm5 = (offset >> 1) & 31U /* 0b11111 */;
       int16_t encoding = B15 | B13 | B12 |
             (type_ ==  kCompareAndBranchNonZero ? B11 : 0) |
             static_cast<uint32_t>(rn_) |
@@ -1304,15 +1304,15 @@
       bool sp_relative = false;
 
       if (byte) {
-        opA = 0b0111;
+        opA = 7U /* 0b0111 */;
       } else if (half) {
-        opA = 0b1000;
+        opA = 8U /* 0b1000 */;
       } else {
         if (rn == SP) {
-          opA = 0b1001;
+          opA = 9U /* 0b1001 */;
           sp_relative = true;
         } else {
-          opA = 0b0110;
+          opA = 6U /* 0b0110 */;
         }
       }
       int16_t encoding = opA << 12 |
@@ -1322,7 +1322,7 @@
       if (sp_relative) {
         // SP relative, 10 bit offset.
         CHECK_LT(offset, (1 << 10));
-        CHECK_EQ((offset & 0b11), 0);
+        CHECK_EQ((offset & 3 /* 0b11 */), 0);
         encoding |= rd << 8 | offset >> 2;
       } else {
         // No SP relative.  The offset is shifted right depending on
@@ -1335,12 +1335,12 @@
         } else if (half) {
           // 6 bit offset, shifted by 1.
           CHECK_LT(offset, (1 << 6));
-          CHECK_EQ((offset & 0b1), 0);
+          CHECK_EQ((offset & 1 /* 0b1 */), 0);
           offset >>= 1;
         } else {
           // 7 bit offset, shifted by 2.
           CHECK_LT(offset, (1 << 7));
-          CHECK_EQ((offset & 0b11), 0);
+          CHECK_EQ((offset & 3 /* 0b11 */), 0);
           offset >>= 2;
         }
         encoding |= rn << 3 | offset  << 6;
@@ -1428,11 +1428,11 @@
     switch (am) {
       case IA:
       case IA_W:
-        op = 0b01;
+        op = 1U /* 0b01 */;
         break;
       case DB:
       case DB_W:
-        op = 0b10;
+        op = 2U /* 0b10 */;
         break;
       case DA:
       case IB:
@@ -1534,9 +1534,9 @@
 
   if (must_be_32bit) {
     // Use encoding T3.
-    uint32_t imm4 = (imm16 >> 12) & 0b1111;
-    uint32_t i = (imm16 >> 11) & 0b1;
-    uint32_t imm3 = (imm16 >> 8) & 0b111;
+    uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */;
+    uint32_t i = (imm16 >> 11) & 1U /* 0b1 */;
+    uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */;
     uint32_t imm8 = imm16 & 0xff;
     int32_t encoding = B31 | B30 | B29 | B28 |
                     B25 | B22 |
@@ -1557,9 +1557,9 @@
 void Thumb2Assembler::movt(Register rd, uint16_t imm16, Condition cond) {
   CheckCondition(cond);
   // Always 32 bits.
-  uint32_t imm4 = (imm16 >> 12) & 0b1111;
-  uint32_t i = (imm16 >> 11) & 0b1;
-  uint32_t imm3 = (imm16 >> 8) & 0b111;
+  uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */;
+  uint32_t i = (imm16 >> 11) & 1U /* 0b1 */;
+  uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */;
   uint32_t imm8 = imm16 & 0xff;
   int32_t encoding = B31 | B30 | B29 | B28 |
                   B25 | B23 | B22 |
@@ -1638,9 +1638,9 @@
 
 void Thumb2Assembler::nop(Condition cond) {
   CheckCondition(cond);
-  int16_t encoding = B15 | B13 | B12 |
+  uint16_t encoding = B15 | B13 | B12 |
       B11 | B10 | B9 | B8;
-  Emit16(encoding);
+  Emit16(static_cast<int16_t>(encoding));
 }
 
 
@@ -1840,17 +1840,17 @@
   if (dbl) {
     // Encoded as D:Vd.
     D = (reg >> 4) & 1;
-    Vd = reg & 0b1111;
+    Vd = reg & 15U /* 0b1111 */;
   } else {
     // Encoded as Vd:D.
     D = reg & 1;
-    Vd = (reg >> 1) & 0b1111;
+    Vd = (reg >> 1) & 15U /* 0b1111 */;
   }
   int32_t encoding = B27 | B26 | B21 | B19 | B18 | B16 |
                     B11 | B9 |
         (dbl ? B8 : 0) |
         (push ? B24 : (B23 | B20)) |
-        0b1110 << 28 |
+        14U /* 0b1110 */ << 28 |
         nregs << (dbl ? 1 : 0) |
         D << 22 |
         Vd << 12;
@@ -1992,7 +1992,7 @@
       mask |= ToItMask(i3, firstcond0, 1);
       SetItCondition(i3, firstcond, 3);
       if (i3 != kItOmitted) {
-        mask |= 0b0001;
+        mask |= 1U /* 0b0001 */;
       }
     }
   }
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 3f90f21..c82b4f0 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -21,6 +21,8 @@
 #include "thread.h"
 #include "utils.h"
 
+using namespace vixl;  // NOLINT(build/namespaces)
+
 namespace art {
 namespace arm64 {
 
@@ -75,7 +77,7 @@
 
 void Arm64Assembler::AddConstant(Register rd, Register rn, int32_t value,
                                  Condition cond) {
-  if ((cond == AL) || (cond == NV)) {
+  if ((cond == al) || (cond == nv)) {
     // VIXL macro-assembler handles all variants.
     ___ Add(reg_x(rd), reg_x(rn), value);
   } else {
@@ -85,7 +87,7 @@
     temps.Exclude(reg_x(rd), reg_x(rn));
     vixl::Register temp = temps.AcquireX();
     ___ Add(temp, reg_x(rn), value);
-    ___ Csel(reg_x(rd), temp, reg_x(rd), COND_OP(cond));
+    ___ Csel(reg_x(rd), temp, reg_x(rd), cond);
   }
 }
 
@@ -195,7 +197,7 @@
 // Load routines.
 void Arm64Assembler::LoadImmediate(Register dest, int32_t value,
                                    Condition cond) {
-  if ((cond == AL) || (cond == NV)) {
+  if ((cond == al) || (cond == nv)) {
     ___ Mov(reg_x(dest), value);
   } else {
     // temp = value
@@ -205,9 +207,9 @@
       temps.Exclude(reg_x(dest));
       vixl::Register temp = temps.AcquireX();
       ___ Mov(temp, value);
-      ___ Csel(reg_x(dest), temp, reg_x(dest), COND_OP(cond));
+      ___ Csel(reg_x(dest), temp, reg_x(dest), cond);
     } else {
-      ___ Csel(reg_x(dest), reg_x(XZR), reg_x(dest), COND_OP(cond));
+      ___ Csel(reg_x(dest), reg_x(XZR), reg_x(dest), cond);
     }
   }
 }
@@ -297,6 +299,10 @@
   CHECK(dst.IsCoreRegister() && base.IsCoreRegister());
   LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), base.AsCoreRegister(),
                   offs.Int32Value());
+  if (kPoisonHeapReferences) {
+    WRegister ref_reg = dst.AsOverlappingCoreRegisterLow();
+    ___ Neg(reg_w(ref_reg), vixl::Operand(reg_w(ref_reg)));
+  }
 }
 
 void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) {
@@ -557,11 +563,11 @@
     }
     ___ Cmp(reg_w(in_reg.AsOverlappingCoreRegisterLow()), 0);
     if (!out_reg.Equals(in_reg)) {
-      LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+      LoadImmediate(out_reg.AsCoreRegister(), 0, eq);
     }
-    AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), NE);
+    AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), ne);
   } else {
-    AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), AL);
+    AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), al);
   }
 }
 
@@ -577,9 +583,9 @@
     // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
     ___ Cmp(reg_w(scratch.AsOverlappingCoreRegisterLow()), 0);
     // Move this logic in add constants with flags.
-    AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
+    AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), ne);
   } else {
-    AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
+    AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), al);
   }
   StoreToOffset(scratch.AsCoreRegister(), SP, out_off.Int32Value());
 }
@@ -593,7 +599,7 @@
   vixl::Label exit;
   if (!out_reg.Equals(in_reg)) {
     // FIXME: Who sets the flags here?
-    LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+    LoadImmediate(out_reg.AsCoreRegister(), 0, eq);
   }
   ___ Cbz(reg_x(in_reg.AsCoreRegister()), &exit);
   LoadFromOffset(out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0);
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index ab4999a..bf89d24 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -33,29 +33,7 @@
 namespace art {
 namespace arm64 {
 
-#define MEM_OP(x...)      vixl::MemOperand(x)
-#define COND_OP(x)        static_cast<vixl::Condition>(x)
-
-enum Condition {
-  kNoCondition = -1,
-  EQ = 0,
-  NE = 1,
-  HS = 2,
-  LO = 3,
-  MI = 4,
-  PL = 5,
-  VS = 6,
-  VC = 7,
-  HI = 8,
-  LS = 9,
-  GE = 10,
-  LT = 11,
-  GT = 12,
-  LE = 13,
-  AL = 14,    // Always.
-  NV = 15,    // Behaves as always/al.
-  kMaxCondition = 16,
-};
+#define MEM_OP(...)      vixl::MemOperand(__VA_ARGS__)
 
 enum LoadOperandType {
   kLoadSignedByte,
@@ -225,15 +203,15 @@
   void StoreSToOffset(SRegister source, Register base, int32_t offset);
   void StoreDToOffset(DRegister source, Register base, int32_t offset);
 
-  void LoadImmediate(Register dest, int32_t value, Condition cond = AL);
+  void LoadImmediate(Register dest, int32_t value, vixl::Condition cond = vixl::al);
   void Load(Arm64ManagedRegister dst, Register src, int32_t src_offset, size_t size);
   void LoadWFromOffset(LoadOperandType type, WRegister dest,
                       Register base, int32_t offset);
   void LoadFromOffset(Register dest, Register base, int32_t offset);
   void LoadSFromOffset(SRegister dest, Register base, int32_t offset);
   void LoadDFromOffset(DRegister dest, Register base, int32_t offset);
-  void AddConstant(Register rd, int32_t value, Condition cond = AL);
-  void AddConstant(Register rd, Register rn, int32_t value, Condition cond = AL);
+  void AddConstant(Register rd, int32_t value, vixl::Condition cond = vixl::al);
+  void AddConstant(Register rd, Register rn, int32_t value, vixl::Condition cond = vixl::al);
 
   // Vixl buffer.
   byte* vixl_buf_;
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index 2d70b7d..e6b4a6a 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -82,12 +82,13 @@
       : array_(array), size_(size) {
   }
 
-  explicit ArrayRef(std::vector<T>& v)
+  template <typename Alloc>
+  explicit ArrayRef(std::vector<T, Alloc>& v)
       : array_(v.data()), size_(v.size()) {
   }
 
-  template <typename U>
-  ArrayRef(const std::vector<U>& v,
+  template <typename U, typename Alloc>
+  ArrayRef(const std::vector<U, Alloc>& v,
            typename std::enable_if<std::is_same<T, const U>::value, tag>::tag t = tag())
       : array_(v.data()), size_(v.size()) {
   }
@@ -167,6 +168,16 @@
   size_t size_;
 };
 
+template <typename T>
+bool operator==(const ArrayRef<T>& lhs, const ArrayRef<T>& rhs) {
+  return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+}
+
+template <typename T>
+bool operator!=(const ArrayRef<T>& lhs, const ArrayRef<T>& rhs) {
+  return !(lhs == rhs);
+}
+
 }  // namespace art
 
 
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 68b784a..e3045e1 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -92,6 +92,7 @@
 
   // Compute the relocation delta and switch to the new contents area.
   ptrdiff_t delta = new_contents - contents_;
+  delete[] contents_;
   contents_ = new_contents;
 
   // Update the cursor and recompute the limit.
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index f72f5e5..4addfa0 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -499,6 +499,10 @@
   // and branch to a ExceptionSlowPath if it is.
   virtual void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) = 0;
 
+  virtual void InitializeFrameDescriptionEntry() {}
+  virtual void FinalizeFrameDescriptionEntry() {}
+  virtual std::vector<uint8_t>* GetFrameDescriptionEntry() { return nullptr; }
+
   virtual ~Assembler() {}
 
  protected:
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 891a287..e3a9580 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -116,6 +116,19 @@
     std::string subdir = toolsdir + std::string("/") + std::string(entry->d_name);
     size_t eabi = subdir.find(TOOL_PREFIX);
     if (eabi != std::string::npos) {
+      // Check if "bin/{as,objcopy,objdump}" exist under this folder.
+      struct stat exec_st;
+      std::string exec_path;
+      exec_path = subdir + "/bin/" + TOOL_PREFIX + "as";
+      if (stat(exec_path.c_str(), &exec_st) != 0)
+        continue;
+      exec_path = subdir + "/bin/" + TOOL_PREFIX + "objcopy";
+      if (stat(exec_path.c_str(), &exec_st) != 0)
+        continue;
+      exec_path = subdir + "/bin/" + TOOL_PREFIX + "objdump";
+      if (stat(exec_path.c_str(), &exec_st) != 0)
+        continue;
+
       std::string suffix = subdir.substr(eabi + strlen(TOOL_PREFIX));
       double version = strtod(suffix.c_str(), nullptr);
       if (version > maxversion) {
diff --git a/compiler/utils/dwarf_cfi.cc b/compiler/utils/dwarf_cfi.cc
new file mode 100644
index 0000000..83e5f5a
--- /dev/null
+++ b/compiler/utils/dwarf_cfi.cc
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "leb128.h"
+#include "utils.h"
+
+#include "dwarf_cfi.h"
+
+namespace art {
+
+void DW_CFA_advance_loc(std::vector<uint8_t>* buf, uint32_t increment) {
+  if (increment < 64) {
+    // Encoding in opcode.
+    buf->push_back(0x1 << 6 | increment);
+  } else if (increment < 256) {
+    // Single byte delta.
+    buf->push_back(0x02);
+    buf->push_back(increment);
+  } else if (increment < 256 * 256) {
+    // Two byte delta.
+    buf->push_back(0x03);
+    buf->push_back(increment & 0xff);
+    buf->push_back((increment >> 8) & 0xff);
+  } else {
+    // Four byte delta.
+    buf->push_back(0x04);
+    PushWord(buf, increment);
+  }
+}
+
+void DW_CFA_offset_extended_sf(std::vector<uint8_t>* buf, int reg, int32_t offset) {
+  buf->push_back(0x11);
+  EncodeUnsignedLeb128(reg, buf);
+  EncodeSignedLeb128(offset, buf);
+}
+
+void DW_CFA_offset(std::vector<uint8_t>* buf, int reg, uint32_t offset) {
+  buf->push_back((0x2 << 6) | reg);
+  EncodeUnsignedLeb128(offset, buf);
+}
+
+void DW_CFA_def_cfa_offset(std::vector<uint8_t>* buf, int32_t offset) {
+  buf->push_back(0x0e);
+  EncodeUnsignedLeb128(offset, buf);
+}
+
+void DW_CFA_remember_state(std::vector<uint8_t>* buf) {
+  buf->push_back(0x0a);
+}
+
+void DW_CFA_restore_state(std::vector<uint8_t>* buf) {
+  buf->push_back(0x0b);
+}
+
+void WriteFDEHeader(std::vector<uint8_t>* buf, bool is_64bit) {
+  // 'length' (filled in by other functions).
+  if (is_64bit) {
+    PushWord(buf, 0xffffffff);  // Indicates 64bit
+    PushWord(buf, 0);
+    PushWord(buf, 0);
+  } else {
+    PushWord(buf, 0);
+  }
+
+  // 'CIE_pointer' (filled in by linker).
+  if (is_64bit) {
+    PushWord(buf, 0);
+    PushWord(buf, 0);
+  } else {
+    PushWord(buf, 0);
+  }
+
+  // 'initial_location' (filled in by linker).
+  if (is_64bit) {
+    PushWord(buf, 0);
+    PushWord(buf, 0);
+  } else {
+    PushWord(buf, 0);
+  }
+
+  // 'address_range' (filled in by other functions).
+  if (is_64bit) {
+    PushWord(buf, 0);
+    PushWord(buf, 0);
+  } else {
+    PushWord(buf, 0);
+  }
+
+  // Augmentation length: 0
+  buf->push_back(0);
+}
+
+void WriteFDEAddressRange(std::vector<uint8_t>* buf, uint64_t data, bool is_64bit) {
+  const size_t kOffsetOfAddressRange = is_64bit? 28 : 12;
+  CHECK(buf->size() >= kOffsetOfAddressRange + (is_64bit? 8 : 4));
+
+  uint8_t *p = buf->data() + kOffsetOfAddressRange;
+  if (is_64bit) {
+    p[0] = data;
+    p[1] = data >> 8;
+    p[2] = data >> 16;
+    p[3] = data >> 24;
+    p[4] = data >> 32;
+    p[5] = data >> 40;
+    p[6] = data >> 48;
+    p[7] = data >> 56;
+  } else {
+    p[0] = data;
+    p[1] = data >> 8;
+    p[2] = data >> 16;
+    p[3] = data >> 24;
+  }
+}
+
+void WriteCFILength(std::vector<uint8_t>* buf, bool is_64bit) {
+  uint64_t length = is_64bit ? buf->size() - 12 : buf->size() - 4;
+  DCHECK_EQ((length & 0x3), 0U);
+
+  uint8_t *p = is_64bit? buf->data() + 4 : buf->data();
+  if (is_64bit) {
+    p[0] = length;
+    p[1] = length >> 8;
+    p[2] = length >> 16;
+    p[3] = length >> 24;
+    p[4] = length >> 32;
+    p[5] = length >> 40;
+    p[6] = length >> 48;
+    p[7] = length >> 56;
+  } else {
+    p[0] = length;
+    p[1] = length >> 8;
+    p[2] = length >> 16;
+    p[3] = length >> 24;
+  }
+}
+
+void PadCFI(std::vector<uint8_t>* buf) {
+  while (buf->size() & 0x3) {
+    buf->push_back(0);
+  }
+}
+
+}  // namespace art
diff --git a/compiler/utils/dwarf_cfi.h b/compiler/utils/dwarf_cfi.h
new file mode 100644
index 0000000..0c8b151
--- /dev/null
+++ b/compiler/utils/dwarf_cfi.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_DWARF_CFI_H_
+#define ART_COMPILER_UTILS_DWARF_CFI_H_
+
+#include <vector>
+
+namespace art {
+
+/**
+ * @brief Enter a 'DW_CFA_advance_loc' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param increment Amount by which to increase the current location.
+ */
+void DW_CFA_advance_loc(std::vector<uint8_t>* buf, uint32_t increment);
+
+/**
+ * @brief Enter a 'DW_CFA_offset_extended_sf' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param reg Register number.
+ * @param offset Offset of register address from CFA.
+ */
+void DW_CFA_offset_extended_sf(std::vector<uint8_t>* buf, int reg, int32_t offset);
+
+/**
+ * @brief Enter a 'DW_CFA_offset' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param reg Register number.
+ * @param offset Offset of register address from CFA.
+ */
+void DW_CFA_offset(std::vector<uint8_t>* buf, int reg, uint32_t offset);
+
+/**
+ * @brief Enter a 'DW_CFA_def_cfa_offset' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param offset New offset of CFA.
+ */
+void DW_CFA_def_cfa_offset(std::vector<uint8_t>* buf, int32_t offset);
+
+/**
+ * @brief Enter a 'DW_CFA_remember_state' into an FDE buffer
+ * @param buf FDE buffer.
+ */
+void DW_CFA_remember_state(std::vector<uint8_t>* buf);
+
+/**
+ * @brief Enter a 'DW_CFA_restore_state' into an FDE buffer
+ * @param buf FDE buffer.
+ */
+void DW_CFA_restore_state(std::vector<uint8_t>* buf);
+
+/**
+ * @brief Write FDE header into an FDE buffer
+ * @param buf FDE buffer.
+ * @param is_64bit If FDE is for 64bit application.
+ */
+void WriteFDEHeader(std::vector<uint8_t>* buf, bool is_64bit);
+
+/**
+ * @brief Set 'address_range' field of an FDE buffer
+ * @param buf FDE buffer.
+ * @param data Data value.
+ * @param is_64bit If FDE is for 64bit application.
+ */
+void WriteFDEAddressRange(std::vector<uint8_t>* buf, uint64_t data, bool is_64bit);
+
+/**
+ * @brief Set 'length' field of an FDE buffer
+ * @param buf FDE buffer.
+ * @param is_64bit If FDE is for 64bit application.
+ */
+void WriteCFILength(std::vector<uint8_t>* buf, bool is_64bit);
+
+/**
+ * @brief Pad an FDE buffer with 0 until its size is a multiple of 4
+ * @param buf FDE buffer.
+ */
+void PadCFI(std::vector<uint8_t>* buf);
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_DWARF_CFI_H_
diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h
index a1a3312..61e420c 100644
--- a/compiler/utils/growable_array.h
+++ b/compiler/utils/growable_array.h
@@ -26,61 +26,14 @@
 // Type of growable list for memory tuning.
 enum OatListKind {
   kGrowableArrayMisc = 0,
-  kGrowableArrayBlockList,
-  kGrowableArraySSAtoDalvikMap,
-  kGrowableArrayDfsOrder,
-  kGrowableArrayDfsPostOrder,
-  kGrowableArrayDomPostOrderTraversal,
-  kGrowableArraySwitchTables,
-  kGrowableArrayFillArrayData,
-  kGrowableArraySuccessorBlocks,
-  kGrowableArrayPredecessors,
-  kGrowableArraySlowPaths,
   kGNumListKinds
 };
 
+// Deprecated
+// TODO: Replace all uses with ArenaVector<T>.
 template<typename T>
 class GrowableArray {
   public:
-    class Iterator {
-      public:
-        explicit Iterator(GrowableArray* g_list)
-          : idx_(0),
-            g_list_(g_list) {}
-
-        explicit Iterator()
-          : idx_(0),
-            g_list_(nullptr) {}
-
-        // NOTE: returns 0/NULL when no next.
-        // TODO: redo to make usage consistent with other iterators.
-        T Next() {
-          DCHECK(g_list_ != nullptr);
-          if (idx_ >= g_list_->Size()) {
-            return 0;
-          } else {
-            return g_list_->Get(idx_++);
-          }
-        }
-
-        void Reset() {
-          idx_ = 0;
-        }
-
-        void Reset(GrowableArray* g_list) {
-          idx_ = 0;
-          g_list_ = g_list;
-        }
-
-        size_t GetIndex() const {
-          return idx_;
-        }
-
-      private:
-        size_t idx_;
-        GrowableArray* g_list_;
-    };
-
     GrowableArray(ArenaAllocator* arena, size_t init_length, OatListKind kind = kGrowableArrayMisc)
       : arena_(arena),
         num_allocated_(init_length),
@@ -88,7 +41,7 @@
         kind_(kind) {
       elem_list_ = static_cast<T*>(arena_->Alloc(sizeof(T) * init_length,
                                                  kArenaAllocGrowableArray));
-    };
+    }
 
 
     // Expand the list size to at least new length.
@@ -105,7 +58,7 @@
       memcpy(new_array, elem_list_, sizeof(T) * num_allocated_);
       num_allocated_ = target_length;
       elem_list_ = new_array;
-    };
+    }
 
     // NOTE: does not return storage, just resets use count.
     void Reset() {
@@ -136,7 +89,7 @@
     T Get(size_t index) const {
       DCHECK_LT(index, num_used_);
       return elem_list_[index];
-    };
+    }
 
     // Overwrite existing element at position index.  List must be large enough.
     void Put(size_t index, T elem) {
@@ -167,14 +120,14 @@
       // We should either have found the element, or it was the last (unscanned) element.
       DCHECK(found || (element == elem_list_[num_used_ - 1]));
       num_used_--;
-    };
+    }
 
     void DeleteAt(size_t index) {
       for (size_t i = index; i < num_used_ - 1; i++) {
         elem_list_[i] = elem_list_[i + 1];
       }
       num_used_--;
-    };
+    }
 
     size_t GetNumAllocated() const { return num_allocated_; }
 
@@ -201,7 +154,7 @@
 
     static void* operator new(size_t size, ArenaAllocator* arena) {
       return arena->Alloc(sizeof(GrowableArray<T>), kArenaAllocGrowableArray);
-    };
+    }
     static void operator delete(void* p) {}  // Nop.
 
   private:
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index b6a5c20..f888d46 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -20,6 +20,7 @@
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "memory_region.h"
 #include "thread.h"
+#include "utils/dwarf_cfi.h"
 
 namespace art {
 namespace x86 {
@@ -745,6 +746,7 @@
   EmitRegisterOperand(dst, src);
 }
 
+
 void X86Assembler::xchgl(Register reg, const Address& address) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x87);
@@ -752,6 +754,13 @@
 }
 
 
+void X86Assembler::cmpw(const Address& address, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitComplex(7, address, imm);
+}
+
+
 void X86Assembler::cmpl(Register reg, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitComplex(7, Operand(reg), imm);
@@ -1407,20 +1416,61 @@
   EmitOperand(reg_or_opcode, Operand(operand));
 }
 
+void X86Assembler::InitializeFrameDescriptionEntry() {
+  WriteFDEHeader(&cfi_info_, false /* is_64bit */);
+}
+
+void X86Assembler::FinalizeFrameDescriptionEntry() {
+  WriteFDEAddressRange(&cfi_info_, buffer_.Size(), false /* is_64bit */);
+  PadCFI(&cfi_info_);
+  WriteCFILength(&cfi_info_, false /* is_64bit */);
+}
+
 constexpr size_t kFramePointerSize = 4;
 
 void X86Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
                               const std::vector<ManagedRegister>& spill_regs,
                               const ManagedRegisterEntrySpills& entry_spills) {
+  cfi_cfa_offset_ = kFramePointerSize;  // Only return address on stack
+  cfi_pc_ = buffer_.Size();  // Nothing emitted yet
+  DCHECK_EQ(cfi_pc_, 0U);
+
+  uint32_t reg_offset = 1;
   CHECK_ALIGNED(frame_size, kStackAlignment);
   for (int i = spill_regs.size() - 1; i >= 0; --i) {
     pushl(spill_regs.at(i).AsX86().AsCpuRegister());
+
+    // DW_CFA_advance_loc
+    DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+    cfi_pc_ = buffer_.Size();
+    // DW_CFA_def_cfa_offset
+    cfi_cfa_offset_ += kFramePointerSize;
+    DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+    // DW_CFA_offset reg offset
+    reg_offset++;
+    DW_CFA_offset(&cfi_info_, spill_regs.at(i).AsX86().DWARFRegId(), reg_offset);
   }
+
   // return address then method on stack
-  addl(ESP, Immediate(-frame_size + (spill_regs.size() * kFramePointerSize) +
-                      sizeof(StackReference<mirror::ArtMethod>) /*method*/ +
-                      kFramePointerSize /*return address*/));
+  int32_t adjust = frame_size - (spill_regs.size() * kFramePointerSize) -
+                   sizeof(StackReference<mirror::ArtMethod>) /*method*/ -
+                   kFramePointerSize /*return address*/;
+  addl(ESP, Immediate(-adjust));
+  // DW_CFA_advance_loc
+  DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+  cfi_pc_ = buffer_.Size();
+  // DW_CFA_def_cfa_offset
+  cfi_cfa_offset_ += adjust;
+  DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+
   pushl(method_reg.AsX86().AsCpuRegister());
+  // DW_CFA_advance_loc
+  DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+  cfi_pc_ = buffer_.Size();
+  // DW_CFA_def_cfa_offset
+  cfi_cfa_offset_ += kFramePointerSize;
+  DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+
   for (size_t i = 0; i < entry_spills.size(); ++i) {
     movl(Address(ESP, frame_size + sizeof(StackReference<mirror::ArtMethod>) +
                  (i * kFramePointerSize)),
@@ -1442,6 +1492,12 @@
 void X86Assembler::IncreaseFrameSize(size_t adjust) {
   CHECK_ALIGNED(adjust, kStackAlignment);
   addl(ESP, Immediate(-adjust));
+  // DW_CFA_advance_loc
+  DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+  cfi_pc_ = buffer_.Size();
+  // DW_CFA_def_cfa_offset
+  cfi_cfa_offset_ += adjust;
+  DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
 }
 
 void X86Assembler::DecreaseFrameSize(size_t adjust) {
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index ce20768..ec983d9 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -337,6 +337,8 @@
   void xchgl(Register dst, Register src);
   void xchgl(Register reg, const Address& address);
 
+  void cmpw(const Address& address, const Immediate& imm);
+
   void cmpl(Register reg, const Immediate& imm);
   void cmpl(Register reg0, Register reg1);
   void cmpl(Register reg, const Address& address);
@@ -571,6 +573,12 @@
   // and branch to a ExceptionSlowPath if it is.
   void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) OVERRIDE;
 
+  void InitializeFrameDescriptionEntry() OVERRIDE;
+  void FinalizeFrameDescriptionEntry() OVERRIDE;
+  std::vector<uint8_t>* GetFrameDescriptionEntry() OVERRIDE {
+    return &cfi_info_;
+  }
+
  private:
   inline void EmitUint8(uint8_t value);
   inline void EmitInt32(int32_t value);
@@ -589,6 +597,9 @@
   void EmitGenericShift(int rm, Register reg, const Immediate& imm);
   void EmitGenericShift(int rm, Register operand, Register shifter);
 
+  std::vector<uint8_t> cfi_info_;
+  uint32_t cfi_cfa_offset_, cfi_pc_;
+
   DISALLOW_COPY_AND_ASSIGN(X86Assembler);
 };
 
diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h
index 09d2b49..5d46ee2 100644
--- a/compiler/utils/x86/managed_register_x86.h
+++ b/compiler/utils/x86/managed_register_x86.h
@@ -88,6 +88,14 @@
 // There is a one-to-one mapping between ManagedRegister and register id.
 class X86ManagedRegister : public ManagedRegister {
  public:
+  int DWARFRegId() const {
+    CHECK(IsCpuRegister());
+    // For all the X86 registers we care about:
+    // EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
+    // DWARF register id is the same as id_.
+    return static_cast<int>(id_);
+  }
+
   ByteRegister AsByteRegister() const {
     CHECK(IsCpuRegister());
     CHECK_LT(AsCpuRegister(), ESP);  // ESP, EBP, ESI and EDI cannot be encoded as byte registers.
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 7684271..1dcd4b3 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -20,6 +20,7 @@
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "memory_region.h"
 #include "thread.h"
+#include "utils/dwarf_cfi.h"
 
 namespace art {
 namespace x86_64 {
@@ -838,6 +839,14 @@
 }
 
 
+void X86_64Assembler::cmpw(const Address& address, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(address);
+  EmitUint8(0x66);
+  EmitComplex(7, address, imm);
+}
+
+
 void X86_64Assembler::cmpl(CpuRegister reg, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitOptionalRex32(reg);
@@ -1714,11 +1723,26 @@
   }
 }
 
+void X86_64Assembler::InitializeFrameDescriptionEntry() {
+  WriteFDEHeader(&cfi_info_, true /* is_64bit */);
+}
+
+void X86_64Assembler::FinalizeFrameDescriptionEntry() {
+  WriteFDEAddressRange(&cfi_info_, buffer_.Size(), true /* is_64bit */);
+  PadCFI(&cfi_info_);
+  WriteCFILength(&cfi_info_, true /* is_64bit */);
+}
+
 constexpr size_t kFramePointerSize = 8;
 
 void X86_64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
                                  const std::vector<ManagedRegister>& spill_regs,
                                  const ManagedRegisterEntrySpills& entry_spills) {
+  cfi_cfa_offset_ = kFramePointerSize;  // Only return address on stack
+  cfi_pc_ = buffer_.Size();  // Nothing emitted yet
+  DCHECK_EQ(cfi_pc_, 0U);
+
+  uint32_t reg_offset = 1;
   CHECK_ALIGNED(frame_size, kStackAlignment);
   int gpr_count = 0;
   for (int i = spill_regs.size() - 1; i >= 0; --i) {
@@ -1726,6 +1750,16 @@
     if (spill.IsCpuRegister()) {
       pushq(spill.AsCpuRegister());
       gpr_count++;
+
+      // DW_CFA_advance_loc
+      DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+      cfi_pc_ = buffer_.Size();
+      // DW_CFA_def_cfa_offset
+      cfi_cfa_offset_ += kFramePointerSize;
+      DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+      // DW_CFA_offset reg offset
+      reg_offset++;
+      DW_CFA_offset(&cfi_info_, spill.DWARFRegId(), reg_offset);
     }
   }
   // return address then method on stack
@@ -1733,6 +1767,13 @@
                           - (gpr_count * kFramePointerSize)
                           - kFramePointerSize /*return address*/;
   subq(CpuRegister(RSP), Immediate(rest_of_frame));
+  // DW_CFA_advance_loc
+  DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+  cfi_pc_ = buffer_.Size();
+  // DW_CFA_def_cfa_offset
+  cfi_cfa_offset_ += rest_of_frame;
+  DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+
   // spill xmms
   int64_t offset = rest_of_frame;
   for (int i = spill_regs.size() - 1; i >= 0; --i) {
@@ -1796,6 +1837,12 @@
 void X86_64Assembler::IncreaseFrameSize(size_t adjust) {
   CHECK_ALIGNED(adjust, kStackAlignment);
   addq(CpuRegister(RSP), Immediate(-static_cast<int64_t>(adjust)));
+  // DW_CFA_advance_loc
+  DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+  cfi_pc_ = buffer_.Size();
+  // DW_CFA_def_cfa_offset
+  cfi_cfa_offset_ += adjust;
+  DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
 }
 
 void X86_64Assembler::DecreaseFrameSize(size_t adjust) {
@@ -1945,6 +1992,9 @@
   X86_64ManagedRegister dest = mdest.AsX86_64();
   CHECK(dest.IsCpuRegister() && dest.IsCpuRegister());
   movq(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+  if (kPoisonHeapReferences) {
+    negl(dest.AsCpuRegister());
+  }
 }
 
 void X86_64Assembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
@@ -2229,4 +2279,3 @@
 
 }  // namespace x86_64
 }  // namespace art
-
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 9a6df1c..1fd65c2 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -253,7 +253,7 @@
 
 class X86_64Assembler FINAL : public Assembler {
  public:
-  X86_64Assembler() {}
+  X86_64Assembler() : cfi_cfa_offset_(0), cfi_pc_(0) {}
   virtual ~X86_64Assembler() {}
 
   /*
@@ -378,6 +378,8 @@
   void xchgq(CpuRegister dst, CpuRegister src);
   void xchgl(CpuRegister reg, const Address& address);
 
+  void cmpw(const Address& address, const Immediate& imm);
+
   void cmpl(CpuRegister reg, const Immediate& imm);
   void cmpl(CpuRegister reg0, CpuRegister reg1);
   void cmpl(CpuRegister reg, const Address& address);
@@ -614,6 +616,12 @@
   // and branch to a ExceptionSlowPath if it is.
   void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) OVERRIDE;
 
+  void InitializeFrameDescriptionEntry() OVERRIDE;
+  void FinalizeFrameDescriptionEntry() OVERRIDE;
+  std::vector<uint8_t>* GetFrameDescriptionEntry() OVERRIDE {
+    return &cfi_info_;
+  }
+
  private:
   void EmitUint8(uint8_t value);
   void EmitInt32(int32_t value);
@@ -655,6 +663,9 @@
   void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, CpuRegister src);
   void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, const Operand& operand);
 
+  std::vector<uint8_t> cfi_info_;
+  uint32_t cfi_cfa_offset_, cfi_pc_;
+
   DISALLOW_COPY_AND_ASSIGN(X86_64Assembler);
 };
 
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 4ed7b20..7a48b63 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -16,6 +16,7 @@
 
 #include "assembler_x86_64.h"
 
+#include "base/stl_util.h"
 #include "utils/assembler_test.h"
 
 namespace art {
@@ -62,6 +63,11 @@
     }
   }
 
+  void TearDown() OVERRIDE {
+    AssemblerTest::TearDown();
+    STLDeleteElements(&registers_);
+  }
+
   std::vector<x86_64::CpuRegister*> GetRegisters() OVERRIDE {
     return registers_;
   }
@@ -219,6 +225,7 @@
     }
   }
 
+  STLDeleteElements(&registers);
   return str.str();
 }
 
diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h
index 822659f..3a96ad0 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.h
+++ b/compiler/utils/x86_64/managed_register_x86_64.h
@@ -87,6 +87,21 @@
 // There is a one-to-one mapping between ManagedRegister and register id.
 class X86_64ManagedRegister : public ManagedRegister {
  public:
+  int DWARFRegId() const {
+    CHECK(IsCpuRegister());
+    switch (id_) {
+      case RAX: return  0;
+      case RDX: return  1;
+      case RCX: return  2;
+      case RBX: return  3;
+      case RSI: return  4;
+      case RDI: return  5;
+      case RBP: return  6;
+      case RSP: return  7;
+      default: return static_cast<int>(id_);  // R8 ~ R15
+    }
+  }
+
   CpuRegister AsCpuRegister() const {
     CHECK(IsCpuRegister());
     return CpuRegister(static_cast<Register>(id_));
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 28db711..2ef826b 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -37,9 +37,9 @@
 endif
 
 # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler,art/compiler,host,ndebug))
 endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler,art/compiler,host,debug))
 endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 05a940c..09825e2 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -45,7 +45,6 @@
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "elf_fixup.h"
-#include "elf_patcher.h"
 #include "elf_stripper.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space-inl.h"
@@ -160,7 +159,14 @@
   UsageError("      Example: --compiler-backend=Portable");
   UsageError("      Default: Quick");
   UsageError("");
-  UsageError("  --compiler-filter=(verify-none|interpret-only|space|balanced|speed|everything):");
+  UsageError("  --compiler-filter="
+                "(verify-none"
+                "|interpret-only"
+                "|space"
+                "|balanced"
+                "|speed"
+                "|everything"
+                "|time):");
   UsageError("      select compiler filter.");
   UsageError("      Example: --compiler-filter=everything");
 #if ART_SMALL_MODE
@@ -226,6 +232,15 @@
   UsageError("  --disable-passes=<pass-names>:  disable one or more passes separated by comma.");
   UsageError("      Example: --disable-passes=UseCount,BBOptimizations");
   UsageError("");
+  UsageError("  --print-pass-options: print a list of passes that have configurable options along "
+             "with the setting.");
+  UsageError("      Will print default if no overridden setting exists.");
+  UsageError("");
+  UsageError("  --pass-options=Pass1Name:Pass1OptionName:Pass1Option#,"
+             "Pass2Name:Pass2OptionName:Pass2Option#");
+  UsageError("      Used to specify a pass specific option. The setting itself must be integer.");
+  UsageError("      Separator used between options is a comma.");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -245,12 +260,12 @@
     CHECK(verification_results != nullptr);
     CHECK(method_inliner_map != nullptr);
     std::unique_ptr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
-                                           compiler_kind,
-                                           instruction_set,
-                                           instruction_set_features,
-                                           verification_results,
-                                           method_inliner_map,
-                                           thread_count));
+                                                 compiler_kind,
+                                                 instruction_set,
+                                                 instruction_set_features,
+                                                 verification_results,
+                                                 method_inliner_map,
+                                                 thread_count));
     if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
       *p_dex2oat = nullptr;
       return false;
@@ -325,39 +340,16 @@
     return ReadImageClasses(image_classes_stream);
   }
 
-  bool PatchOatCode(const CompilerDriver* compiler_driver, File* oat_file,
-                    const std::string& oat_location, std::string* error_msg) {
-    // We asked to include patch information but we are not making an image. We need to fix
-    // everything up manually.
-    std::unique_ptr<ElfFile> elf_file(ElfFile::Open(oat_file, PROT_READ|PROT_WRITE,
-                                                    MAP_SHARED, error_msg));
-    if (elf_file.get() == NULL) {
-      LOG(ERROR) << error_msg;
-      return false;
-    }
-    {
-      ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
-      return ElfPatcher::Patch(compiler_driver, elf_file.get(), oat_location, error_msg);
-    }
-  }
-
-  const CompilerDriver* CreateOatFile(const std::string& boot_image_option,
-                                      const std::string& android_root,
-                                      bool is_host,
-                                      const std::vector<const DexFile*>& dex_files,
-                                      File* oat_file,
-                                      const std::string& oat_location,
-                                      const std::string& bitcode_filename,
-                                      bool image,
-                                      std::unique_ptr<std::set<std::string>>& image_classes,
-                                      bool dump_stats,
-                                      bool dump_passes,
-                                      TimingLogger& timings,
-                                      CumulativeLogger& compiler_phases_timings,
-                                      std::string profile_file,
-                                      SafeMap<std::string, std::string>* key_value_store) {
-    CHECK(key_value_store != nullptr);
-
+  void Compile(const std::string& boot_image_option,
+               const std::vector<const DexFile*>& dex_files,
+               const std::string& bitcode_filename,
+               bool image,
+               std::unique_ptr<std::set<std::string>>& image_classes,
+               bool dump_stats,
+               bool dump_passes,
+               TimingLogger* timings,
+               CumulativeLogger* compiler_phases_timings,
+               const std::string& profile_file) {
     // Handle and ClassLoader creation needs to come after Runtime::Create
     jobject class_loader = nullptr;
     Thread* self = Thread::Current();
@@ -376,31 +368,45 @@
       Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
     }
 
-    std::unique_ptr<CompilerDriver> driver(new CompilerDriver(compiler_options_,
-                                                              verification_results_,
-                                                              method_inliner_map_,
-                                                              compiler_kind_,
-                                                              instruction_set_,
-                                                              instruction_set_features_,
-                                                              image,
-                                                              image_classes.release(),
-                                                              thread_count_,
-                                                              dump_stats,
-                                                              dump_passes,
-                                                              &compiler_phases_timings,
-                                                              profile_file));
+    driver_.reset(new CompilerDriver(compiler_options_,
+                                     verification_results_,
+                                     method_inliner_map_,
+                                     compiler_kind_,
+                                     instruction_set_,
+                                     instruction_set_features_,
+                                     image,
+                                     image_classes.release(),
+                                     thread_count_,
+                                     dump_stats,
+                                     dump_passes,
+                                     compiler_phases_timings,
+                                     profile_file));
 
-    driver->GetCompiler()->SetBitcodeFileName(*driver.get(), bitcode_filename);
+    driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename);
 
-    driver->CompileAll(class_loader, dex_files, &timings);
+    driver_->CompileAll(class_loader, dex_files, timings);
+  }
 
-    TimingLogger::ScopedTiming t2("dex2oat OatWriter", &timings);
+  void PrepareImageWriter(uintptr_t image_base) {
+    image_writer_.reset(new ImageWriter(*driver_, image_base));
+  }
+
+  bool CreateOatFile(const std::vector<const DexFile*>& dex_files,
+                     const std::string& android_root,
+                     bool is_host,
+                     File* oat_file,
+                     const std::string& oat_location,
+                     TimingLogger* timings,
+                     SafeMap<std::string, std::string>* key_value_store) {
+    CHECK(key_value_store != nullptr);
+
+    TimingLogger::ScopedTiming t2("dex2oat OatWriter", timings);
     std::string image_file_location;
     uint32_t image_file_location_oat_checksum = 0;
     uintptr_t image_file_location_oat_data_begin = 0;
     int32_t image_patch_delta = 0;
-    if (!driver->IsImage()) {
-      TimingLogger::ScopedTiming t3("Loading image checksum", &timings);
+    if (!driver_->IsImage()) {
+      TimingLogger::ScopedTiming t3("Loading image checksum", timings);
       gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
       image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
       image_file_location_oat_data_begin =
@@ -416,49 +422,50 @@
     OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
                          image_file_location_oat_data_begin,
                          image_patch_delta,
-                         driver.get(),
-                         &timings,
+                         driver_.get(),
+                         image_writer_.get(),
+                         timings,
                          key_value_store);
 
-    t2.NewTiming("Writing ELF");
-    if (!driver->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
-      LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
-      return nullptr;
-    }
-
-    // Flush result to disk. Patching code will re-open the file (mmap), so ensure that our view
-    // of the file already made it there and won't be re-ordered with writes from PatchOat or
-    // image patching.
-    oat_file->Flush();
-
-    if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) {
-      t2.NewTiming("Patching ELF");
-      std::string error_msg;
-      if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
-        LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath() << ": " << error_msg;
-        return nullptr;
+    if (driver_->IsImage()) {
+      // The OatWriter constructor has already updated offsets in methods and we need to
+      // prepare method offsets in the image address space for direct method patching.
+      t2.NewTiming("Preparing image address space");
+      if (!image_writer_->PrepareImageAddressSpace()) {
+        LOG(ERROR) << "Failed to prepare image address space.";
+        return false;
       }
     }
 
-    return driver.release();
+    t2.NewTiming("Writing ELF");
+    if (!driver_->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
+      LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
+      return false;
+    }
+
+    // Flush result to disk.
+    t2.NewTiming("Flushing ELF");
+    if (oat_file->Flush() != 0) {
+      LOG(ERROR) << "Failed to flush ELF file " << oat_file->GetPath();
+      return false;
+    }
+
+    return true;
   }
 
   bool CreateImageFile(const std::string& image_filename,
-                       uintptr_t image_base,
                        const std::string& oat_filename,
-                       const std::string& oat_location,
-                       const CompilerDriver& compiler)
+                       const std::string& oat_location)
       LOCKS_EXCLUDED(Locks::mutator_lock_) {
-    uintptr_t oat_data_begin;
-    {
-      // ImageWriter is scoped so it can free memory before doing FixupElf
-      ImageWriter image_writer(compiler);
-      if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
-        LOG(ERROR) << "Failed to create image file " << image_filename;
-        return false;
-      }
-      oat_data_begin = image_writer.GetOatDataBegin();
+    CHECK(image_writer_ != nullptr);
+    if (!image_writer_->Write(image_filename, oat_filename, oat_location)) {
+      LOG(ERROR) << "Failed to create image file " << image_filename;
+      return false;
     }
+    uintptr_t oat_data_begin = image_writer_->GetOatDataBegin();
+
+    // Destroy ImageWriter before doing FixupElf.
+    image_writer_.reset();
 
     std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
     if (oat_file.get() == nullptr) {
@@ -488,7 +495,9 @@
         method_inliner_map_(method_inliner_map),
         runtime_(nullptr),
         thread_count_(thread_count),
-        start_ns_(NanoTime()) {
+        start_ns_(NanoTime()),
+        driver_(nullptr),
+        image_writer_(nullptr) {
     CHECK(compiler_options != nullptr);
     CHECK(verification_results != nullptr);
     CHECK(method_inliner_map != nullptr);
@@ -555,6 +564,8 @@
   Runtime* runtime_;
   size_t thread_count_;
   uint64_t start_ns_;
+  std::unique_ptr<CompilerDriver> driver_;
+  std::unique_ptr<ImageWriter> image_writer_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
@@ -858,6 +869,7 @@
   bool dump_stats = false;
   bool dump_timing = false;
   bool dump_passes = false;
+  bool print_pass_options = false;
   bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
   bool include_debug_symbols = kIsDebugBuild;
   bool dump_slow_timing = kIsDebugBuild;
@@ -1045,6 +1057,11 @@
     } else if (option.starts_with("--dump-cfg-passes=")) {
       std::string dump_passes = option.substr(strlen("--dump-cfg-passes=")).data();
       PassDriverMEOpts::SetDumpPassList(dump_passes);
+    } else if (option == "--print-pass-options") {
+      print_pass_options = true;
+    } else if (option.starts_with("--pass-options=")) {
+      std::string options = option.substr(strlen("--pass-options=")).data();
+      PassDriverMEOpts::SetOverriddenPassOptions(options);
     } else if (option == "--include-patch-information") {
       include_patch_information = true;
     } else if (option == "--no-include-patch-information") {
@@ -1171,6 +1188,8 @@
     compiler_filter = CompilerOptions::kSpeed;
   } else if (strcmp(compiler_filter_string, "everything") == 0) {
     compiler_filter = CompilerOptions::kEverything;
+  } else if (strcmp(compiler_filter_string, "time") == 0) {
+    compiler_filter = CompilerOptions::kTime;
   } else {
     Usage("Unknown --compiler-filter value %s", compiler_filter_string);
   }
@@ -1191,6 +1210,10 @@
       break;
   }
 
+  if (print_pass_options) {
+    PassDriverMEOpts::PrintPassOptions();
+  }
+
   std::unique_ptr<CompilerOptions> compiler_options(new CompilerOptions(compiler_filter,
                                                                         huge_method_threshold,
                                                                         large_method_threshold,
@@ -1363,7 +1386,7 @@
    * If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
    * Don't bother to check if we're doing the image.
    */
-  if (!image && compiler_options->IsCompilationEnabled()) {
+  if (!image && compiler_options->IsCompilationEnabled() && compiler_kind == Compiler::kQuick) {
     size_t num_methods = 0;
     for (size_t i = 0; i != dex_files.size(); ++i) {
       const DexFile* dex_file = dex_files[i];
@@ -1393,22 +1416,28 @@
   oss << kRuntimeISA;
   key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
 
-  std::unique_ptr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
-                                                                        android_root,
-                                                                        is_host,
-                                                                        dex_files,
-                                                                        oat_file.get(),
-                                                                        oat_location,
-                                                                        bitcode_filename,
-                                                                        image,
-                                                                        image_classes,
-                                                                        dump_stats,
-                                                                        dump_passes,
-                                                                        timings,
-                                                                        compiler_phases_timings,
-                                                                        profile_file,
-                                                                        key_value_store.get()));
-  if (compiler.get() == nullptr) {
+  dex2oat->Compile(boot_image_option,
+                   dex_files,
+                   bitcode_filename,
+                   image,
+                   image_classes,
+                   dump_stats,
+                   dump_passes,
+                   &timings,
+                   &compiler_phases_timings,
+                   profile_file);
+
+  if (image) {
+    dex2oat->PrepareImageWriter(image_base);
+  }
+
+  if (!dex2oat->CreateOatFile(dex_files,
+                              android_root,
+                              is_host,
+                              oat_file.get(),
+                              oat_location,
+                              &timings,
+                              key_value_store.get())) {
     LOG(ERROR) << "Failed to create oat file: " << oat_location;
     return EXIT_FAILURE;
   }
@@ -1444,34 +1473,43 @@
   //
   // To get this all correct, we go through several steps.
   //
-  // 1. We have already created that oat file above with
-  // CreateOatFile. Originally this was just our own proprietary file
-  // but now it is contained within an ELF dynamic object (aka an .so
-  // file). The Compiler returned by CreateOatFile provides
-  // PatchInformation for references to oat code and Methods that need
-  // to be update once we know where the oat file will be located
-  // after the image.
+  // 1. We prepare offsets for all data in the oat file and calculate
+  // the oat data size and code size. During this stage, we also set
+  // oat code offsets in methods for use by the image writer.
   //
-  // 2. We create the image file. It needs to know where the oat file
+  // 2. We prepare offsets for the objects in the image and calculate
+  // the image size.
+  //
+  // 3. We create the oat file. Originally this was just our own proprietary
+  // file but now it is contained within an ELF dynamic object (aka an .so
+  // file). Since we know the image size and oat data size and code size we
+  // can prepare the ELF headers and we then know the ELF memory segment
+  // layout and we can now resolve all references. The compiler provides
+  // LinkerPatch information in each CompiledMethod and we resolve these,
+  // using the layout information and image object locations provided by
+  // image writer, as we're writing the method code.
+  //
+  // 4. We create the image file. It needs to know where the oat file
   // will be loaded after itself. Originally when oat file was simply
   // memory mapped so we could predict where its contents were based
   // on the file size. Now that it is an ELF file, we need to inspect
   // the ELF file to understand the in memory segment layout including
-  // where the oat header is located within. ElfPatcher's Patch method
-  // uses the PatchInformation from the Compiler to touch up absolute
-  // references in the oat file.
+  // where the oat header is located within.
+  // TODO: We could just remember this information from step 3.
   //
-  // 3. We fixup the ELF program headers so that dlopen will try to
+  // 5. We fixup the ELF program headers so that dlopen will try to
   // load the .so at the desired location at runtime by offsetting the
   // Elf32_Phdr.p_vaddr values by the desired base address.
+  // TODO: Do this in step 3. We already know the layout there.
+  //
+  // Steps 1.-3. are done by the CreateOatFile() above, steps 4.-5.
+  // are done by the CreateImageFile() below.
   //
   if (image) {
     TimingLogger::ScopedTiming t("dex2oat ImageWriter", &timings);
     bool image_creation_success = dex2oat->CreateImageFile(image_filename,
-                                                           image_base,
                                                            oat_unstripped,
-                                                           oat_location,
-                                                           *compiler.get());
+                                                           oat_location);
     if (!image_creation_success) {
       return EXIT_FAILURE;
     }
@@ -1484,7 +1522,7 @@
       LOG(INFO) << Dumpable<TimingLogger>(timings);
     }
     if (dump_passes) {
-      LOG(INFO) << Dumpable<CumulativeLogger>(*compiler.get()->GetTimingsLogger());
+      LOG(INFO) << Dumpable<CumulativeLogger>(compiler_phases_timings);
     }
     return EXIT_SUCCESS;
   }
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index a0abc9e..d67c169 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -99,9 +99,9 @@
   $(eval $(call build-libart-disassembler,target,debug))
 endif
 # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-libart-disassembler,host,ndebug))
 endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-libart-disassembler,host,debug))
 endif
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 54e7761..6f8e08b 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -82,14 +82,14 @@
 
 void DisassemblerArm::DumpMemoryDomain(std::ostream& os, uint32_t domain) {
   switch (domain) {
-    case 0b1111: os << "sy"; break;
-    case 0b1110: os << "st"; break;
-    case 0b1011: os << "ish"; break;
-    case 0b1010: os << "ishst"; break;
-    case 0b0111: os << "nsh"; break;
-    case 0b0110: os << "nshst"; break;
-    case 0b0011: os << "osh"; break;
-    case 0b0010: os << "oshst"; break;
+    case 15U /* 0b1111 */: os << "sy"; break;
+    case 14U /* 0b1110 */: os << "st"; break;
+    case 11U /* 0b1011 */: os << "ish"; break;
+    case 10U /* 0b1010 */: os << "ishst"; break;
+    case  7U /* 0b0111 */: os << "nsh"; break;
+    case  6U /* 0b0110 */: os << "nshst"; break;
+    case  3U /* 0b0011 */: os << "osh"; break;
+    case  2U /* 0b0010 */: os << "oshst"; break;
   }
 }
 
@@ -269,7 +269,7 @@
         uint32_t op = (instruction >> 21) & 0xf;
         opcode = kDataProcessingOperations[op];
         bool implicit_s = ((op & ~3) == 8);  // TST, TEQ, CMP, and CMN.
-        bool is_mov = op == 0b1101 || op == 0b1111;
+        bool is_mov = op == 13U /* 0b1101 */ || op == 15U /* 0b1111 */;
         if (is_mov) {
           // Show only Rd and Rm.
           if (s) {
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 4708498..195c45f 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -58,10 +58,10 @@
 };
 
 // 64-bit opcode REX modifier.
-constexpr uint8_t REX_W = 0b1000;
-constexpr uint8_t REX_R = 0b0100;
-constexpr uint8_t REX_X = 0b0010;
-constexpr uint8_t REX_B = 0b0001;
+constexpr uint8_t REX_W = 8U /* 0b1000 */;
+constexpr uint8_t REX_R = 4U /* 0b0100 */;
+constexpr uint8_t REX_X = 2U /* 0b0010 */;
+constexpr uint8_t REX_B = 1U /* 0b0001 */;
 
 static void DumpReg0(std::ostream& os, uint8_t rex, size_t reg,
                      bool byte_operand, uint8_t size_override) {
@@ -558,14 +558,19 @@
         has_modrm = true;
         src_reg_file = dst_reg_file = SSE;
         break;
-      case 0x62:
+      case 0x60: case 0x61: case 0x62: case 0x6C:
         if (prefix[2] == 0x66) {
           src_reg_file = dst_reg_file = SSE;
           prefix[2] = 0;  // Clear prefix now. It has served its purpose as part of the opcode.
         } else {
           src_reg_file = dst_reg_file = MMX;
         }
-        opcode << "punpckldq";
+        switch (*instr) {
+          case 0x60: opcode << "punpcklbw"; break;
+          case 0x61: opcode << "punpcklwd"; break;
+          case 0x62: opcode << "punpckldq"; break;
+          case 0x6c: opcode << "punpcklqdq"; break;
+        }
         load = true;
         has_modrm = true;
         break;
@@ -650,7 +655,7 @@
         } else {
           dst_reg_file = MMX;
         }
-        static const char* x73_opcodes[] = {"unknown-73", "unknown-73", "psrlq", "unknown-73", "unknown-73", "unknown-73", "psllq", "unknown-73"};
+        static const char* x73_opcodes[] = {"unknown-73", "unknown-73", "psrlq", "psrldq", "unknown-73", "unknown-73", "psllq", "unknown-73"};
         modrm_opcodes = x73_opcodes;
         reg_is_opcode = true;
         has_modrm = true;
@@ -702,12 +707,24 @@
         load = true;
         immediate_bytes = 1;
         break;
+      case 0xA5:
+        opcode << "shld";
+        has_modrm = true;
+        load = true;
+        cx = true;
+        break;
       case 0xAC:
         opcode << "shrd";
         has_modrm = true;
         load = true;
         immediate_bytes = 1;
         break;
+      case 0xAD:
+        opcode << "shrd";
+        has_modrm = true;
+        load = true;
+        cx = true;
+        break;
       case 0xAE:
         if (prefix[0] == 0xF3) {
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
@@ -750,8 +767,9 @@
       case 0xB1: opcode << "cmpxchg"; has_modrm = true; store = true; break;
       case 0xB6: opcode << "movzxb"; has_modrm = true; load = true; byte_second_operand = true; break;
       case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break;
-      case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; byte_second_operand = true; rex |= (rex == 0 ? 0 : 0b1000); break;
+      case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; byte_second_operand = true; rex |= (rex == 0 ? 0 : REX_W); break;
       case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break;
+      case 0xC3: opcode << "movnti"; store = true; has_modrm = true; break;
       case 0xC5:
         if (prefix[2] == 0x66) {
           opcode << "pextrw";
@@ -787,6 +805,18 @@
         opcode << "bswap";
         reg_in_opcode = true;
         break;
+      case 0xD4:
+        if (prefix[2] == 0x66) {
+          src_reg_file = dst_reg_file = SSE;
+          prefix[2] = 0;
+        } else {
+          src_reg_file = dst_reg_file = MMX;
+        }
+        opcode << "paddq";
+        prefix[2] = 0;
+        has_modrm = true;
+        load = true;
+        break;
       case 0xDB:
         if (prefix[2] == 0x66) {
           src_reg_file = dst_reg_file = SSE;
@@ -834,66 +864,14 @@
         has_modrm = true;
         load = true;
         break;
+      case 0xF4:
+      case 0xF6:
       case 0xF8:
-        if (prefix[2] == 0x66) {
-          src_reg_file = dst_reg_file = SSE;
-          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
-        } else {
-          src_reg_file = dst_reg_file = MMX;
-        }
-        opcode << "psubb";
-        prefix[2] = 0;
-        has_modrm = true;
-        load = true;
-        break;
       case 0xF9:
-        if (prefix[2] == 0x66) {
-          src_reg_file = dst_reg_file = SSE;
-          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
-        } else {
-          src_reg_file = dst_reg_file = MMX;
-        }
-        opcode << "psubw";
-        prefix[2] = 0;
-        has_modrm = true;
-        load = true;
-        break;
       case 0xFA:
-        if (prefix[2] == 0x66) {
-          src_reg_file = dst_reg_file = SSE;
-          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
-        } else {
-          src_reg_file = dst_reg_file = MMX;
-        }
-        opcode << "psubd";
-        prefix[2] = 0;
-        has_modrm = true;
-        load = true;
-        break;
+      case 0xFB:
       case 0xFC:
-        if (prefix[2] == 0x66) {
-          src_reg_file = dst_reg_file = SSE;
-          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
-        } else {
-          src_reg_file = dst_reg_file = MMX;
-        }
-        opcode << "paddb";
-        prefix[2] = 0;
-        has_modrm = true;
-        load = true;
-        break;
       case 0xFD:
-        if (prefix[2] == 0x66) {
-          src_reg_file = dst_reg_file = SSE;
-          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
-        } else {
-          src_reg_file = dst_reg_file = MMX;
-        }
-        opcode << "paddw";
-        prefix[2] = 0;
-        has_modrm = true;
-        load = true;
-        break;
       case 0xFE:
         if (prefix[2] == 0x66) {
           src_reg_file = dst_reg_file = SSE;
@@ -901,7 +879,17 @@
         } else {
           src_reg_file = dst_reg_file = MMX;
         }
-        opcode << "paddd";
+        switch (*instr) {
+          case 0xF4: opcode << "pmuludq"; break;
+          case 0xF6: opcode << "psadbw"; break;
+          case 0xF8: opcode << "psubb"; break;
+          case 0xF9: opcode << "psubw"; break;
+          case 0xFA: opcode << "psubd"; break;
+          case 0xFB: opcode << "psubq"; break;
+          case 0xFC: opcode << "paddb"; break;
+          case 0xFD: opcode << "paddw"; break;
+          case 0xFE: opcode << "paddd"; break;
+        }
         prefix[2] = 0;
         has_modrm = true;
         load = true;
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index c35ff85..25d10bd 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -22,17 +22,17 @@
 	oatdump.cc
 
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler libart-compiler,art/disassembler art/compiler,target,ndebug))
 endif
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler libart-compiler,art/disassembler art/compiler,target,debug))
 endif
 
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler libart-compiler,art/disassembler art/compiler,host,ndebug))
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler libart-compiler,art/disassembler art/compiler,host,debug))
 endif
 
 ########################################################################
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 7607bf0..d5e766f 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -20,6 +20,7 @@
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/stringpiece.h"
@@ -29,6 +30,7 @@
 #include "dex_file-inl.h"
 #include "dex_instruction.h"
 #include "disassembler.h"
+#include "elf_builder.h"
 #include "field_helper.h"
 #include "gc_map.h"
 #include "gc/space/image_space.h"
@@ -47,13 +49,16 @@
 #include "oat.h"
 #include "oat_file-inl.h"
 #include "os.h"
+#include "output_stream.h"
 #include "runtime.h"
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
+#include "ScopedLocalRef.h"
 #include "thread_list.h"
 #include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
 #include "vmap_table.h"
+#include "well_known_classes.h"
 
 namespace art {
 
@@ -102,7 +107,6 @@
           "  --no-disassemble may be used to disable disassembly.\n"
           "      Example: --no-disassemble\n"
           "\n");
-  exit(EXIT_FAILURE);
 }
 
 const char* image_roots_descriptions_[] = {
@@ -116,24 +120,250 @@
   "kClassRoots",
 };
 
+class OatSymbolizer : public CodeOutput {
+ public:
+  explicit OatSymbolizer(const OatFile* oat_file, std::string& output_name) :
+      oat_file_(oat_file), builder_(nullptr), elf_output_(nullptr), output_name_(output_name) {}
+
+  bool Init() {
+    Elf32_Word oat_data_size = oat_file_->GetOatHeader().GetExecutableOffset();
+
+    uint32_t diff = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin());
+    uint32_t oat_exec_size = diff - oat_data_size;
+
+    if (output_name_.empty()) {
+      output_name_ = "symbolized.oat";
+    }
+    elf_output_ = OS::CreateEmptyFile(output_name_.c_str());
+
+    builder_.reset(new ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+                                  Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(
+        this,
+        elf_output_,
+        oat_file_->GetOatHeader().GetInstructionSet(),
+        0,
+        oat_data_size,
+        oat_data_size,
+        oat_exec_size,
+        true,
+        false));
+
+    if (!builder_->Init()) {
+      builder_.reset(nullptr);
+      return false;
+    }
+
+    return true;
+  }
+
+  typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&,
+                                          uint32_t,
+                                          const OatFile::OatMethod&,
+                                          const DexFile&,
+                                          uint32_t,
+                                          const DexFile::CodeItem*,
+                                          uint32_t);
+
+  bool Symbolize() {
+    if (builder_.get() == nullptr) {
+      return false;
+    }
+
+    Walk(&art::OatSymbolizer::RegisterForDedup);
+
+    NormalizeState();
+
+    Walk(&art::OatSymbolizer::AddSymbol);
+
+    bool result = builder_->Write();
+
+    elf_output_->Flush();
+    elf_output_->Close();
+
+    return result;
+  }
+
+  void Walk(Callback callback) {
+    std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
+    for (size_t i = 0; i < oat_dex_files.size(); i++) {
+      const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+      CHECK(oat_dex_file != NULL);
+      WalkOatDexFile(oat_dex_file, callback);
+    }
+  }
+
+  void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) {
+    std::string error_msg;
+    std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+    if (dex_file.get() == nullptr) {
+      return;
+    }
+    for (size_t class_def_index = 0;
+        class_def_index < dex_file->NumClassDefs();
+        class_def_index++) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+      const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+      OatClassType type = oat_class.GetType();
+      switch (type) {
+        case kOatClassAllCompiled:
+        case kOatClassSomeCompiled:
+          WalkOatClass(oat_class, *dex_file.get(), class_def, callback);
+          break;
+
+        case kOatClassNoneCompiled:
+        case kOatClassMax:
+          // Ignore.
+          break;
+      }
+    }
+  }
+
+  void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file,
+                    const DexFile::ClassDef& class_def, Callback callback) {
+    const byte* class_data = dex_file.GetClassData(class_def);
+    if (class_data == nullptr) {  // empty class such as a marker interface?
+      return;
+    }
+    // Note: even if this is an interface or a native class, we still have to walk it, as there
+    //       might be a static initializer.
+    ClassDataItemIterator it(dex_file, class_data);
+    SkipAllFields(&it);
+    uint32_t class_method_idx = 0;
+    while (it.HasNextDirectMethod()) {
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+      WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+                    it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback);
+      class_method_idx++;
+      it.Next();
+    }
+    while (it.HasNextVirtualMethod()) {
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+      WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+                    it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback);
+      class_method_idx++;
+      it.Next();
+    }
+    DCHECK(!it.HasNext());
+  }
+
+  void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+                     const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+                     uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+                     uint32_t method_access_flags, Callback callback) {
+    if ((method_access_flags & kAccAbstract) != 0) {
+      // Abstract method, no code.
+      return;
+    }
+    if (oat_method.GetCodeOffset() == 0) {
+      // No code.
+      return;
+    }
+
+    (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item,
+                      method_access_flags);
+  }
+
+  void RegisterForDedup(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+                        const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+                        uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+                        uint32_t method_access_flags) {
+    state_[oat_method.GetCodeOffset()]++;
+  }
+
+  void NormalizeState() {
+    for (auto& x : state_) {
+      if (x.second == 1) {
+        state_[x.first] = 0;
+      }
+    }
+  }
+
+  enum class DedupState {  // private
+    kNotDeduplicated,
+    kDeduplicatedFirst,
+    kDeduplicatedOther
+  };
+  DedupState IsDuplicated(uint32_t offset) {
+    if (state_[offset] == 0) {
+      return DedupState::kNotDeduplicated;
+    }
+    if (state_[offset] == 1) {
+      return DedupState::kDeduplicatedOther;
+    }
+    state_[offset] = 1;
+    return DedupState::kDeduplicatedFirst;
+  }
+
+  void AddSymbol(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+                 const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+                 uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+                 uint32_t method_access_flags) {
+    DedupState dedup = IsDuplicated(oat_method.GetCodeOffset());
+    if (dedup != DedupState::kDeduplicatedOther) {
+      std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true);
+
+      if (dedup == DedupState::kDeduplicatedFirst) {
+        pretty_name = "[Dedup]" + pretty_name;
+      }
+
+      ElfSymtabBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr,
+      Elf32_Sym, Elf32_Shdr>* symtab = &builder_->symtab_builder_;
+
+      symtab->AddSymbol(pretty_name, &builder_->text_builder_, oat_method.GetCodeOffset() -
+                        oat_file_->GetOatHeader().GetExecutableOffset(), true,
+                        oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC);
+    }
+  }
+
+  // Set oat data offset. Required by ElfBuilder/CodeOutput.
+  void SetCodeOffset(size_t offset) {
+    // Nothing to do.
+  }
+
+  // Write oat code. Required by ElfBuilder/CodeOutput.
+  bool Write(OutputStream* out) {
+    return out->WriteFully(oat_file_->Begin(), oat_file_->End() - oat_file_->Begin());
+  }
+
+ private:
+  static void SkipAllFields(ClassDataItemIterator* it) {
+    while (it->HasNextStaticField()) {
+      it->Next();
+    }
+    while (it->HasNextInstanceField()) {
+      it->Next();
+    }
+  }
+
+  const OatFile* oat_file_;
+  std::unique_ptr<ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+                              Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr> > builder_;
+  File* elf_output_;
+  std::unordered_map<uint32_t, uint32_t> state_;
+  std::string output_name_;
+};
+
 class OatDumperOptions {
  public:
   OatDumperOptions(bool dump_raw_mapping_table,
                    bool dump_raw_gc_map,
                    bool dump_vmap,
                    bool disassemble_code,
-                   bool absolute_addresses)
+                   bool absolute_addresses,
+                   Handle<mirror::ClassLoader>* class_loader)
     : dump_raw_mapping_table_(dump_raw_mapping_table),
       dump_raw_gc_map_(dump_raw_gc_map),
       dump_vmap_(dump_vmap),
       disassemble_code_(disassemble_code),
-      absolute_addresses_(absolute_addresses) {}
+      absolute_addresses_(absolute_addresses),
+      class_loader_(class_loader) {}
 
   const bool dump_raw_mapping_table_;
   const bool dump_raw_gc_map_;
   const bool dump_vmap_;
   const bool disassemble_code_;
   const bool absolute_addresses_;
+  Handle<mirror::ClassLoader>* class_loader_;
 };
 
 class OatDumper {
@@ -145,6 +375,7 @@
       disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet(),
                                          new DisassemblerOptions(options_->absolute_addresses_,
                                                                  oat_file.Begin()))) {
+    CHECK(options_->class_loader_ != nullptr);
     AddAllOffsets();
   }
 
@@ -574,6 +805,12 @@
       *indent2_os << "\n";
     }
     {
+        // Based on spill masks from QuickMethodFrameInfo so placed
+        // after it is dumped, but useful for understanding quick
+        // code, so dumped here.
+        DumpVregLocations(*indent2_os, oat_method, code_item);
+    }
+    {
       *indent1_os << "CODE: ";
       uint32_t code_size_offset = oat_method.GetQuickCodeSizeOffset();
       if (code_size_offset > oat_file_.Size()) {
@@ -664,6 +901,12 @@
   }
 
   void DumpVmap(std::ostream& os, const OatFile::OatMethod& oat_method) {
+    // If the native GC map is null, then this method has been compiled with the
+    // optimizing compiler. The optimizing compiler currently outputs its stack map
+    // in the vmap table, and the code below does not work with such a stack map.
+    if (oat_method.GetNativeGcMap() == nullptr) {
+      return;
+    }
     const uint8_t* raw_table = oat_method.GetVmapTable();
     if (raw_table != nullptr) {
       const VmapTable vmap_table(raw_table);
@@ -690,6 +933,45 @@
     }
   }
 
+  void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method,
+                         const DexFile::CodeItem* code_item) {
+    if (code_item != nullptr) {
+      size_t num_locals_ins = code_item->registers_size_;
+      size_t num_ins = code_item->ins_size_;
+      size_t num_locals = num_locals_ins - num_ins;
+      size_t num_outs = code_item->outs_size_;
+
+      os << "vr_stack_locations:";
+      for (size_t reg = 0; reg <= num_locals_ins; reg++) {
+        // For readability, delimit the different kinds of VRs.
+        if (reg == num_locals_ins) {
+          os << "\n\tmethod*:";
+        } else if (reg == num_locals && num_ins > 0) {
+          os << "\n\tins:";
+        } else if (reg == 0 && num_locals > 0) {
+          os << "\n\tlocals:";
+        }
+
+        uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
+                                                      oat_method.GetFpSpillMask(),
+                                                      oat_method.GetFrameSizeInBytes(), reg,
+                                                      GetInstructionSet());
+        os << " v" << reg << "[sp + #" << offset << "]";
+      }
+
+      for (size_t out_reg = 0; out_reg < num_outs; out_reg++) {
+        if (out_reg == 0) {
+          os << "\n\touts:";
+        }
+
+        uint32_t offset = StackVisitor::GetOutVROffset(out_reg, GetInstructionSet());
+        os << " v" << out_reg << "[sp + #" << offset << "]";
+      }
+
+      os << "\n";
+    }
+  }
+
   void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
                     const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) {
     const uint8_t* raw_table = oat_method.GetVmapTable();
@@ -898,13 +1180,16 @@
                                          uint32_t method_access_flags) {
     if ((method_access_flags & kAccNative) == 0) {
       ScopedObjectAccess soa(Thread::Current());
-      StackHandleScope<2> hs(soa.Self());
+      StackHandleScope<1> hs(soa.Self());
       Handle<mirror::DexCache> dex_cache(
           hs.NewHandle(Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file)));
-      auto class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
-      return verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache,
-                                                           class_loader, &class_def, code_item,
-                                                           nullptr, method_access_flags);
+      DCHECK(options_->class_loader_ != nullptr);
+      return verifier::MethodVerifier::VerifyMethodAndDump(soa.Self(), os, dex_method_idx, dex_file,
+                                                           dex_cache,
+                                                           *options_->class_loader_,
+                                                           &class_def, code_item,
+                                                           NullHandle<mirror::ArtMethod>(),
+                                                           method_access_flags);
     }
 
     return nullptr;
@@ -1146,15 +1431,26 @@
       StackHandleScope<1> hs(Thread::Current());
       FieldHelper fh(hs.NewHandle(field));
       mirror::Class* type = fh.GetType();
+      DCHECK(type->IsPrimitive());
       if (type->IsPrimitiveLong()) {
         os << StringPrintf("%" PRId64 " (0x%" PRIx64 ")\n", field->Get64(obj), field->Get64(obj));
       } else if (type->IsPrimitiveDouble()) {
         os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
       } else if (type->IsPrimitiveFloat()) {
         os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
-      } else {
-        DCHECK(type->IsPrimitive());
+      } else if (type->IsPrimitiveInt()) {
         os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
+      } else if (type->IsPrimitiveChar()) {
+        os << StringPrintf("%u (0x%x)\n", field->GetChar(obj), field->GetChar(obj));
+      } else if (type->IsPrimitiveShort()) {
+        os << StringPrintf("%d (0x%x)\n", field->GetShort(obj), field->GetShort(obj));
+      } else if (type->IsPrimitiveBoolean()) {
+        os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj)? "true" : "false",
+            field->GetBoolean(obj));
+      } else if (type->IsPrimitiveByte()) {
+        os << StringPrintf("%d (0x%x)\n", field->GetByte(obj), field->GetByte(obj));
+      } else {
+        LOG(FATAL) << "Unknown type: " << PrettyClass(type);
       }
     } else {
       // Get the value, don't compute the type unless it is non-null as we don't want
@@ -1673,103 +1969,10 @@
   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
 
-static int oatdump(int argc, char** argv) {
-  InitLogging(argv);
+static NoopCompilerCallbacks callbacks;
 
-  // Skip over argv[0].
-  argv++;
-  argc--;
-
-  if (argc == 0) {
-    fprintf(stderr, "No arguments specified\n");
-    usage();
-  }
-
-  const char* oat_filename = nullptr;
-  const char* image_location = nullptr;
-  const char* boot_image_location = nullptr;
-  InstructionSet instruction_set = kRuntimeISA;
-  std::string elf_filename_prefix;
-  std::ostream* os = &std::cout;
-  std::unique_ptr<std::ofstream> out;
-  bool dump_raw_mapping_table = false;
-  bool dump_raw_gc_map = false;
-  bool dump_vmap = true;
-  bool disassemble_code = true;
-
-  for (int i = 0; i < argc; i++) {
-    const StringPiece option(argv[i]);
-    if (option.starts_with("--oat-file=")) {
-      oat_filename = option.substr(strlen("--oat-file=")).data();
-    } else if (option.starts_with("--image=")) {
-      image_location = option.substr(strlen("--image=")).data();
-    } else if (option.starts_with("--boot-image=")) {
-      boot_image_location = option.substr(strlen("--boot-image=")).data();
-    } else if (option.starts_with("--instruction-set=")) {
-      StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
-      if (instruction_set_str == "arm") {
-        instruction_set = kThumb2;
-      } else if (instruction_set_str == "arm64") {
-        instruction_set = kArm64;
-      } else if (instruction_set_str == "mips") {
-        instruction_set = kMips;
-      } else if (instruction_set_str == "x86") {
-        instruction_set = kX86;
-      } else if (instruction_set_str == "x86_64") {
-        instruction_set = kX86_64;
-      }
-    } else if (option =="--dump:raw_mapping_table") {
-      dump_raw_mapping_table = true;
-    } else if (option == "--dump:raw_gc_map") {
-      dump_raw_gc_map = true;
-    } else if (option == "--no-dump:vmap") {
-      dump_vmap = false;
-    } else if (option == "--no-disassemble") {
-      disassemble_code = false;
-    } else if (option.starts_with("--output=")) {
-      const char* filename = option.substr(strlen("--output=")).data();
-      out.reset(new std::ofstream(filename));
-      if (!out->good()) {
-        fprintf(stderr, "Failed to open output filename %s\n", filename);
-        usage();
-      }
-      os = out.get();
-    } else {
-      fprintf(stderr, "Unknown argument %s\n", option.data());
-      usage();
-    }
-  }
-
-  if (image_location == nullptr && oat_filename == nullptr) {
-    fprintf(stderr, "Either --image or --oat must be specified\n");
-    return EXIT_FAILURE;
-  }
-
-  if (image_location != nullptr && oat_filename != nullptr) {
-    fprintf(stderr, "Either --image or --oat must be specified but not both\n");
-    return EXIT_FAILURE;
-  }
-
-  // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
-  bool absolute_addresses = (oat_filename == nullptr);
-  std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(dump_raw_mapping_table,
-                                                                            dump_raw_gc_map,
-                                                                            dump_vmap,
-                                                                            disassemble_code,
-                                                                            absolute_addresses));
-  if (oat_filename != nullptr) {
-    std::string error_msg;
-    OatFile* oat_file =
-        OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
-    if (oat_file == nullptr) {
-      fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
-      return EXIT_FAILURE;
-    }
-    OatDumper oat_dumper(*oat_file, oat_dumper_options.release());
-    bool success = oat_dumper.Dump(*os);
-    return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
-  }
-
+static Runtime* StartRuntime(const char* boot_image_location, const char* image_location,
+                             InstructionSet instruction_set) {
   RuntimeOptions options;
   std::string image_option;
   std::string oat_option;
@@ -1777,7 +1980,6 @@
   std::string boot_oat_option;
 
   // We are more like a compiler than a run-time. We don't want to execute code.
-  NoopCompilerCallbacks callbacks;
   options.push_back(std::make_pair("compilercallbacks", &callbacks));
 
   if (boot_image_location != nullptr) {
@@ -1796,14 +1998,24 @@
 
   if (!Runtime::Create(options, false)) {
     fprintf(stderr, "Failed to create runtime\n");
-    return EXIT_FAILURE;
+    return nullptr;
   }
-  std::unique_ptr<Runtime> runtime(Runtime::Current());
+
   // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
   // give it away now and then switch to a more manageable ScopedObjectAccess.
   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+  return Runtime::Current();
+}
+
+static int DumpImage(Runtime* runtime, const char* image_location, OatDumperOptions* options,
+                     std::ostream* os) {
+  // Dumping the image, no explicit class loader.
+  NullHandle<mirror::ClassLoader> null_class_loader;
+  options->class_loader_ = &null_class_loader;
+
   ScopedObjectAccess soa(Thread::Current());
-  gc::Heap* heap = Runtime::Current()->GetHeap();
+  gc::Heap* heap = runtime->GetHeap();
   gc::space::ImageSpace* image_space = heap->GetImageSpace();
   CHECK(image_space != nullptr);
   const ImageHeader& image_header = image_space->GetImageHeader();
@@ -1811,11 +2023,227 @@
     fprintf(stderr, "Invalid image header %s\n", image_location);
     return EXIT_FAILURE;
   }
-  ImageDumper image_dumper(os, *image_space, image_header, oat_dumper_options.release());
+  ImageDumper image_dumper(os, *image_space, image_header, options);
   bool success = image_dumper.Dump();
   return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
+static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOptions* options,
+                              std::ostream* os) {
+  CHECK(runtime != nullptr && oat_file != nullptr && options != nullptr);
+
+  Thread* self = Thread::Current();
+  CHECK(self != nullptr);
+  // Need well-known-classes.
+  WellKnownClasses::Init(self->GetJniEnv());
+
+  // Need to register dex files to get a working dex cache.
+  ScopedObjectAccess soa(self);
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  class_linker->RegisterOatFile(oat_file);
+  std::vector<const DexFile*> dex_files;
+  for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
+    std::string error_msg;
+    const DexFile* dex_file = odf->OpenDexFile(&error_msg);
+    CHECK(dex_file != nullptr) << error_msg;
+    class_linker->RegisterDexFile(*dex_file);
+    dex_files.push_back(dex_file);
+  }
+
+  // Need a class loader.
+  soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader);
+  ScopedLocalRef<jobject> class_loader_local(soa.Env(),
+      soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
+  jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
+  // Fake that we're a compiler.
+  runtime->SetCompileTimeClassPath(class_loader, dex_files);
+
+  // Use the class loader while dumping.
+  StackHandleScope<1> scope(self);
+  Handle<mirror::ClassLoader> loader_handle = scope.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(class_loader));
+  options->class_loader_ = &loader_handle;
+
+  OatDumper oat_dumper(*oat_file, options);
+  bool success = oat_dumper.Dump(*os);
+  return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static int DumpOatWithoutRuntime(OatFile* oat_file, OatDumperOptions* options, std::ostream* os) {
+  // No image = no class loader.
+  NullHandle<mirror::ClassLoader> null_class_loader;
+  options->class_loader_ = &null_class_loader;
+
+  OatDumper oat_dumper(*oat_file, options);
+  bool success = oat_dumper.Dump(*os);
+  return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
+                   std::ostream* os) {
+  std::string error_msg;
+  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+  if (oat_file == nullptr) {
+    fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+    return EXIT_FAILURE;
+  }
+
+  if (runtime != nullptr) {
+    return DumpOatWithRuntime(runtime, oat_file, options, os);
+  } else {
+    return DumpOatWithoutRuntime(oat_file, options, os);
+  }
+}
+
+static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
+  std::string error_msg;
+  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+  if (oat_file == nullptr) {
+    fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+    return EXIT_FAILURE;
+  }
+
+  OatSymbolizer oat_symbolizer(oat_file, output_name);
+  if (!oat_symbolizer.Init()) {
+    fprintf(stderr, "Failed to initialize symbolizer\n");
+    return EXIT_FAILURE;
+  }
+  if (!oat_symbolizer.Symbolize()) {
+    fprintf(stderr, "Failed to symbolize\n");
+    return EXIT_FAILURE;
+  }
+
+  return EXIT_SUCCESS;
+}
+
+struct OatdumpArgs {
+  bool Parse(int argc, char** argv) {
+    // Skip over argv[0].
+    argv++;
+    argc--;
+
+    if (argc == 0) {
+      fprintf(stderr, "No arguments specified\n");
+      usage();
+      return false;
+    }
+
+    for (int i = 0; i < argc; i++) {
+      const StringPiece option(argv[i]);
+      if (option.starts_with("--oat-file=")) {
+        oat_filename_ = option.substr(strlen("--oat-file=")).data();
+      } else if (option.starts_with("--image=")) {
+        image_location_ = option.substr(strlen("--image=")).data();
+      } else if (option.starts_with("--boot-image=")) {
+        boot_image_location_ = option.substr(strlen("--boot-image=")).data();
+      } else if (option.starts_with("--instruction-set=")) {
+        StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
+        instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
+        if (instruction_set_ == kNone) {
+          fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
+          usage();
+          return false;
+        }
+      } else if (option =="--dump:raw_mapping_table") {
+        dump_raw_mapping_table_ = true;
+      } else if (option == "--dump:raw_gc_map") {
+        dump_raw_gc_map_ = true;
+      } else if (option == "--no-dump:vmap") {
+        dump_vmap_ = false;
+      } else if (option == "--no-disassemble") {
+        disassemble_code_ = false;
+      } else if (option.starts_with("--output=")) {
+        output_name_ = option.substr(strlen("--output=")).ToString();
+        const char* filename = output_name_.c_str();
+        out_.reset(new std::ofstream(filename));
+        if (!out_->good()) {
+          fprintf(stderr, "Failed to open output filename %s\n", filename);
+          usage();
+          return false;
+        }
+        os_ = out_.get();
+      } else if (option.starts_with("--symbolize=")) {
+        oat_filename_ = option.substr(strlen("--symbolize=")).data();
+        symbolize_ = true;
+      } else {
+        fprintf(stderr, "Unknown argument %s\n", option.data());
+        usage();
+        return false;
+      }
+    }
+
+    if (image_location_ == nullptr && oat_filename_ == nullptr) {
+      fprintf(stderr, "Either --image or --oat must be specified\n");
+      return false;
+    }
+
+    if (image_location_ != nullptr && oat_filename_ != nullptr) {
+      fprintf(stderr, "Either --image or --oat must be specified but not both\n");
+      return false;
+    }
+
+    return true;
+  }
+
+  const char* oat_filename_ = nullptr;
+  const char* image_location_ = nullptr;
+  const char* boot_image_location_ = nullptr;
+  InstructionSet instruction_set_ = kRuntimeISA;
+  std::string elf_filename_prefix_;
+  std::ostream* os_ = &std::cout;
+  std::unique_ptr<std::ofstream> out_;
+  std::string output_name_;
+  bool dump_raw_mapping_table_ = false;
+  bool dump_raw_gc_map_ = false;
+  bool dump_vmap_ = true;
+  bool disassemble_code_ = true;
+  bool symbolize_ = false;
+};
+
+static int oatdump(int argc, char** argv) {
+  InitLogging(argv);
+
+  OatdumpArgs args;
+  if (!args.Parse(argc, argv)) {
+    return EXIT_FAILURE;
+  }
+
+  // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
+  bool absolute_addresses = (args.oat_filename_ == nullptr);
+
+  std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(
+      args.dump_raw_mapping_table_,
+      args.dump_raw_gc_map_,
+      args.dump_vmap_,
+      args.disassemble_code_,
+      absolute_addresses,
+      nullptr));
+
+  std::unique_ptr<Runtime> runtime;
+  if ((args.boot_image_location_ != nullptr || args.image_location_ != nullptr) &&
+      !args.symbolize_) {
+    // If we have a boot image option, try to start the runtime; except when just symbolizing.
+    runtime.reset(StartRuntime(args.boot_image_location_,
+                               args.image_location_,
+                               args.instruction_set_));
+  }
+
+  if (args.oat_filename_ != nullptr) {
+    if (args.symbolize_) {
+      return SymbolizeOat(args.oat_filename_, args.output_name_);
+    } else {
+      return DumpOat(runtime.get(), args.oat_filename_, oat_dumper_options.release(), args.os_);
+    }
+  }
+
+  if (runtime.get() == nullptr) {
+    // We need the runtime when printing an image.
+    return EXIT_FAILURE;
+  }
+
+  return DumpImage(runtime.get(), args.image_location_, oat_dumper_options.release(), args.os_);
+}
+
 }  // namespace art
 
 int main(int argc, char** argv) {
diff --git a/patchoat/Android.mk b/patchoat/Android.mk
index 8b6b9ad..1e16096 100644
--- a/patchoat/Android.mk
+++ b/patchoat/Android.mk
@@ -37,9 +37,9 @@
 endif
 
 # We always build patchoat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,ndebug))
 endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,debug))
 endif
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index a9b2a43..50b4ece 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -411,13 +411,11 @@
 void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) {
   // Just update the entry points if it looks like we should.
   // TODO: sanity check all the pointers' values
-#if defined(ART_USE_PORTABLE_COMPILER)
   uintptr_t portable = reinterpret_cast<uintptr_t>(
       object->GetEntryPointFromPortableCompiledCode<kVerifyNone>());
   if (portable != 0) {
     copy->SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(portable + delta_));
   }
-#endif
   uintptr_t quick= reinterpret_cast<uintptr_t>(
       object->GetEntryPointFromQuickCompiledCode<kVerifyNone>());
   if (quick != 0) {
@@ -469,16 +467,13 @@
   return true;
 }
 
-bool PatchOat::CheckOatFile() {
-  Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches");
-  if (patches_sec == nullptr) {
+template <typename ptr_t>
+bool PatchOat::CheckOatFile(const Elf32_Shdr& patches_sec) {
+  if (patches_sec.sh_type != SHT_OAT_PATCH) {
     return false;
   }
-  if (patches_sec->sh_type != SHT_OAT_PATCH) {
-    return false;
-  }
-  uintptr_t* patches = reinterpret_cast<uintptr_t*>(oat_file_->Begin() + patches_sec->sh_offset);
-  uintptr_t* patches_end = patches + (patches_sec->sh_size/sizeof(uintptr_t));
+  ptr_t* patches = reinterpret_cast<ptr_t*>(oat_file_->Begin() + patches_sec.sh_offset);
+  ptr_t* patches_end = patches + (patches_sec.sh_size / sizeof(ptr_t));
   Elf32_Shdr* oat_data_sec = oat_file_->FindSectionByName(".rodata");
   Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text");
   if (oat_data_sec == nullptr) {
@@ -577,6 +572,11 @@
     }
   }
 
+  t.NewTiming("Fixup Debug Sections");
+  if (!oat_file_->FixupDebugSections(delta_)) {
+    return false;
+  }
+
   return true;
 }
 
@@ -604,10 +604,28 @@
     LOG(ERROR) << ".oat_patches section not found. Aborting patch";
     return false;
   }
-  DCHECK(CheckOatFile()) << "Oat file invalid";
-  CHECK_EQ(patches_sec->sh_type, SHT_OAT_PATCH) << "Unexpected type of .oat_patches";
-  uintptr_t* patches = reinterpret_cast<uintptr_t*>(oat_file_->Begin() + patches_sec->sh_offset);
-  uintptr_t* patches_end = patches + (patches_sec->sh_size/sizeof(uintptr_t));
+  if (patches_sec->sh_type != SHT_OAT_PATCH) {
+    LOG(ERROR) << "Unexpected type of .oat_patches";
+    return false;
+  }
+
+  switch (patches_sec->sh_entsize) {
+    case sizeof(uint32_t):
+      return PatchTextSection<uint32_t>(*patches_sec);
+    case sizeof(uint64_t):
+      return PatchTextSection<uint64_t>(*patches_sec);
+    default:
+      LOG(ERROR) << ".oat_patches Entsize of " << patches_sec->sh_entsize << "bits "
+                 << "is not valid";
+      return false;
+  }
+}
+
+template <typename ptr_t>
+bool PatchOat::PatchTextSection(const Elf32_Shdr& patches_sec) {
+  DCHECK(CheckOatFile<ptr_t>(patches_sec)) << "Oat file invalid";
+  ptr_t* patches = reinterpret_cast<ptr_t*>(oat_file_->Begin() + patches_sec.sh_offset);
+  ptr_t* patches_end = patches + (patches_sec.sh_size / sizeof(ptr_t));
   Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text");
   CHECK(oat_text_sec != nullptr);
   byte* to_patch = oat_file_->Begin() + oat_text_sec->sh_offset;
@@ -619,7 +637,6 @@
     CHECK_LT(reinterpret_cast<uintptr_t>(patch_loc), to_patch_end);
     *patch_loc += delta_;
   }
-
   return true;
 }
 
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 6960d3b..9086d58 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -36,7 +36,7 @@
 class Reference;
 class Class;
 class ArtMethod;
-};  // namespace mirror
+}  // namespace mirror
 
 class PatchOat {
  public:
@@ -74,11 +74,12 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool InHeap(mirror::Object*);
 
-  bool CheckOatFile();
-
   // Patches oat in place, modifying the oat_file given to the constructor.
   bool PatchElf();
   bool PatchTextSection();
+  // Templatized version to actually do the patching with the right sized offsets.
+  template <typename ptr_t> bool PatchTextSection(const Elf32_Shdr& patches_sec);
+  template <typename ptr_t> bool CheckOatFile(const Elf32_Shdr& patches_sec);
   bool PatchOatHeader();
   bool PatchSymbols(Elf32_Shdr* section);
 
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 17ee8ab..e954476 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -80,6 +80,7 @@
   interpreter/interpreter.cc \
   interpreter/interpreter_common.cc \
   interpreter/interpreter_switch_impl.cc \
+  java_vm_ext.cc \
   jdwp/jdwp_event.cc \
   jdwp/jdwp_expand_buf.cc \
   jdwp/jdwp_handler.cc \
@@ -87,6 +88,7 @@
   jdwp/jdwp_request.cc \
   jdwp/jdwp_socket.cc \
   jdwp/object_registry.cc \
+  jni_env_ext.cc \
   jni_internal.cc \
   jobject_comparator.cc \
   mem_map.cc \
@@ -326,10 +328,6 @@
   LIBART_CFLAGS += -DUSE_JEMALLOC
 endif
 
-ifeq ($(ART_USE_HSPACE_COMPACT),true)
-  LIBART_CFLAGS += -DART_USE_HSPACE_COMPACT
-endif
-
 # $(1): target or host
 # $(2): ndebug or debug
 define build-libart
@@ -424,7 +422,7 @@
   include external/libcxx/libcxx.mk
   LOCAL_SHARED_LIBRARIES += libbacktrace_libc++
   ifeq ($$(art_target_or_host),target)
-    LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils libsigchain
+    LOCAL_SHARED_LIBRARIES += libcutils libdl libutils libsigchain
     LOCAL_STATIC_LIBRARIES := libziparchive libz
   else # host
     LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils
@@ -471,10 +469,10 @@
 
 # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since
 # they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-libart,host,ndebug))
 endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-libart,host,debug))
 endif
 
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 8c6afd6..2780d1b 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -48,12 +48,24 @@
 extern "C" void* art_quick_resolve_string(void*, uint32_t);
 
 // Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
 extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
 extern "C" int art_quick_set32_static(uint32_t, int32_t);
 extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
 extern "C" int art_quick_set64_static(uint32_t, int64_t);
 extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
 extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
 extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
 extern "C" int32_t art_quick_get32_static(uint32_t);
 extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -154,15 +166,27 @@
   qpoints->pResolveString = art_quick_resolve_string;
 
   // Field
+  qpoints->pSet8Instance = art_quick_set8_instance;
+  qpoints->pSet8Static = art_quick_set8_static;
+  qpoints->pSet16Instance = art_quick_set16_instance;
+  qpoints->pSet16Static = art_quick_set16_static;
   qpoints->pSet32Instance = art_quick_set32_instance;
   qpoints->pSet32Static = art_quick_set32_static;
   qpoints->pSet64Instance = art_quick_set64_instance;
   qpoints->pSet64Static = art_quick_set64_static;
   qpoints->pSetObjInstance = art_quick_set_obj_instance;
   qpoints->pSetObjStatic = art_quick_set_obj_static;
+  qpoints->pGetByteInstance = art_quick_get_byte_instance;
+  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+  qpoints->pGetShortInstance = art_quick_get_short_instance;
+  qpoints->pGetCharInstance = art_quick_get_char_instance;
   qpoints->pGet32Instance = art_quick_get32_instance;
   qpoints->pGet64Instance = art_quick_get64_instance;
   qpoints->pGetObjInstance = art_quick_get_obj_instance;
+  qpoints->pGetByteStatic = art_quick_get_byte_static;
+  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+  qpoints->pGetShortStatic = art_quick_get_short_static;
+  qpoints->pGetCharStatic = art_quick_get_char_static;
   qpoints->pGet32Static = art_quick_get32_static;
   qpoints->pGet64Static = art_quick_get64_static;
   qpoints->pGetObjStatic = art_quick_get_obj_static;
@@ -232,6 +256,6 @@
   qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
   qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
   qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-};
+}
 
 }  // namespace art
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1b30c9c..51bcd3c 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -203,6 +203,77 @@
 END \c_name
 .endm
 
+.macro  RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg
+    ldr \reg, [r9, #THREAD_EXCEPTION_OFFSET]   // Get exception field.
+    cbnz \reg, 1f
+    bx lr
+1:
+    DELIVER_PENDING_EXCEPTION
+.endm
+
+.macro  RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+    RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r1
+.endm
+
+.macro RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+    RETURN_IF_RESULT_IS_ZERO
+    DELIVER_PENDING_EXCEPTION
+.endm
+
+// Macros taking opportunity of code similarities for downcalls with referrer for non-wide fields.
+.macro  ONE_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r1, [sp, #32]                 @ pass referrer
+    mov    r2, r9                        @ pass Thread::Current
+    mov    r3, sp                        @ pass SP
+    bl     \entrypoint                   @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    \return
+END \name
+.endm
+
+.macro  TWO_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r2, [sp, #32]                 @ pass referrer
+    mov    r3, r9                        @ pass Thread::Current
+    mov    r12, sp
+    str    r12, [sp, #-16]!              @ expand the frame and pass SP
+    .pad #16
+    .cfi_adjust_cfa_offset 16
+    bl     \entrypoint                   @ (field_idx, Object*, referrer, Thread*, SP)
+    add    sp, #16                       @ strip the extra frame
+    .cfi_adjust_cfa_offset -16
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    \return
+END \name
+.endm
+
+.macro  THREE_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
+    ldr    r3, [sp, #32]                 @ pass referrer
+    mov    r12, sp                       @ save SP
+    sub    sp, #8                        @ grow frame for alignment with stack args
+    .pad #8
+    .cfi_adjust_cfa_offset 8
+    push   {r9, r12}                     @ pass Thread::Current and SP
+    .save {r9, r12}
+    .cfi_adjust_cfa_offset 8
+    .cfi_rel_offset r9, 0
+    .cfi_rel_offset r12, 4
+    bl     \entrypoint                   @ (field_idx, Object*, new_val, referrer, Thread*, SP)
+    add    sp, #16                       @ release out args
+    .cfi_adjust_cfa_offset -16
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME   @ TODO: we can clearly save an add here
+    \return
+END \name
+.endm
+
     /*
      * Called by managed code, saves callee saves and then calls artThrowException
      * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception.
@@ -601,23 +672,14 @@
 END art_quick_initialize_type_and_verify_access
 
     /*
-     * Called by managed code to resolve a static field and load a 32-bit primitive value.
+     * Called by managed code to resolve a static field and load a non-wide value.
      */
-    .extern artGet32StaticFromCode
-ENTRY art_quick_get32_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r1, [sp, #32]                 @ pass referrer
-    mov    r2, r9                        @ pass Thread::Current
-    mov    r3, sp                        @ pass SP
-    bl     artGet32StaticFromCode        @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
-    ldr    r1, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    cbnz   r1, 1f                        @ success if no exception pending
-    bx     lr                            @ return on success
-1:
-    DELIVER_PENDING_EXCEPTION
-END art_quick_get32_static
-
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
     /*
      * Called by managed code to resolve a static field and load a 64-bit primitive value.
      */
@@ -637,43 +699,14 @@
 END art_quick_get64_static
 
     /*
-     * Called by managed code to resolve a static field and load an object reference.
+     * Called by managed code to resolve an instance field and load a non-wide value.
      */
-    .extern artGetObjStaticFromCode
-ENTRY art_quick_get_obj_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r1, [sp, #32]                 @ pass referrer
-    mov    r2, r9                        @ pass Thread::Current
-    mov    r3, sp                        @ pass SP
-    bl     artGetObjStaticFromCode       @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
-    ldr    r1, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    cbnz   r1, 1f                        @ success if no exception pending
-    bx     lr                            @ return on success
-1:
-    DELIVER_PENDING_EXCEPTION
-END art_quick_get_obj_static
-
-    /*
-     * Called by managed code to resolve an instance field and load a 32-bit primitive value.
-     */
-    .extern artGet32InstanceFromCode
-ENTRY art_quick_get32_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r2, [sp, #32]                 @ pass referrer
-    mov    r3, r9                        @ pass Thread::Current
-    mov    r12, sp
-    str    r12, [sp, #-16]!              @ expand the frame and pass SP
-    bl     artGet32InstanceFromCode      @ (field_idx, Object*, referrer, Thread*, SP)
-    add    sp, #16                       @ strip the extra frame
-    ldr    r1, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    cbnz   r1, 1f                        @ success if no exception pending
-    bx     lr                            @ return on success
-1:
-    DELIVER_PENDING_EXCEPTION
-END art_quick_get32_instance
-
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
     /*
      * Called by managed code to resolve an instance field and load a 64-bit primitive value.
      */
@@ -698,48 +731,12 @@
 END art_quick_get64_instance
 
     /*
-     * Called by managed code to resolve an instance field and load an object reference.
+     * Called by managed code to resolve a static field and store a non-wide value.
      */
-    .extern artGetObjInstanceFromCode
-ENTRY art_quick_get_obj_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r2, [sp, #32]                 @ pass referrer
-    mov    r3, r9                        @ pass Thread::Current
-    mov    r12, sp
-    str    r12, [sp, #-16]!              @ expand the frame and pass SP
-    .pad #16
-    .cfi_adjust_cfa_offset 16
-    bl     artGetObjInstanceFromCode     @ (field_idx, Object*, referrer, Thread*, SP)
-    add    sp, #16                       @ strip the extra frame
-    .cfi_adjust_cfa_offset -16
-    ldr    r1, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    cbnz   r1, 1f                        @ success if no exception pending
-    bx     lr                            @ return on success
-1:
-    DELIVER_PENDING_EXCEPTION
-END art_quick_get_obj_instance
-
-    /*
-     * Called by managed code to resolve a static field and store a 32-bit primitive value.
-     */
-    .extern artSet32StaticFromCode
-ENTRY art_quick_set32_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r2, [sp, #32]                 @ pass referrer
-    mov    r3, r9                        @ pass Thread::Current
-    mov    r12, sp
-    str    r12, [sp, #-16]!              @ expand the frame and pass SP
-    .pad #16
-    .cfi_adjust_cfa_offset 16
-    bl     artSet32StaticFromCode        @ (field_idx, new_val, referrer, Thread*, SP)
-    add    sp, #16                       @ strip the extra frame
-    .cfi_adjust_cfa_offset -16
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_set32_static
-
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
     /*
      * Called by managed code to resolve a static field and store a 64-bit primitive value.
      * On entry r0 holds field index, r1:r2 hold new_val
@@ -767,53 +764,16 @@
 END art_quick_set64_static
 
     /*
-     * Called by managed code to resolve a static field and store an object reference.
+     * Called by managed code to resolve an instance field and store a non-wide value.
      */
-    .extern artSetObjStaticFromCode
-ENTRY art_quick_set_obj_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r2, [sp, #32]                 @ pass referrer
-    mov    r3, r9                        @ pass Thread::Current
-    mov    r12, sp
-    str    r12, [sp, #-16]!              @ expand the frame and pass SP
-    .pad #16
-    .cfi_adjust_cfa_offset 16
-    bl     artSetObjStaticFromCode       @ (field_idx, new_val, referrer, Thread*, SP)
-    add    sp, #16                       @ strip the extra frame
-    .cfi_adjust_cfa_offset -16
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_set_obj_static
-
-    /*
-     * Called by managed code to resolve an instance field and store a 32-bit primitive value.
-     */
-    .extern artSet32InstanceFromCode
-ENTRY art_quick_set32_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r3, [sp, #32]                 @ pass referrer
-    mov    r12, sp                       @ save SP
-    sub    sp, #8                        @ grow frame for alignment with stack args
-    .pad #8
-    .cfi_adjust_cfa_offset 8
-    push   {r9, r12}                     @ pass Thread::Current and SP
-    .save {r9, r12}
-    .cfi_adjust_cfa_offset 8
-    .cfi_rel_offset r9, 0
-    .cfi_rel_offset r12, 4
-    bl     artSet32InstanceFromCode      @ (field_idx, Object*, new_val, referrer, Thread*, SP)
-    add    sp, #16                       @ release out args
-    .cfi_adjust_cfa_offset -16
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME   @ TODO: we can clearly save an add here
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_set32_instance
-
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
     /*
      * Called by managed code to resolve an instance field and store a 64-bit primitive value.
      */
-    .extern artSet32InstanceFromCode
+    .extern artSet64InstanceFromCode
 ENTRY art_quick_set64_instance
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
     mov    r12, sp                       @ save SP
@@ -833,29 +793,6 @@
 END art_quick_set64_instance
 
     /*
-     * Called by managed code to resolve an instance field and store an object reference.
-     */
-    .extern artSetObjInstanceFromCode
-ENTRY art_quick_set_obj_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     @ save callee saves in case of GC
-    ldr    r3, [sp, #32]                 @ pass referrer
-    mov    r12, sp                       @ save SP
-    sub    sp, #8                        @ grow frame for alignment with stack args
-    .pad #8
-    .cfi_adjust_cfa_offset 8
-    push   {r9, r12}                     @ pass Thread::Current and SP
-    .save {r9, r12}
-    .cfi_adjust_cfa_offset 8
-    .cfi_rel_offset r9, 0
-    bl     artSetObjInstanceFromCode     @ (field_idx, Object*, new_val, referrer, Thread*, SP)
-    add    sp, #16                       @ release out args
-    .cfi_adjust_cfa_offset -16
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME   @ TODO: we can clearly save an add here
-    RETURN_IF_RESULT_IS_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_set_obj_instance
-
-    /*
      * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
      * exception on error. On success the String is returned. R0 holds the referring method,
      * R1 holds the string index. The fast path check for hit in strings cache has already been
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 0c33d9c..70e93b3 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -47,12 +47,24 @@
 extern "C" void* art_quick_resolve_string(void*, uint32_t);
 
 // Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
 extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
 extern "C" int art_quick_set32_static(uint32_t, int32_t);
 extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
 extern "C" int art_quick_set64_static(uint32_t, int64_t);
 extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
 extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
 extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
 extern "C" int32_t art_quick_get32_static(uint32_t);
 extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -136,15 +148,27 @@
   qpoints->pResolveString = art_quick_resolve_string;
 
   // Field
+  qpoints->pSet8Instance = art_quick_set8_instance;
+  qpoints->pSet8Static = art_quick_set8_static;
+  qpoints->pSet16Instance = art_quick_set16_instance;
+  qpoints->pSet16Static = art_quick_set16_static;
   qpoints->pSet32Instance = art_quick_set32_instance;
   qpoints->pSet32Static = art_quick_set32_static;
   qpoints->pSet64Instance = art_quick_set64_instance;
   qpoints->pSet64Static = art_quick_set64_static;
   qpoints->pSetObjInstance = art_quick_set_obj_instance;
   qpoints->pSetObjStatic = art_quick_set_obj_static;
+  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+  qpoints->pGetByteInstance = art_quick_get_byte_instance;
+  qpoints->pGetCharInstance = art_quick_get_char_instance;
+  qpoints->pGetShortInstance = art_quick_get_short_instance;
   qpoints->pGet32Instance = art_quick_get32_instance;
   qpoints->pGet64Instance = art_quick_get64_instance;
   qpoints->pGetObjInstance = art_quick_get_obj_instance;
+  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+  qpoints->pGetByteStatic = art_quick_get_byte_static;
+  qpoints->pGetCharStatic = art_quick_get_char_static;
+  qpoints->pGetShortStatic = art_quick_get_short_static;
   qpoints->pGet32Static = art_quick_get32_static;
   qpoints->pGet64Static = art_quick_get64_static;
   qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 2a19e27..606816a 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1266,17 +1266,29 @@
 TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
 TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO
 
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
 TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
 TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
 
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
 THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
 THREE_ARG_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
 THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 4db5ea6..6add93b 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -22,9 +22,9 @@
 // Offset of field Thread::tls32_.state_and_flags verified in InitCpu
 #define THREAD_FLAGS_OFFSET 0
 // Offset of field Thread::tlsPtr_.card_table verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 112
+#define THREAD_CARD_TABLE_OFFSET 120
 // Offset of field Thread::tlsPtr_.exception verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 116
+#define THREAD_EXCEPTION_OFFSET 124
 
 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index d3e7d5e..25e911d 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -49,12 +49,24 @@
 extern "C" void* art_quick_resolve_string(void*, uint32_t);
 
 // Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
 extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
 extern "C" int art_quick_set32_static(uint32_t, int32_t);
 extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
 extern "C" int art_quick_set64_static(uint32_t, int64_t);
 extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
 extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
 extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
 extern "C" int32_t art_quick_get32_static(uint32_t);
 extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -159,15 +171,27 @@
   qpoints->pResolveString = art_quick_resolve_string;
 
   // Field
+  qpoints->pSet8Instance = art_quick_set8_instance;
+  qpoints->pSet8Static = art_quick_set8_static;
+  qpoints->pSet16Instance = art_quick_set16_instance;
+  qpoints->pSet16Static = art_quick_set16_static;
   qpoints->pSet32Instance = art_quick_set32_instance;
   qpoints->pSet32Static = art_quick_set32_static;
   qpoints->pSet64Instance = art_quick_set64_instance;
   qpoints->pSet64Static = art_quick_set64_static;
   qpoints->pSetObjInstance = art_quick_set_obj_instance;
   qpoints->pSetObjStatic = art_quick_set_obj_static;
+  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+  qpoints->pGetByteInstance = art_quick_get_byte_instance;
+  qpoints->pGetCharInstance = art_quick_get_char_instance;
+  qpoints->pGetShortInstance = art_quick_get_short_instance;
   qpoints->pGet32Instance = art_quick_get32_instance;
   qpoints->pGet64Instance = art_quick_get64_instance;
   qpoints->pGetObjInstance = art_quick_get_obj_instance;
+  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+  qpoints->pGetByteStatic = art_quick_get_byte_static;
+  qpoints->pGetCharStatic = art_quick_get_char_static;
+  qpoints->pGetShortStatic = art_quick_get_short_static;
   qpoints->pGet32Static = art_quick_get32_static;
   qpoints->pGet64Static = art_quick_get64_static;
   qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 8786222..9e9e523 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -739,6 +739,59 @@
     move    $a3, $sp                           # pass $sp
     RETURN_IF_RESULT_IS_NON_ZERO
 END art_quick_initialize_type_and_verify_access
+    /*
+     * Called by managed code to resolve a static field and load a boolean primitive value.
+     */
+    .extern artGetBooleanStaticFromCode
+ENTRY art_quick_get_boolean_static
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, 64($sp)                  # pass referrer's Method*
+    move   $a2, rSELF                    # pass Thread::Current
+    jal    artGetBooleanStaticFromCode   # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+    move   $a3, $sp                      # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_boolean_static
+    /*
+     * Called by managed code to resolve a static field and load a byte primitive value.
+     */
+    .extern artGetByteStaticFromCode
+ENTRY art_quick_get_byte_static
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, 64($sp)                  # pass referrer's Method*
+    move   $a2, rSELF                    # pass Thread::Current
+    jal    artGetByteStaticFromCode      # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+    move   $a3, $sp                      # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_byte_static
+
+    /*
+     * Called by managed code to resolve a static field and load a char primitive value.
+     */
+    .extern artGetCharStaticFromCode
+ENTRY art_quick_get_char_static
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, 64($sp)                  # pass referrer's Method*
+    move   $a2, rSELF                    # pass Thread::Current
+    jal    artGetCharStaticFromCode      # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+    move   $a3, $sp                      # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_char_static
+    /*
+     * Called by managed code to resolve a static field and load a short primitive value.
+     */
+    .extern artGetShortStaticFromCode
+ENTRY art_quick_get_short_static
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a1, 64($sp)                  # pass referrer's Method*
+    move   $a2, rSELF                    # pass Thread::Current
+    jal    artGetShortStaticFromCode     # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+    move   $a3, $sp                      # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_short_static
 
     /*
      * Called by managed code to resolve a static field and load a 32-bit primitive value.
@@ -783,6 +836,60 @@
 END art_quick_get_obj_static
 
     /*
+     * Called by managed code to resolve an instance field and load a boolean primitive value.
+     */
+    .extern artGetBooleanInstanceFromCode
+ENTRY art_quick_get_boolean_instance
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, 64($sp)                  # pass referrer's Method*
+    move   $a3, rSELF                    # pass Thread::Current
+    jal    artGetBooleanInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp)
+    sw     $sp, 16($sp)                  # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_boolean_instance
+    /*
+     * Called by managed code to resolve an instance field and load a byte primitive value.
+     */
+    .extern artGetByteInstanceFromCode
+ENTRY art_quick_get_byte_instance
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, 64($sp)                  # pass referrer's Method*
+    move   $a3, rSELF                    # pass Thread::Current
+    jal    artGetByteInstanceFromCode    # (field_idx, Object*, referrer, Thread*, $sp)
+    sw     $sp, 16($sp)                  # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_byte_instance
+
+    /*
+     * Called by managed code to resolve an instance field and load a char primitive value.
+     */
+    .extern artGetCharInstanceFromCode
+ENTRY art_quick_get_char_instance
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, 64($sp)                  # pass referrer's Method*
+    move   $a3, rSELF                    # pass Thread::Current
+    jal    artGetCharInstanceFromCode    # (field_idx, Object*, referrer, Thread*, $sp)
+    sw     $sp, 16($sp)                  # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_char_instance
+    /*
+     * Called by managed code to resolve an instance field and load a short primitive value.
+     */
+    .extern artGetShortInstanceFromCode
+ENTRY art_quick_get_short_instance
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, 64($sp)                  # pass referrer's Method*
+    move   $a3, rSELF                    # pass Thread::Current
+    jal    artGetShortInstanceFromCode      # (field_idx, Object*, referrer, Thread*, $sp)
+    sw     $sp, 16($sp)                  # pass $sp
+    RETURN_IF_NO_EXCEPTION
+END art_quick_get_short_instance
+
+    /*
      * Called by managed code to resolve an instance field and load a 32-bit primitive value.
      */
     .extern artGet32InstanceFromCode
@@ -825,6 +932,34 @@
 END art_quick_get_obj_instance
 
     /*
+     * Called by managed code to resolve a static field and store a 8-bit primitive value.
+     */
+    .extern artSet8StaticFromCode
+ENTRY art_quick_set8_static
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, 64($sp)                  # pass referrer's Method*
+    move   $a3, rSELF                    # pass Thread::Current
+    jal    artSet8StaticFromCode         # (field_idx, new_val, referrer, Thread*, $sp)
+    sw     $sp, 16($sp)                  # pass $sp
+    RETURN_IF_ZERO
+END art_quick_set8_static
+
+    /*
+     * Called by managed code to resolve a static field and store a 16-bit primitive value.
+     */
+    .extern artSet16StaticFromCode
+ENTRY art_quick_set16_static
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a2, 64($sp)                  # pass referrer's Method*
+    move   $a3, rSELF                    # pass Thread::Current
+    jal    artSet16StaticFromCode        # (field_idx, new_val, referrer, Thread*, $sp)
+    sw     $sp, 16($sp)                  # pass $sp
+    RETURN_IF_ZERO
+END art_quick_set16_static
+
+    /*
      * Called by managed code to resolve a static field and store a 32-bit primitive value.
      */
     .extern artSet32StaticFromCode
@@ -841,7 +976,7 @@
     /*
      * Called by managed code to resolve a static field and store a 64-bit primitive value.
      */
-    .extern artSet32StaticFromCode
+    .extern artSet64StaticFromCode
 ENTRY art_quick_set64_static
     GENERATE_GLOBAL_POINTER
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
@@ -867,6 +1002,34 @@
 END art_quick_set_obj_static
 
     /*
+     * Called by managed code to resolve an instance field and store a 8-bit primitive value.
+     */
+    .extern artSet8InstanceFromCode
+ENTRY art_quick_set8_instance
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a3, 64($sp)                  # pass referrer's Method*
+    sw     rSELF, 16($sp)                # pass Thread::Current
+    jal    artSet8InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*, $sp)
+    sw     $sp, 20($sp)                  # pass $sp
+    RETURN_IF_ZERO
+END art_quick_set8_instance
+
+    /*
+     * Called by managed code to resolve an instance field and store a 16-bit primitive value.
+     */
+    .extern artSet16InstanceFromCode
+ENTRY art_quick_set16_instance
+    GENERATE_GLOBAL_POINTER
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
+    lw     $a3, 64($sp)                  # pass referrer's Method*
+    sw     rSELF, 16($sp)                # pass Thread::Current
+    jal    artSet16InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*, $sp)
+    sw     $sp, 20($sp)                  # pass $sp
+    RETURN_IF_ZERO
+END art_quick_set16_instance
+
+    /*
      * Called by managed code to resolve an instance field and store a 32-bit primitive value.
      */
     .extern artSet32InstanceFromCode
@@ -883,7 +1046,7 @@
     /*
      * Called by managed code to resolve an instance field and store a 64-bit primitive value.
      */
-    .extern artSet32InstanceFromCode
+    .extern artSet64InstanceFromCode
 ENTRY art_quick_set64_instance
     GENERATE_GLOBAL_POINTER
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME     # save callee saves in case of GC
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 864e3f7..6b74a1b 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -309,7 +309,7 @@
         "addl $16, %%esp"           // Pop referrer
         : "=a" (result)
           // Use the result from eax
-        : "a"(arg0), "c"(arg1), "d"(arg2), "D"(code), [referrer]"m"(referrer), [hidden]"r"(hidden)
+        : "a"(arg0), "c"(arg1), "d"(arg2), "D"(code), [referrer]"r"(referrer), [hidden]"m"(hidden)
           // This places code into edi, arg0 into eax, arg1 into ecx, and arg2 into edx
         : "memory");  // clobber.
     // TODO: Should we clobber the other registers? EBX gets clobbered by some of the stubs,
@@ -398,7 +398,7 @@
         // Load call params into the right registers.
         "ldp x0, x1, [sp]\n\t"
         "ldp x2, x3, [sp, #16]\n\t"
-        "ldp x18, x12, [sp, #32]\n\t"
+        "ldp x18, x17, [sp, #32]\n\t"
         "add sp, sp, #48\n\t"
         ".cfi_adjust_cfa_offset -48\n\t"
 
@@ -489,19 +489,17 @@
     // Note: Uses the native convention
     // TODO: Set the thread?
     __asm__ __volatile__(
-        "movq %[hidden], %%r9\n\t"     // No need to save r9, listed as clobbered
-        "movd %%r9, %%xmm0\n\t"
         "pushq %[referrer]\n\t"        // Push referrer
         "pushq (%%rsp)\n\t"            // & 16B alignment padding
         ".cfi_adjust_cfa_offset 16\n\t"
-        "call *%%rax\n\t"              // Call the stub
+        "call *%%rbx\n\t"              // Call the stub
         "addq $16, %%rsp\n\t"          // Pop nullptr and padding
         ".cfi_adjust_cfa_offset -16\n\t"
         : "=a" (result)
         // Use the result from rax
-        : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code), [referrer] "m"(referrer), [hidden] "m"(hidden)
+        : "D"(arg0), "S"(arg1), "d"(arg2), "b"(code), [referrer] "c"(referrer), [hidden] "a"(hidden)
         // This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into rax
-        : "rbx", "rcx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+        : "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
           "memory");  // clobber all
     // TODO: Should we clobber the other registers?
 #else
@@ -1306,6 +1304,259 @@
 }
 
 
+static void GetSetBooleanStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+                           mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 5;
+  uint8_t values[num_values] = { 0, 1, 2, 128, 0xFF };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              static_cast<size_t>(values[i]),
+                              0U,
+                              StubTest::GetEntrypoint(self, kQuickSet8Static),
+                              self,
+                              referrer);
+
+    size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                           0U, 0U,
+                                           StubTest::GetEntrypoint(self, kQuickGetBooleanStatic),
+                                           self,
+                                           referrer);
+    // Boolean currently stores bools as uint8_t, be more zealous about asserting correct writes/gets.
+    EXPECT_EQ(values[i], static_cast<uint8_t>(res)) << "Iteration " << i;
+  }
+#else
+  LOG(INFO) << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetByteStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+                           mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 5;
+  int8_t values[num_values] = { -128, -64, 0, 64, 127 };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              static_cast<size_t>(values[i]),
+                              0U,
+                              StubTest::GetEntrypoint(self, kQuickSet8Static),
+                              self,
+                              referrer);
+
+    size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                           0U, 0U,
+                                           StubTest::GetEntrypoint(self, kQuickGetByteStatic),
+                                           self,
+                                           referrer);
+    EXPECT_EQ(values[i], static_cast<int8_t>(res)) << "Iteration " << i;
+  }
+#else
+  LOG(INFO) << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+
+static void GetSetBooleanInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+                             Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 5;
+  uint8_t values[num_values] = { 0, true, 2, 128, 0xFF };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              reinterpret_cast<size_t>(obj->Get()),
+                              static_cast<size_t>(values[i]),
+                              StubTest::GetEntrypoint(self, kQuickSet8Instance),
+                              self,
+                              referrer);
+
+    uint8_t res = f->Get()->GetBoolean(obj->Get());
+    EXPECT_EQ(values[i], res) << "Iteration " << i;
+
+    f->Get()->SetBoolean<false>(obj->Get(), res);
+
+    size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                            reinterpret_cast<size_t>(obj->Get()),
+                                            0U,
+                                            StubTest::GetEntrypoint(self, kQuickGetBooleanInstance),
+                                            self,
+                                            referrer);
+    EXPECT_EQ(res, static_cast<uint8_t>(res2));
+  }
+#else
+  LOG(INFO) << "Skipping set_boolean_instance as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_boolean_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetByteInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+                             Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 5;
+  int8_t values[num_values] = { -128, -64, 0, 64, 127 };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              reinterpret_cast<size_t>(obj->Get()),
+                              static_cast<size_t>(values[i]),
+                              StubTest::GetEntrypoint(self, kQuickSet8Instance),
+                              self,
+                              referrer);
+
+    int8_t res = f->Get()->GetByte(obj->Get());
+    EXPECT_EQ(res, values[i]) << "Iteration " << i;
+    f->Get()->SetByte<false>(obj->Get(), ++res);
+
+    size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                            reinterpret_cast<size_t>(obj->Get()),
+                                            0U,
+                                            StubTest::GetEntrypoint(self, kQuickGetByteInstance),
+                                            self,
+                                            referrer);
+    EXPECT_EQ(res, static_cast<int8_t>(res2));
+  }
+#else
+  LOG(INFO) << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+static void GetSetCharStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+                           mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 6;
+  uint16_t values[num_values] = { 0, 1, 2, 255, 32768, 0xFFFF };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              static_cast<size_t>(values[i]),
+                              0U,
+                              StubTest::GetEntrypoint(self, kQuickSet16Static),
+                              self,
+                              referrer);
+
+    size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                           0U, 0U,
+                                           StubTest::GetEntrypoint(self, kQuickGetCharStatic),
+                                           self,
+                                           referrer);
+
+    EXPECT_EQ(values[i], static_cast<uint16_t>(res)) << "Iteration " << i;
+  }
+#else
+  LOG(INFO) << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetShortStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+                           mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 6;
+  int16_t values[num_values] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              static_cast<size_t>(values[i]),
+                              0U,
+                              StubTest::GetEntrypoint(self, kQuickSet16Static),
+                              self,
+                              referrer);
+
+    size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                           0U, 0U,
+                                           StubTest::GetEntrypoint(self, kQuickGetShortStatic),
+                                           self,
+                                           referrer);
+
+    EXPECT_EQ(static_cast<int16_t>(res), values[i]) << "Iteration " << i;
+  }
+#else
+  LOG(INFO) << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+static void GetSetCharInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+                             Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 6;
+  uint16_t values[num_values] = { 0, 1, 2, 255, 32768, 0xFFFF };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              reinterpret_cast<size_t>(obj->Get()),
+                              static_cast<size_t>(values[i]),
+                              StubTest::GetEntrypoint(self, kQuickSet16Instance),
+                              self,
+                              referrer);
+
+    uint16_t res = f->Get()->GetChar(obj->Get());
+    EXPECT_EQ(res, values[i]) << "Iteration " << i;
+    f->Get()->SetChar<false>(obj->Get(), ++res);
+
+    size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                            reinterpret_cast<size_t>(obj->Get()),
+                                            0U,
+                                            StubTest::GetEntrypoint(self, kQuickGetCharInstance),
+                                            self,
+                                            referrer);
+    EXPECT_EQ(res, static_cast<uint16_t>(res2));
+  }
+#else
+  LOG(INFO) << "Skipping set_char_instance as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_char_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetShortInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+                             Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  constexpr size_t num_values = 6;
+  int16_t values[num_values] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
+
+  for (size_t i = 0; i < num_values; ++i) {
+    test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                              reinterpret_cast<size_t>(obj->Get()),
+                              static_cast<size_t>(values[i]),
+                              StubTest::GetEntrypoint(self, kQuickSet16Instance),
+                              self,
+                              referrer);
+
+    int16_t res = f->Get()->GetShort(obj->Get());
+    EXPECT_EQ(res, values[i]) << "Iteration " << i;
+    f->Get()->SetShort<false>(obj->Get(), ++res);
+
+    size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+                                            reinterpret_cast<size_t>(obj->Get()),
+                                            0U,
+                                            StubTest::GetEntrypoint(self, kQuickGetShortInstance),
+                                            self,
+                                            referrer);
+    EXPECT_EQ(res, static_cast<int16_t>(res2));
+  }
+#else
+  LOG(INFO) << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
 static void GetSet32Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
                            mirror::ArtMethod* referrer, StubTest* test)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1555,6 +1806,26 @@
 
       Primitive::Type type = f->GetTypeAsPrimitiveType();
       switch (type) {
+        case Primitive::Type::kPrimBoolean:
+          if (test_type == type) {
+            GetSetBooleanStatic(&obj, &f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimByte:
+          if (test_type == type) {
+            GetSetByteStatic(&obj, &f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimChar:
+          if (test_type == type) {
+            GetSetCharStatic(&obj, &f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimShort:
+          if (test_type == type) {
+            GetSetShortStatic(&obj, &f, self, m.Get(), test);
+          }
+          break;
         case Primitive::Type::kPrimInt:
           if (test_type == type) {
             GetSet32Static(&obj, &f, self, m.Get(), test);
@@ -1590,6 +1861,26 @@
 
       Primitive::Type type = f->GetTypeAsPrimitiveType();
       switch (type) {
+        case Primitive::Type::kPrimBoolean:
+          if (test_type == type) {
+            GetSetBooleanInstance(&obj, &f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimByte:
+          if (test_type == type) {
+            GetSetByteInstance(&obj, &f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimChar:
+          if (test_type == type) {
+            GetSetCharInstance(&obj, &f, self, m.Get(), test);
+          }
+          break;
+        case Primitive::Type::kPrimShort:
+          if (test_type == type) {
+            GetSetShortInstance(&obj, &f, self, m.Get(), test);
+          }
+          break;
         case Primitive::Type::kPrimInt:
           if (test_type == type) {
             GetSet32Instance(&obj, &f, self, m.Get(), test);
@@ -1618,6 +1909,33 @@
   // TODO: Deallocate things.
 }
 
+TEST_F(StubTest, Fields8) {
+  TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+
+  Thread* self = Thread::Current();
+
+  self->TransitionFromSuspendedToRunnable();
+  LoadDex("AllFields");
+  bool started = runtime_->Start();
+  CHECK(started);
+
+  TestFields(self, this, Primitive::Type::kPrimBoolean);
+  TestFields(self, this, Primitive::Type::kPrimByte);
+}
+
+TEST_F(StubTest, Fields16) {
+  TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+
+  Thread* self = Thread::Current();
+
+  self->TransitionFromSuspendedToRunnable();
+  LoadDex("AllFields");
+  bool started = runtime_->Start();
+  CHECK(started);
+
+  TestFields(self, this, Primitive::Type::kPrimChar);
+  TestFields(self, this, Primitive::Type::kPrimShort);
+}
 
 TEST_F(StubTest, Fields32) {
   TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
@@ -1658,7 +1976,6 @@
   TestFields(self, this, Primitive::Type::kPrimLong);
 }
 
-
 TEST_F(StubTest, IMT) {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
   TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
@@ -1709,19 +2026,6 @@
   jmethodID obj_constructor = env->GetMethodID(obj_jclass, "<init>", "()V");
   ASSERT_NE(nullptr, obj_constructor);
 
-  // Sanity check: check that there is a conflict for List.contains in ArrayList.
-
-  mirror::Class* arraylist_class = soa.Decode<mirror::Class*>(arraylist_jclass);
-  mirror::ArtMethod* m = arraylist_class->GetEmbeddedImTableEntry(
-      inf_contains->GetDexMethodIndex() % mirror::Class::kImtSize);
-
-  if (!m->IsImtConflictMethod()) {
-    LOG(WARNING) << "Test is meaningless, no IMT conflict in setup: " <<
-        PrettyMethod(m, true);
-    LOG(WARNING) << "Please update StubTest.IMT.";
-    return;
-  }
-
   // Create instances.
 
   jobject jarray_list = env->NewObject(arraylist_jclass, arraylist_constructor);
@@ -1732,7 +2036,11 @@
   ASSERT_NE(nullptr, jobj);
   Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(jobj)));
 
-  // Invoke.
+  // Invocation tests.
+
+  // 1. imt_conflict
+
+  // Contains.
 
   size_t result =
       Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
@@ -1750,7 +2058,7 @@
 
   ASSERT_FALSE(self->IsExceptionPending()) << PrettyTypeOf(self->GetException(nullptr));
 
-  // Invoke again.
+  // Contains.
 
   result = Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
                                         reinterpret_cast<size_t>(obj.Get()),
@@ -1760,6 +2068,28 @@
 
   ASSERT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
+
+  // 2. regular interface trampoline
+
+  result = Invoke3WithReferrer(static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()),
+                               reinterpret_cast<size_t>(array_list.Get()),
+                               reinterpret_cast<size_t>(obj.Get()),
+                               StubTest::GetEntrypoint(self,
+                                   kQuickInvokeInterfaceTrampolineWithAccessCheck),
+                               self, contains_amethod.Get());
+
+  ASSERT_FALSE(self->IsExceptionPending());
+  EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
+
+  result = Invoke3WithReferrer(static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()),
+                               reinterpret_cast<size_t>(array_list.Get()),
+                               reinterpret_cast<size_t>(array_list.Get()),
+                               StubTest::GetEntrypoint(self,
+                                   kQuickInvokeInterfaceTrampolineWithAccessCheck),
+                               self, contains_amethod.Get());
+
+  ASSERT_FALSE(self->IsExceptionPending());
+  EXPECT_EQ(static_cast<size_t>(JNI_FALSE), result);
 #else
   LOG(INFO) << "Skipping imt as I don't know how to do that on " << kRuntimeISA;
   // Force-print to std::cout so it's also outside the logcat.
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index a578023..efbbfb3 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -181,13 +181,4 @@
 #endif
 END_MACRO
 
-MACRO0(SETUP_GOT)
-    PUSH  ebx
-    SETUP_GOT_NOSAVE
-END_MACRO
-
-MACRO0(UNDO_SETUP_GOT)
-    POP  ebx
-END_MACRO
-
 #endif  // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_S_
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index a072996..682c502 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -47,12 +47,24 @@
 extern "C" void* art_quick_resolve_string(void*, uint32_t);
 
 // Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
 extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
 extern "C" int art_quick_set32_static(uint32_t, int32_t);
 extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
 extern "C" int art_quick_set64_static(uint32_t, int64_t);
 extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
 extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
 extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
 extern "C" int32_t art_quick_get32_static(uint32_t);
 extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -137,15 +149,27 @@
   qpoints->pResolveString = art_quick_resolve_string;
 
   // Field
+  qpoints->pSet8Instance = art_quick_set8_instance;
+  qpoints->pSet8Static = art_quick_set8_static;
+  qpoints->pSet16Instance = art_quick_set16_instance;
+  qpoints->pSet16Static = art_quick_set16_static;
   qpoints->pSet32Instance = art_quick_set32_instance;
   qpoints->pSet32Static = art_quick_set32_static;
   qpoints->pSet64Instance = art_quick_set64_instance;
   qpoints->pSet64Static = art_quick_set64_static;
   qpoints->pSetObjInstance = art_quick_set_obj_instance;
   qpoints->pSetObjStatic = art_quick_set_obj_static;
+  qpoints->pGetByteInstance = art_quick_get_byte_instance;
+  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+  qpoints->pGetShortInstance = art_quick_get_short_instance;
+  qpoints->pGetCharInstance = art_quick_get_char_instance;
   qpoints->pGet32Instance = art_quick_get32_instance;
   qpoints->pGet64Instance = art_quick_get64_instance;
   qpoints->pGetObjInstance = art_quick_get_obj_instance;
+  qpoints->pGetByteStatic = art_quick_get_byte_static;
+  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+  qpoints->pGetShortStatic = art_quick_get_short_static;
+  qpoints->pGetCharStatic = art_quick_get_char_static;
   qpoints->pGet32Static = art_quick_get32_static;
   qpoints->pGet64Static = art_quick_get64_static;
   qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index fb26f5f..17310b6 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -104,11 +104,17 @@
   bool two_byte = false;
   uint32_t displacement_size = 0;
   uint32_t immediate_size = 0;
+  bool operand_size_prefix = false;
 
   // Prefixes.
   while (true) {
     bool prefix_present = false;
     switch (opcode) {
+      // Group 3
+      case 0x66:
+        operand_size_prefix = true;
+        // fallthrough
+
       // Group 1
       case 0xf0:
       case 0xf2:
@@ -122,9 +128,6 @@
       case 0x64:
       case 0x65:
 
-      // Group 3
-      case 0x66:
-
       // Group 4
       case 0x67:
         opcode = *pc++;
@@ -150,8 +153,8 @@
 
   if (two_byte) {
     switch (opcode) {
-      case 0x10:            // vmovsd/ss
-      case 0x11:            // vmovsd/ss
+      case 0x10:        // vmovsd/ss
+      case 0x11:        // vmovsd/ss
       case 0xb6:        // movzx
       case 0xb7:
       case 0xbe:        // movsx
@@ -165,7 +168,8 @@
     }
   } else {
     switch (opcode) {
-      case 0x89:            // mov
+      case 0x88:        // mov byte
+      case 0x89:        // mov
       case 0x8b:
       case 0x38:        // cmp with memory.
       case 0x39:
@@ -188,7 +192,7 @@
       case 0x81:        // group 1, word immediate.
         modrm = *pc++;
         has_modrm = true;
-        immediate_size = 4;
+        immediate_size = operand_size_prefix ? 2 : 4;
         break;
 
       default:
@@ -203,18 +207,18 @@
   }
 
   if (has_modrm) {
-    uint8_t mod = (modrm >> 6) & 0b11;
+    uint8_t mod = (modrm >> 6) & 3U /* 0b11 */;
 
     // Check for SIB.
-    if (mod != 0b11 && (modrm & 0b111) == 4) {
+    if (mod != 3U /* 0b11 */ && (modrm & 7U /* 0b111 */) == 4) {
       ++pc;     // SIB
     }
 
     switch (mod) {
-      case 0b00: break;
-      case 0b01: displacement_size = 1; break;
-      case 0b10: displacement_size = 4; break;
-      case 0b11:
+      case 0U /* 0b00 */: break;
+      case 1U /* 0b01 */: displacement_size = 1; break;
+      case 2U /* 0b10 */: displacement_size = 4; break;
+      case 3U /* 0b11 */:
         break;
     }
   }
diff --git a/runtime/arch/x86/jni_entrypoints_x86.S b/runtime/arch/x86/jni_entrypoints_x86.S
index 997a259..5d27e47 100644
--- a/runtime/arch/x86/jni_entrypoints_x86.S
+++ b/runtime/arch/x86/jni_entrypoints_x86.S
@@ -20,18 +20,13 @@
      * Jni dlsym lookup stub.
      */
 DEFINE_FUNCTION art_jni_dlsym_lookup_stub
-    subl LITERAL(4), %esp         // align stack
-    CFI_ADJUST_CFA_OFFSET(4)
-    SETUP_GOT                     // pushes ebx
+    subl LITERAL(8), %esp         // align stack
+    CFI_ADJUST_CFA_OFFSET(8)
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    CFI_ADJUST_CFA_OFFSET(4)
-    call PLT_SYMBOL(artFindNativeMethod)  // (Thread*)
-    addl LITERAL(4), %esp         // remove argument
-    CFI_ADJUST_CFA_OFFSET(-4)
-    UNDO_SETUP_GOT                // pop ebx
-    addl LITERAL(4), %esp         // remove padding
-    CFI_ADJUST_CFA_OFFSET(-4)
-    testl %eax, %eax         // check if returned method code is null
+    call SYMBOL(artFindNativeMethod)  // (Thread*)
+    addl LITERAL(12), %esp        // remove argument & padding
+    CFI_ADJUST_CFA_OFFSET(-12)
+    testl %eax, %eax              // check if returned method code is null
     jz .Lno_native_code_found     // if null, jump to return to handle
     jmp *%eax                     // otherwise, tail call to intended method
 .Lno_native_code_found:
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
index 9365795..f5fe869 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -70,8 +70,7 @@
     PUSH ebp                        // Set up frame.
     movl %esp, %ebp
     CFI_DEF_CFA_REGISTER(%ebp)
-    subl LITERAL(4), %esp           // Align stack
-    SETUP_GOT                       // pushes ebx
+    subl LITERAL(8), %esp           // Align stack
     leal 8(%ebp), %edx              // %edx = ArtMethod** called_addr
     movl 12(%ebp), %ecx             // %ecx = receiver
     movl 0(%edx), %eax              // %eax = ArtMethod* called
@@ -79,8 +78,7 @@
     pushl %fs:THREAD_SELF_OFFSET    // Pass thread.
     pushl %ecx                      // Pass receiver.
     pushl %eax                      // Pass called.
-    call PLT_SYMBOL(artPortableProxyInvokeHandler)  // (called, receiver, Thread*, &called)
-    UNDO_SETUP_GOT
+    call SYMBOL(artPortableProxyInvokeHandler)  // (called, receiver, Thread*, &called)
     leave
     CFI_RESTORE(%ebp)
     CFI_DEF_CFA(%esp, 4)
@@ -94,8 +92,7 @@
   PUSH ebp                        // Set up frame.
   movl %esp, %ebp
   CFI_DEF_CFA_REGISTER(%ebp)
-  subl LITERAL(4), %esp           // Align stack
-  SETUP_GOT                       // pushes ebx
+  subl LITERAL(8), %esp           // Align stack
   leal 8(%ebp), %edx              // %edx = ArtMethod** called_addr
   movl 12(%ebp), %ecx             // %ecx = receiver
   movl 0(%edx), %eax              // %eax = ArtMethod* called
@@ -103,8 +100,7 @@
   pushl %fs:THREAD_SELF_OFFSET    // Pass thread.
   pushl %ecx                      // Pass receiver.
   pushl %eax                      // Pass called.
-  call PLT_SYMBOL(artPortableResolutionTrampoline)  // (called, receiver, Thread*, &called)
-  UNDO_SETUP_GOT
+  call SYMBOL(artPortableResolutionTrampoline)  // (called, receiver, Thread*, &called)
   leave
   CFI_RESTORE(%ebp)
   CFI_DEF_CFA(%esp, 4)
@@ -119,15 +115,13 @@
   PUSH ebp                        // Set up frame.
   movl %esp, %ebp
   CFI_DEF_CFA_REGISTER(%ebp)
-  subl LITERAL(8), %esp           // Align stack
-  SETUP_GOT
+  subl LITERAL(12), %esp           // Align stack
   leal 8(%ebp), %edx              // %edx = ArtMethod** called_addr
   movl 0(%edx), %eax              // %eax = ArtMethod* called
   pushl %edx                      // Pass called_addr.
   pushl %fs:THREAD_SELF_OFFSET    // Pass thread.
   pushl %eax                      // Pass called.
-  call PLT_SYMBOL(artPortableToInterpreterBridge)  // (called, Thread*, &called)
-  UNDO_SETUP_GOT
+  call SYMBOL(artPortableToInterpreterBridge)  // (called, Thread*, &called)
   leave
   CFI_RESTORE(%ebp)
   CFI_DEF_CFA(%esp, 4)
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 75c8646..4155b7e 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -105,7 +105,6 @@
     PUSH ecx                                 // pass SP
     pushl %fs:THREAD_SELF_OFFSET             // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    SETUP_GOT_NOSAVE                         // clobbers ebx (harmless here)
     call SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*, SP)
     int3                                     // unreached
 END_MACRO
@@ -120,7 +119,6 @@
     PUSH ecx                      // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
     call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
@@ -136,7 +134,6 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass arg1
-    SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
     call VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
@@ -152,7 +149,6 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
     call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, Thread*, SP)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
@@ -219,7 +215,6 @@
     PUSH eax    // <-- callee save Method* to go here
     movl %esp, %edx  // remember SP
     // Outgoing argument set up
-    SETUP_GOT_NOSAVE
     subl MACRO_LITERAL(12), %esp  // alignment padding
     CFI_ADJUST_CFA_OFFSET(12)
     PUSH edx                      // pass SP
@@ -318,7 +313,6 @@
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
     mov %esp, %edx                // remember SP
-    SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
     // Outgoing argument set up
     subl MACRO_LITERAL(8), %esp   // push padding
     CFI_ADJUST_CFA_OFFSET(8)
@@ -337,7 +331,6 @@
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
     mov %esp, %edx                // remember SP
-    SETUP_GOT_NOSAVE              // clobbers EBX
     // Outgoing argument set up
     PUSH eax                      // push padding
     PUSH edx                      // pass SP
@@ -356,7 +349,6 @@
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
     mov %esp, %edx                // remember SP
-    SETUP_GOT_NOSAVE              // clobbers EBX
     // Outgoing argument set up
     PUSH edx                      // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
@@ -384,7 +376,6 @@
     PUSH edx                      // pass arg3
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, arg3, Thread*, SP)
     addl MACRO_LITERAL(32), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -393,6 +384,48 @@
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
 
+MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
+    DEFINE_FUNCTION RAW_VAR(c_name, 0)
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
+    mov %esp, %edx                // remember SP
+    mov 32(%esp), %ecx            // get referrer
+    // Outgoing argument set up
+    PUSH edx                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH ecx                      // pass referrer
+    PUSH eax                      // pass arg1
+    call VAR(cxx_name, 1)         // cxx_name(arg1, referrer, Thread*, SP)
+    addl MACRO_LITERAL(16), %esp  // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-16)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    CALL_MACRO(return_macro, 2)   // return or deliver exception
+    END_FUNCTION RAW_VAR(c_name, 0)
+END_MACRO
+
+MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
+    DEFINE_FUNCTION RAW_VAR(c_name, 0)
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
+    mov %esp, %ebx                // remember SP
+    mov 32(%esp), %edx            // get referrer
+    subl MACRO_LITERAL(12), %esp  // alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
+    PUSH ebx                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    // Outgoing argument set up
+    PUSH edx                      // pass referrer
+    PUSH ecx                      // pass arg2
+    PUSH eax                      // pass arg1
+    call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, referrer, Thread*, SP)
+    addl MACRO_LITERAL(32), %esp  // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-32)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
+    CALL_MACRO(return_macro, 2)   // return or deliver exception
+    END_FUNCTION RAW_VAR(c_name, 0)
+END_MACRO
+
+
 MACRO0(RETURN_IF_RESULT_IS_NON_ZERO)
     testl %eax, %eax               // eax == 0 ?
     jz  1f                         // if eax == 0 goto 1
@@ -559,7 +592,6 @@
 .Lslow_lock:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
     mov %esp, %edx                // remember SP
-    SETUP_GOT_NOSAVE              // clobbers EBX
     // Outgoing argument set up
     PUSH eax                      // push padding
     PUSH edx                      // pass SP
@@ -593,7 +625,6 @@
 .Lslow_unlock:
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
     mov %esp, %edx                // remember SP
-    SETUP_GOT_NOSAVE              // clobbers EBX
     // Outgoing argument set up
     PUSH eax                      // push padding
     PUSH edx                      // pass SP
@@ -608,7 +639,6 @@
 END_FUNCTION art_quick_unlock_object
 
 DEFINE_FUNCTION art_quick_is_assignable
-    SETUP_GOT_NOSAVE             // clobbers EBX
     PUSH eax                     // alignment padding
     PUSH ecx                     // pass arg2 - obj->klass
     PUSH eax                     // pass arg1 - checked class
@@ -619,7 +649,6 @@
 END_FUNCTION art_quick_is_assignable
 
 DEFINE_FUNCTION art_quick_check_cast
-    SETUP_GOT_NOSAVE             // clobbers EBX
     PUSH eax                     // alignment padding
     PUSH ecx                     // pass arg2 - obj->klass
     PUSH eax                     // pass arg1 - checked class
@@ -691,7 +720,6 @@
     pushl CLASS_OFFSET(%edx)     // pass arg2 - type of the value to be stored
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ebx                     // pass arg1 - component type of the array
-    SETUP_GOT_NOSAVE             // clobbers EBX
     call SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
     addl LITERAL(16), %esp       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
@@ -738,7 +766,6 @@
     PUSH eax                      // alignment padding
     PUSH ecx                      // pass arg2 a.hi
     PUSH eax                      // pass arg1 a.lo
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(art_d2l)      // (jdouble a)
     addl LITERAL(12), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-12)
@@ -748,7 +775,6 @@
 DEFINE_FUNCTION art_quick_f2l
     subl LITERAL(8), %esp         // alignment padding
     CFI_ADJUST_CFA_OFFSET(8)
-    SETUP_GOT_NOSAVE              // clobbers EBX
     PUSH eax                      // pass arg1 a
     call SYMBOL(art_f2l)      // (jfloat a)
     addl LITERAL(12), %esp        // pop arguments
@@ -763,7 +789,6 @@
     PUSH edx                     // pass arg3 b.lo
     PUSH ecx                     // pass arg2 a.hi
     PUSH eax                     // pass arg1 a.lo
-    SETUP_GOT_NOSAVE             // clobbers EBX
     call SYMBOL(artLdiv)     // (jlong a, jlong b)
     addl LITERAL(28), %esp       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-28)
@@ -777,7 +802,6 @@
     PUSH edx                     // pass arg3 b.lo
     PUSH ecx                     // pass arg2 a.hi
     PUSH eax                     // pass arg1 a.lo
-    SETUP_GOT_NOSAVE             // clobbers EBX
     call SYMBOL(artLmod)     // (jlong a, jlong b)
     addl LITERAL(28), %esp       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-28)
@@ -832,6 +856,46 @@
     ret
 END_FUNCTION art_quick_lushr
 
+DEFINE_FUNCTION art_quick_set8_instance
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
+    mov %esp, %ebx                // remember SP
+    subl LITERAL(8), %esp         // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
+    PUSH ebx                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    mov 32(%ebx), %ebx            // get referrer
+    PUSH ebx                      // pass referrer
+    PUSH edx                      // pass new_val
+    PUSH ecx                      // pass object
+    PUSH eax                      // pass field_idx
+    call PLT_SYMBOL(artSet8InstanceFromCode)  // (field_idx, Object*, new_val, referrer, Thread*, SP)
+    addl LITERAL(32), %esp        // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-32)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
+    RETURN_IF_EAX_ZERO            // return or deliver exception
+END_FUNCTION art_quick_set8_instance
+
+DEFINE_FUNCTION art_quick_set16_instance
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
+    mov %esp, %ebx                // remember SP
+    subl LITERAL(8), %esp         // alignment padding
+    CFI_ADJUST_CFA_OFFSET(8)
+    PUSH ebx                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    mov 32(%ebx), %ebx            // get referrer
+    PUSH ebx                      // pass referrer
+    PUSH edx                      // pass new_val
+    PUSH ecx                      // pass object
+    PUSH eax                      // pass field_idx
+    call PLT_SYMBOL(artSet16InstanceFromCode)  // (field_idx, Object*, new_val, referrer, Thread*, SP)
+    addl LITERAL(32), %esp        // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-32)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
+    RETURN_IF_EAX_ZERO            // return or deliver exception
+END_FUNCTION art_quick_set16_instance
+
 DEFINE_FUNCTION art_quick_set32_instance
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
     mov %esp, %ebx                // remember SP
@@ -845,7 +909,6 @@
     PUSH edx                      // pass new_val
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artSet32InstanceFromCode)  // (field_idx, Object*, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -865,7 +928,6 @@
     PUSH edx                      // pass low half of new_val
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artSet64InstanceFromCode)  // (field_idx, Object*, new_val, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -886,7 +948,6 @@
     PUSH edx                      // pass new_val
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artSetObjInstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -894,25 +955,12 @@
     RETURN_IF_EAX_ZERO            // return or deliver exception
 END_FUNCTION art_quick_set_obj_instance
 
-DEFINE_FUNCTION art_quick_get32_instance
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %ebx                // remember SP
-    mov 32(%esp), %edx            // get referrer
-    subl LITERAL(12), %esp        // alignment padding
-    CFI_ADJUST_CFA_OFFSET(12)
-    PUSH ebx                      // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    CFI_ADJUST_CFA_OFFSET(4)
-    PUSH edx                      // pass referrer
-    PUSH ecx                      // pass object
-    PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
-    call SYMBOL(artGet32InstanceFromCode)  // (field_idx, Object*, referrer, Thread*, SP)
-    addl LITERAL(32), %esp        // pop arguments
-    CFI_ADJUST_CFA_OFFSET(-32)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
-    RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
-END_FUNCTION art_quick_get32_instance
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 
 DEFINE_FUNCTION art_quick_get64_instance
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
@@ -926,7 +974,6 @@
     PUSH edx                      // pass referrer
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artGet64InstanceFromCode)  // (field_idx, Object*, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -934,7 +981,7 @@
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 END_FUNCTION art_quick_get64_instance
 
-DEFINE_FUNCTION art_quick_get_obj_instance
+DEFINE_FUNCTION art_quick_set8_static
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
     mov %esp, %ebx                // remember SP
     mov 32(%esp), %edx            // get referrer
@@ -944,15 +991,33 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH edx                      // pass referrer
-    PUSH ecx                      // pass object
+    PUSH ecx                      // pass new_val
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
-    call SYMBOL(artGetObjInstanceFromCode) // (field_idx, Object*, referrer, Thread*, SP)
+    call SYMBOL(artSet8StaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
-    RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
-END_FUNCTION art_quick_get_obj_instance
+    RETURN_IF_EAX_ZERO            // return or deliver exception
+END_FUNCTION art_quick_set8_static
+
+DEFINE_FUNCTION art_quick_set16_static
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
+    mov %esp, %ebx                // remember SP
+    mov 32(%esp), %edx            // get referrer
+    subl LITERAL(12), %esp        // alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
+    PUSH ebx                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH edx                      // pass referrer
+    PUSH ecx                      // pass new_val
+    PUSH eax                      // pass field_idx
+    call SYMBOL(artSet16StaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
+    addl LITERAL(32), %esp        // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-32)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
+    RETURN_IF_EAX_ZERO            // return or deliver exception
+END_FUNCTION art_quick_set16_static
 
 DEFINE_FUNCTION art_quick_set32_static
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
@@ -966,7 +1031,6 @@
     PUSH edx                      // pass referrer
     PUSH ecx                      // pass new_val
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artSet32StaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -987,7 +1051,6 @@
     PUSH ecx                      // pass low half of new_val
     PUSH ebx                      // pass referrer
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -1007,63 +1070,19 @@
     PUSH edx                      // pass referrer
     PUSH ecx                      // pass new_val
     PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artSetObjStaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     RETURN_IF_EAX_ZERO            // return or deliver exception
 END_FUNCTION art_quick_set_obj_static
 
-DEFINE_FUNCTION art_quick_get32_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    mov 32(%esp), %ecx            // get referrer
-    PUSH edx                      // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    CFI_ADJUST_CFA_OFFSET(4)
-    PUSH ecx                      // pass referrer
-    PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
-    call SYMBOL(artGet32StaticFromCode)    // (field_idx, referrer, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
-    RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
-END_FUNCTION art_quick_get32_static
-
-DEFINE_FUNCTION art_quick_get64_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    mov 32(%esp), %ecx            // get referrer
-    PUSH edx                      // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    CFI_ADJUST_CFA_OFFSET(4)
-    PUSH ecx                      // pass referrer
-    PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
-    call SYMBOL(artGet64StaticFromCode)    // (field_idx, referrer, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
-    RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
-END_FUNCTION art_quick_get64_static
-
-DEFINE_FUNCTION art_quick_get_obj_static
-    SETUP_REF_ONLY_CALLEE_SAVE_FRAME       // save ref containing registers for GC
-    mov %esp, %edx                // remember SP
-    mov 32(%esp), %ecx            // get referrer
-    PUSH edx                      // pass SP
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    CFI_ADJUST_CFA_OFFSET(4)
-    PUSH ecx                      // pass referrer
-    PUSH eax                      // pass field_idx
-    SETUP_GOT_NOSAVE              // clobbers EBX
-    call SYMBOL(artGetObjStaticFromCode)   // (field_idx, referrer, Thread*, SP)
-    addl LITERAL(16), %esp        // pop arguments
-    CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
-    RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
-END_FUNCTION art_quick_get_obj_static
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 
 DEFINE_FUNCTION art_quick_proxy_invoke_handler
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame and Method*
@@ -1072,7 +1091,6 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass receiver
     PUSH eax                      // pass proxy method
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
     movd %eax, %xmm0              // place return value also into floating point return value
     movd %edx, %xmm1
@@ -1104,7 +1122,6 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass receiver
     PUSH eax                      // pass method
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artQuickResolutionTrampoline) // (Method* called, receiver, Thread*, SP)
     movl %eax, %edi               // remember code pointer in EDI
     addl LITERAL(16), %esp        // pop arguments
@@ -1139,7 +1156,6 @@
     subl LITERAL(8), %esp         // Padding for 16B alignment.
     pushl %ebp                    // Pass SP (to ArtMethod).
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
-    SETUP_GOT_NOSAVE              // Clobbers ebx.
     call SYMBOL(artQuickGenericJniTrampoline)  // (Thread*, sp)
 
     // The C call will have registered the complete save-frame on success.
@@ -1212,7 +1228,6 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass  method
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
     movd %eax, %xmm0              // place return value also into floating point return value
     movd %edx, %xmm1
@@ -1239,8 +1254,8 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // Pass receiver.
     PUSH eax                      // Pass Method*.
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+    SETUP_GOT_NOSAVE
     addl LITERAL(28), %esp        // Pop arguments upto saved Method*.
     movl 28(%esp), %edi           // Restore edi.
     movl %eax, 28(%esp)           // Place code* over edi, just under return pc.
@@ -1274,7 +1289,6 @@
     PUSH ecx                      // Pass SP.
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current.
     CFI_ADJUST_CFA_OFFSET(4)
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artInstrumentationMethodExitFromCode)  // (Thread*, SP, gpr_result, fpr_result)
     mov   %eax, %ecx              // Move returned link register.
     addl LITERAL(32), %esp        // Pop arguments.
@@ -1304,7 +1318,6 @@
     PUSH ecx                      // Pass SP.
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
     CFI_ADJUST_CFA_OFFSET(4)
-    SETUP_GOT_NOSAVE              // clobbers EBX
     call SYMBOL(artDeoptimize)  // artDeoptimize(Thread*, SP)
     int3                          // Unreachable.
 END_FUNCTION art_quick_deoptimize
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index f7acbdb..4ae61a2 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -119,6 +119,8 @@
     .balign 16
 END_MACRO
 
+// TODO: we might need to use SYMBOL() here to add the underscore prefix
+// for mac builds.
 MACRO1(DEFINE_FUNCTION, c_name)
     FUNCTION_TYPE(\c_name, 0)
     ASM_HIDDEN VAR(c_name, 0)
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 35a0cf4..c9028e1 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -48,12 +48,24 @@
 extern "C" void* art_quick_resolve_string(void*, uint32_t);
 
 // Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
 extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
 extern "C" int art_quick_set32_static(uint32_t, int32_t);
 extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
 extern "C" int art_quick_set64_static(uint32_t, int64_t);
 extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
 extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
 extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
 extern "C" int32_t art_quick_get32_static(uint32_t);
 extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -141,15 +153,27 @@
   qpoints->pResolveString = art_quick_resolve_string;
 
   // Field
+  qpoints->pSet8Instance = art_quick_set8_instance;
+  qpoints->pSet8Static = art_quick_set8_static;
+  qpoints->pSet16Instance = art_quick_set16_instance;
+  qpoints->pSet16Static = art_quick_set16_static;
   qpoints->pSet32Instance = art_quick_set32_instance;
   qpoints->pSet32Static = art_quick_set32_static;
   qpoints->pSet64Instance = art_quick_set64_instance;
   qpoints->pSet64Static = art_quick_set64_static;
   qpoints->pSetObjInstance = art_quick_set_obj_instance;
   qpoints->pSetObjStatic = art_quick_set_obj_static;
+  qpoints->pGetByteInstance = art_quick_get_byte_instance;
+  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+  qpoints->pGetShortInstance = art_quick_get_short_instance;
+  qpoints->pGetCharInstance = art_quick_get_char_instance;
   qpoints->pGet32Instance = art_quick_get32_instance;
   qpoints->pGet64Instance = art_quick_get64_instance;
   qpoints->pGetObjInstance = art_quick_get_obj_instance;
+  qpoints->pGetByteStatic = art_quick_get_byte_static;
+  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+  qpoints->pGetShortStatic = art_quick_get_short_static;
+  qpoints->pGetCharStatic = art_quick_get_char_static;
   qpoints->pGet32Static = art_quick_get32_static;
   qpoints->pGet64Static = art_quick_get64_static;
   qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 5798092..e68cfbc 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1076,17 +1076,29 @@
 UNIMPLEMENTED art_quick_lshr
 UNIMPLEMENTED art_quick_lushr
 
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_EAX_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_EAX_ZERO
 THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_EAX_ZERO
 THREE_ARG_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_EAX_ZERO
 THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_EAX_ZERO
 
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCode, RETURN_IF_EAX_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCode, RETURN_IF_EAX_ZERO
 TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_EAX_ZERO
 TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_EAX_ZERO
 
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
 ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5978443..62f3593 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -27,7 +27,7 @@
 #define CLASS_OFFSET 0
 #define LOCK_WORD_OFFSET 4
 
-#if !defined(USE_BAKER_OR_BROOKS_READ_BARRIER)
+#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER
 
 // Offsets within java.lang.Class.
 #define CLASS_COMPONENT_TYPE_OFFSET 12
@@ -44,13 +44,8 @@
 
 // Offsets within java.lang.Method.
 #define METHOD_DEX_CACHE_METHODS_OFFSET 12
-#if defined(ART_USE_PORTABLE_COMPILER)
 #define METHOD_PORTABLE_CODE_OFFSET 40
 #define METHOD_QUICK_CODE_OFFSET 48
-#else
-#define METHOD_PORTABLE_CODE_OFFSET 40
-#define METHOD_QUICK_CODE_OFFSET 40
-#endif  // ART_USE_PORTABLE_COMPILER
 
 #else
 
@@ -72,6 +67,6 @@
 #define METHOD_PORTABLE_CODE_OFFSET 48
 #define METHOD_QUICK_CODE_OFFSET 56
 
-#endif  // USE_BAKER_OR_BROOKS_READ_BARRIER
+#endif
 
 #endif  // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 5f43bec..b8edad3 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -57,17 +57,19 @@
   }
 }
 
-void Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) {
+bool Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) {
   MutexLock mu(self, lock_);
   SetCountLocked(self, count_ + delta);
+  bool timed_out = false;
   if (count_ != 0) {
-    condition_.TimedWait(self, timeout_ms, 0);
+    timed_out = condition_.TimedWait(self, timeout_ms, 0);
   }
+  return timed_out;
 }
 
 void Barrier::SetCountLocked(Thread* self, int count) {
   count_ = count;
-  if (count_ == 0) {
+  if (count == 0) {
     condition_.Broadcast(self);
   }
 }
diff --git a/runtime/barrier.h b/runtime/barrier.h
index a433cac..167e1d6 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -38,10 +38,11 @@
   void Init(Thread* self, int count);
 
   // Increment the count by delta, wait on condition if count is non zero.
-  void Increment(Thread* self, int delta);
+  void Increment(Thread* self, int delta) LOCKS_EXCLUDED(lock_);
 
-  // Increment the count by delta, wait on condition if count is non zero, with a timeout
-  void Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_);
+  // Increment the count by delta, wait on condition if count is non zero, with a timeout. Returns
+  // true if time out occurred.
+  bool Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_);
 
  private:
   void SetCountLocked(Thread* self, int count) EXCLUSIVE_LOCKS_REQUIRED(lock_);
diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc
index 39d51a5..64cdbbf 100644
--- a/runtime/base/allocator.cc
+++ b/runtime/base/allocator.cc
@@ -29,7 +29,7 @@
 Atomic<uint64_t> TrackedAllocators::max_bytes_used_[kAllocatorTagCount];
 Atomic<uint64_t> TrackedAllocators::total_bytes_used_[kAllocatorTagCount];
 
-class MallocAllocator : public Allocator {
+class MallocAllocator FINAL : public Allocator {
  public:
   explicit MallocAllocator() {}
   ~MallocAllocator() {}
@@ -48,7 +48,7 @@
 
 MallocAllocator g_malloc_allocator;
 
-class NoopAllocator : public Allocator {
+class NoopAllocator FINAL : public Allocator {
  public:
   explicit NoopAllocator() {}
   ~NoopAllocator() {}
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index a7adb02..2c3e966 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -66,6 +66,7 @@
   kAllocatorTagCompileTimeClassPath,
   kAllocatorTagOatFile,
   kAllocatorTagDexFileVerifier,
+  kAllocatorTagRosAlloc,
   kAllocatorTagCount,  // Must always be last element.
 };
 std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag);
@@ -149,6 +150,10 @@
     Key, T, Compare, TrackingAllocator<std::pair<Key, T>, kTag>> {
 };
 
+template<class Key, AllocatorTag kTag, class Compare = std::less<Key>>
+class AllocationTrackingSet : public std::set<Key, Compare, TrackingAllocator<Key, kTag>> {
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_ALLOCATOR_H_
diff --git a/runtime/base/bit_vector-inl.h b/runtime/base/bit_vector-inl.h
new file mode 100644
index 0000000..dc13dd5
--- /dev/null
+++ b/runtime/base/bit_vector-inl.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
+#define ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
+
+#include "bit_vector.h"
+#include "logging.h"
+#include "utils.h"
+
+namespace art {
+
+inline bool BitVector::IndexIterator::operator==(const IndexIterator& other) const {
+  DCHECK(bit_storage_ == other.bit_storage_);
+  DCHECK_EQ(storage_size_, other.storage_size_);
+  return bit_index_ == other.bit_index_;
+}
+
+inline int BitVector::IndexIterator::operator*() const {
+  DCHECK_LT(bit_index_, BitSize());
+  return bit_index_;
+}
+
+inline BitVector::IndexIterator& BitVector::IndexIterator::operator++() {
+  DCHECK_LT(bit_index_, BitSize());
+  bit_index_ = FindIndex(bit_index_ + 1u);
+  return *this;
+}
+
+inline BitVector::IndexIterator BitVector::IndexIterator::operator++(int) {
+  IndexIterator result(*this);
+  ++*this;
+  return result;
+}
+
+inline uint32_t BitVector::IndexIterator::FindIndex(uint32_t start_index) const {
+  DCHECK_LE(start_index, BitSize());
+  uint32_t word_index = start_index / kWordBits;
+  if (UNLIKELY(word_index == storage_size_)) {
+    return start_index;
+  }
+  uint32_t word = bit_storage_[word_index];
+  // Mask out any bits in the first word we've already considered.
+  word &= static_cast<uint32_t>(-1) << (start_index & 0x1f);
+  while (word == 0u) {
+    ++word_index;
+    if (UNLIKELY(word_index == storage_size_)) {
+      return BitSize();
+    }
+    word = bit_storage_[word_index];
+  }
+  return word_index * 32u + CTZ(word);
+}
+
+inline void BitVector::ClearAllBits() {
+  memset(storage_, 0, storage_size_ * kWordBytes);
+}
+
+inline bool BitVector::Equal(const BitVector* src) const {
+  return (storage_size_ == src->GetStorageSize()) &&
+    (expandable_ == src->IsExpandable()) &&
+    (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0);
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 1b9022e..3d2f0de 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -16,20 +16,14 @@
 
 #include "bit_vector.h"
 
+#include "allocator.h"
+#include "bit_vector-inl.h"
+
 namespace art {
 
-// TODO: profile to make sure this is still a win relative to just using shifted masks.
-static uint32_t check_masks[32] = {
-  0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
-  0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
-  0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,
-  0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000,
-  0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000,
-  0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
-  0x40000000, 0x80000000 };
-
-static inline uint32_t BitsToWords(uint32_t bits) {
-  return (bits + 31) >> 5;
+// The number of words necessary to encode bits.
+static constexpr uint32_t BitsToWords(uint32_t bits) {
+  return RoundUp(bits, 32) / 32;
 }
 
 // TODO: replace excessive argument defaulting when we are at gcc 4.7
@@ -40,10 +34,10 @@
                      Allocator* allocator,
                      uint32_t storage_size,
                      uint32_t* storage)
-  : allocator_(allocator),
-    expandable_(expandable),
+  : storage_(storage),
     storage_size_(storage_size),
-    storage_(storage) {
+    allocator_(allocator),
+    expandable_(expandable) {
   COMPILE_ASSERT(sizeof(*storage_) == kWordBytes, check_word_bytes);
   COMPILE_ASSERT(sizeof(*storage_) * 8u == kWordBits, check_word_bits);
   if (storage_ == nullptr) {
@@ -56,59 +50,7 @@
   allocator_->Free(storage_);
 }
 
-/*
- * Determine whether or not the specified bit is set.
- */
-bool BitVector::IsBitSet(uint32_t num) const {
-  // If the index is over the size:
-  if (num >= storage_size_ * kWordBits) {
-    // Whether it is expandable or not, this bit does not exist: thus it is not set.
-    return false;
-  }
-
-  return IsBitSet(storage_, num);
-}
-
-// Mark all bits bit as "clear".
-void BitVector::ClearAllBits() {
-  memset(storage_, 0, storage_size_ * kWordBytes);
-}
-
-// Mark the specified bit as "set".
-/*
- * TUNING: this could have pathologically bad growth/expand behavior.  Make sure we're
- * not using it badly or change resize mechanism.
- */
-void BitVector::SetBit(uint32_t num) {
-  if (num >= storage_size_ * kWordBits) {
-    DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num;
-
-    /* Round up to word boundaries for "num+1" bits */
-    uint32_t new_size = BitsToWords(num + 1);
-    DCHECK_GT(new_size, storage_size_);
-    uint32_t *new_storage =
-        static_cast<uint32_t*>(allocator_->Alloc(new_size * kWordBytes));
-    memcpy(new_storage, storage_, storage_size_ * kWordBytes);
-    // Zero out the new storage words.
-    memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * kWordBytes);
-    // TOTO: collect stats on space wasted because of resize.
-    storage_ = new_storage;
-    storage_size_ = new_size;
-  }
-
-  storage_[num >> 5] |= check_masks[num & 0x1f];
-}
-
-// Mark the specified bit as "unset".
-void BitVector::ClearBit(uint32_t num) {
-  // If the index is over the size, we don't have to do anything, it is cleared.
-  if (num < storage_size_ * kWordBits) {
-    // Otherwise, go ahead and clear it.
-    storage_[num >> 5] &= ~check_masks[num & 0x1f];
-  }
-}
-
-bool BitVector::SameBitsSet(const BitVector *src) {
+bool BitVector::SameBitsSet(const BitVector *src) const {
   int our_highest = GetHighestBitSet();
   int src_highest = src->GetHighestBitSet();
 
@@ -134,7 +76,6 @@
   return (memcmp(storage_, src->GetRawStorage(), our_highest_index * kWordBytes) == 0);
 }
 
-// Intersect with another bit vector.
 void BitVector::Intersect(const BitVector* src) {
   uint32_t src_storage_size = src->storage_size_;
 
@@ -155,9 +96,6 @@
   }
 }
 
-/*
- * Union with another bit vector.
- */
 bool BitVector::Union(const BitVector* src) {
   // Get the highest bit to determine how much we need to expand.
   int highest_bit = src->GetHighestBitSet();
@@ -175,8 +113,7 @@
   if (storage_size_ < src_size) {
     changed = true;
 
-    // Set it to reallocate.
-    SetBit(highest_bit);
+    EnsureSize(highest_bit);
 
     // Paranoid: storage size should be big enough to hold this bit now.
     DCHECK_LT(static_cast<uint32_t> (highest_bit), storage_size_ * kWordBits);
@@ -242,21 +179,20 @@
 }
 
 void BitVector::Subtract(const BitVector *src) {
-    uint32_t src_size = src->storage_size_;
+  uint32_t src_size = src->storage_size_;
 
-    // We only need to operate on bytes up to the smaller of the sizes of the two operands.
-    unsigned int min_size = (storage_size_ > src_size) ? src_size : storage_size_;
+  // We only need to operate on bytes up to the smaller of the sizes of the two operands.
+  unsigned int min_size = (storage_size_ > src_size) ? src_size : storage_size_;
 
-    // Difference until max, we know both accept it:
-    //   There is no need to do more:
-    //     If we are bigger than src, the upper bits are unchanged.
-    //     If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted.
-    for (uint32_t idx = 0; idx < min_size; idx++) {
-        storage_[idx] &= (~(src->GetRawStorageWord(idx)));
-    }
+  // Difference until max, we know both accept it:
+  //   There is no need to do more:
+  //     If we are bigger than src, the upper bits are unchanged.
+  //     If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted.
+  for (uint32_t idx = 0; idx < min_size; idx++) {
+    storage_[idx] &= (~(src->GetRawStorageWord(idx)));
+  }
 }
 
-// Count the number of bits that are set.
 uint32_t BitVector::NumSetBits() const {
   uint32_t count = 0;
   for (uint32_t word = 0; word < storage_size_; word++) {
@@ -265,17 +201,11 @@
   return count;
 }
 
-// Count the number of bits that are set in range [0, end).
 uint32_t BitVector::NumSetBits(uint32_t end) const {
   DCHECK_LE(end, storage_size_ * kWordBits);
   return NumSetBits(storage_, end);
 }
 
-/*
- * Mark specified number of bits as "set". Cannot set all bits like ClearAll
- * since there might be unused bits - setting those to one will confuse the
- * iterator.
- */
 void BitVector::SetInitialBits(uint32_t num_bits) {
   // If num_bits is 0, clear everything.
   if (num_bits == 0) {
@@ -288,7 +218,7 @@
 
   uint32_t idx;
   // We can set every storage element with -1.
-  for (idx = 0; idx < (num_bits >> 5); idx++) {
+  for (idx = 0; idx < WordIndex(num_bits); idx++) {
     storage_[idx] = -1;
   }
 
@@ -312,20 +242,8 @@
     uint32_t value = storage_[idx];
 
     if (value != 0) {
-      // Shift right for the counting.
-      value /= 2;
-
-      int cnt = 0;
-
-      // Count the bits.
-      while (value > 0) {
-        value /= 2;
-        cnt++;
-      }
-
-      // Return cnt + how many storage units still remain * the number of bits per unit.
-      int res = cnt + (idx * kWordBits);
-      return res;
+      // Return highest bit set in value plus bits from previous storage indexes.
+      return 31 - CLZ(value) + (idx * kWordBits);
     }
   }
 
@@ -333,23 +251,6 @@
   return -1;
 }
 
-bool BitVector::EnsureSizeAndClear(unsigned int num) {
-  // Check if the bitvector is expandable.
-  if (IsExpandable() == false) {
-    return false;
-  }
-
-  if (num > 0) {
-    // Now try to expand by setting the last bit.
-    SetBit(num - 1);
-  }
-
-  // We must clear all bits as per our specification.
-  ClearAllBits();
-
-  return true;
-}
-
 void BitVector::Copy(const BitVector *src) {
   // Get highest bit set, we only need to copy till then.
   int highest_bit = src->GetHighestBitSet();
@@ -375,13 +276,8 @@
   }
 }
 
-bool BitVector::IsBitSet(const uint32_t* storage, uint32_t num) {
-  uint32_t val = storage[num >> 5] & check_masks[num & 0x1f];
-  return (val != 0);
-}
-
 uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) {
-  uint32_t word_end = end >> 5;
+  uint32_t word_end = WordIndex(end);
   uint32_t partial_word_bits = end & 0x1f;
 
   uint32_t count = 0u;
@@ -400,45 +296,6 @@
   os << buffer.str() << std::endl;
 }
 
-
-void BitVector::DumpDotHelper(bool last_entry, FILE* file, std::ostringstream& buffer) const {
-  // Now print it to the file.
-  fprintf(file, "    {%s}", buffer.str().c_str());
-
-  // If it isn't the last entry, add a |.
-  if (last_entry == false) {
-    fprintf(file, "|");
-  }
-
-  // Add the \n.
-  fprintf(file, "\\\n");
-}
-
-void BitVector::DumpDot(FILE* file, const char* prefix, bool last_entry) const {
-  std::ostringstream buffer;
-  DumpHelper(prefix, buffer);
-  DumpDotHelper(last_entry, file, buffer);
-}
-
-void BitVector::DumpIndicesDot(FILE* file, const char* prefix, bool last_entry) const {
-  std::ostringstream buffer;
-  DumpIndicesHelper(prefix, buffer);
-  DumpDotHelper(last_entry, file, buffer);
-}
-
-void BitVector::DumpIndicesHelper(const char* prefix, std::ostringstream& buffer) const {
-  // Initialize it.
-  if (prefix != nullptr) {
-    buffer << prefix;
-  }
-
-  for (size_t i = 0; i < storage_size_ * kWordBits; i++) {
-    if (IsBitSet(i)) {
-      buffer << i << " ";
-    }
-  }
-}
-
 void BitVector::DumpHelper(const char* prefix, std::ostringstream& buffer) const {
   // Initialize it.
   if (prefix != nullptr) {
@@ -452,4 +309,22 @@
   buffer << ')';
 }
 
+void BitVector::EnsureSize(uint32_t idx) {
+  if (idx >= storage_size_ * kWordBits) {
+    DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << idx;
+
+    /* Round up to word boundaries for "idx+1" bits */
+    uint32_t new_size = BitsToWords(idx + 1);
+    DCHECK_GT(new_size, storage_size_);
+    uint32_t *new_storage =
+        static_cast<uint32_t*>(allocator_->Alloc(new_size * kWordBytes));
+    memcpy(new_storage, storage_, storage_size_ * kWordBytes);
+    // Zero out the new storage words.
+    memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * kWordBytes);
+    // TOTO: collect stats on space wasted because of resize.
+    storage_ = new_storage;
+    storage_size_ = new_size;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index fb1646f..1e28a27 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -18,235 +18,237 @@
 #define ART_RUNTIME_BASE_BIT_VECTOR_H_
 
 #include <stdint.h>
-#include <stddef.h>
-
-#include "allocator.h"
-#include "base/logging.h"
-#include "utils.h"
+#include <iterator>
 
 namespace art {
 
+class Allocator;
+
 /*
  * Expanding bitmap, used for tracking resources.  Bits are numbered starting
  * from zero.  All operations on a BitVector are unsynchronized.
  */
 class BitVector {
-  public:
-    class IndexContainer;
+ public:
+  class IndexContainer;
 
-    /**
-     * @brief Convenient iterator across the indexes of the BitVector's set bits.
-     *
-     * @details IndexIterator is a Forward iterator (C++11: 24.2.5) from the lowest
-     * to the highest index of the BitVector's set bits. Instances can be retrieved
-     * only through BitVector::Indexes() which returns an IndexContainer wrapper
-     * object with begin() and end() suitable for range-based loops:
-     *   for (uint32_t idx : bit_vector.Indexes()) {
-     *     // Use idx.
-     *   }
-     */
-    class IndexIterator
-        : std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, uint32_t> {
-      public:
-        bool operator==(const IndexIterator& other) const {
-          DCHECK(bit_storage_ == other.bit_storage_);
-          DCHECK_EQ(storage_size_, other.storage_size_);
-          return bit_index_ == other.bit_index_;
-        }
+  /**
+   * @brief Convenient iterator across the indexes of the BitVector's set bits.
+   *
+   * @details IndexIterator is a Forward iterator (C++11: 24.2.5) from the lowest
+   * to the highest index of the BitVector's set bits. Instances can be retrieved
+   * only through BitVector::Indexes() which returns an IndexContainer wrapper
+   * object with begin() and end() suitable for range-based loops:
+   *   for (uint32_t idx : bit_vector.Indexes()) {
+   *     // Use idx.
+   *   }
+   */
+  class IndexIterator :
+      std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, uint32_t> {
+   public:
+    bool operator==(const IndexIterator& other) const;
 
-        bool operator!=(const IndexIterator& other) const {
-          return !(*this == other);
-        }
-
-        int operator*() const {
-          DCHECK_LT(bit_index_, BitSize());
-          return bit_index_;
-        }
-
-        IndexIterator& operator++() {
-          DCHECK_LT(bit_index_, BitSize());
-          bit_index_ = FindIndex(bit_index_ + 1u);
-          return *this;
-        }
-
-        IndexIterator operator++(int) {
-          IndexIterator result(*this);
-          ++*this;
-          return result;
-        }
-
-        // Helper function to check for end without comparing with bit_vector.Indexes().end().
-        bool Done() const {
-          return bit_index_ == BitSize();
-        }
-
-      private:
-        struct begin_tag { };
-        struct end_tag { };
-
-        IndexIterator(const BitVector* bit_vector, begin_tag)
-          : bit_storage_(bit_vector->GetRawStorage()),
-            storage_size_(bit_vector->storage_size_),
-            bit_index_(FindIndex(0u)) { }
-
-        IndexIterator(const BitVector* bit_vector, end_tag)
-          : bit_storage_(bit_vector->GetRawStorage()),
-            storage_size_(bit_vector->storage_size_),
-            bit_index_(BitSize()) { }
-
-        uint32_t BitSize() const {
-          return storage_size_ * kWordBits;
-        }
-
-        uint32_t FindIndex(uint32_t start_index) const {
-          DCHECK_LE(start_index, BitSize());
-          uint32_t word_index = start_index / kWordBits;
-          if (UNLIKELY(word_index == storage_size_)) {
-            return start_index;
-          }
-          uint32_t word = bit_storage_[word_index];
-          // Mask out any bits in the first word we've already considered.
-          word &= static_cast<uint32_t>(-1) << (start_index & 0x1f);
-          while (word == 0u) {
-            ++word_index;
-            if (UNLIKELY(word_index == storage_size_)) {
-              return BitSize();
-            }
-            word = bit_storage_[word_index];
-          }
-          return word_index * 32u + CTZ(word);
-        }
-
-        const uint32_t* const bit_storage_;
-        const uint32_t storage_size_;  // Size of vector in words.
-        uint32_t bit_index_;           // Current index (size in bits).
-
-        friend class BitVector::IndexContainer;
-    };
-
-    /**
-     * @brief BitVector wrapper class for iteration across indexes of set bits.
-     */
-    class IndexContainer {
-     public:
-      explicit IndexContainer(const BitVector* bit_vector) : bit_vector_(bit_vector) { }
-
-      IndexIterator begin() const {
-        return IndexIterator(bit_vector_, IndexIterator::begin_tag());
-      }
-
-      IndexIterator end() const {
-        return IndexIterator(bit_vector_, IndexIterator::end_tag());
-      }
-
-     private:
-      const BitVector* const bit_vector_;
-    };
-
-    BitVector(uint32_t start_bits,
-              bool expandable,
-              Allocator* allocator,
-              uint32_t storage_size = 0,
-              uint32_t* storage = nullptr);
-
-    virtual ~BitVector();
-
-    void SetBit(uint32_t num);
-    void ClearBit(uint32_t num);
-    bool IsBitSet(uint32_t num) const;
-    void ClearAllBits();
-    void SetInitialBits(uint32_t num_bits);
-
-    void Copy(const BitVector* src);
-    void Intersect(const BitVector* src2);
-    bool Union(const BitVector* src);
-
-    // Set bits of union_with that are not in not_in.
-    bool UnionIfNotIn(const BitVector* union_with, const BitVector* not_in);
-
-    void Subtract(const BitVector* src);
-    // Are we equal to another bit vector?  Note: expandability attributes must also match.
-    bool Equal(const BitVector* src) {
-      return (storage_size_ == src->GetStorageSize()) &&
-        (expandable_ == src->IsExpandable()) &&
-        (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0);
+    bool operator!=(const IndexIterator& other) const {
+      return !(*this == other);
     }
 
-    /**
-     * @brief Are all the bits set the same?
-     * @details expandability and size can differ as long as the same bits are set.
-     */
-    bool SameBitsSet(const BitVector *src);
+    int operator*() const;
 
-    uint32_t NumSetBits() const;
+    IndexIterator& operator++();
 
-    // Number of bits set in range [0, end).
-    uint32_t NumSetBits(uint32_t end) const;
+    IndexIterator operator++(int);
 
-    IndexContainer Indexes() const {
-      return IndexContainer(this);
+    // Helper function to check for end without comparing with bit_vector.Indexes().end().
+    bool Done() const {
+      return bit_index_ == BitSize();
     }
 
-    uint32_t GetStorageSize() const { return storage_size_; }
-    bool IsExpandable() const { return expandable_; }
-    uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; }
-    uint32_t* GetRawStorage() { return storage_; }
-    const uint32_t* GetRawStorage() const { return storage_; }
-    size_t GetSizeOf() const { return storage_size_ * kWordBytes; }
+   private:
+    struct begin_tag { };
+    struct end_tag { };
 
-    /**
-     * @return the highest bit set, -1 if none are set
+    IndexIterator(const BitVector* bit_vector, begin_tag)
+      : bit_storage_(bit_vector->GetRawStorage()),
+        storage_size_(bit_vector->storage_size_),
+        bit_index_(FindIndex(0u)) { }
+
+    IndexIterator(const BitVector* bit_vector, end_tag)
+      : bit_storage_(bit_vector->GetRawStorage()),
+        storage_size_(bit_vector->storage_size_),
+        bit_index_(BitSize()) { }
+
+    uint32_t BitSize() const {
+      return storage_size_ * kWordBits;
+    }
+
+    uint32_t FindIndex(uint32_t start_index) const;
+    const uint32_t* const bit_storage_;
+    const uint32_t storage_size_;  // Size of vector in words.
+    uint32_t bit_index_;           // Current index (size in bits).
+
+    friend class BitVector::IndexContainer;
+  };
+
+  /**
+   * @brief BitVector wrapper class for iteration across indexes of set bits.
+   */
+  class IndexContainer {
+   public:
+    explicit IndexContainer(const BitVector* bit_vector) : bit_vector_(bit_vector) { }
+
+    IndexIterator begin() const {
+      return IndexIterator(bit_vector_, IndexIterator::begin_tag());
+    }
+
+    IndexIterator end() const {
+      return IndexIterator(bit_vector_, IndexIterator::end_tag());
+    }
+
+   private:
+    const BitVector* const bit_vector_;
+  };
+
+  BitVector(uint32_t start_bits,
+            bool expandable,
+            Allocator* allocator,
+            uint32_t storage_size = 0,
+            uint32_t* storage = nullptr);
+
+  virtual ~BitVector();
+
+  // Mark the specified bit as "set".
+  void SetBit(uint32_t idx) {
+    /*
+     * TUNING: this could have pathologically bad growth/expand behavior.  Make sure we're
+     * not using it badly or change resize mechanism.
      */
-    int GetHighestBitSet() const;
+    if (idx >= storage_size_ * kWordBits) {
+      EnsureSize(idx);
+    }
+    storage_[WordIndex(idx)] |= BitMask(idx);
+  }
 
-    // Is bit set in storage. (No range check.)
-    static bool IsBitSet(const uint32_t* storage, uint32_t num);
-    // Number of bits set in range [0, end) in storage. (No range check.)
-    static uint32_t NumSetBits(const uint32_t* storage, uint32_t end);
+  // Mark the specified bit as "unset".
+  void ClearBit(uint32_t idx) {
+    // If the index is over the size, we don't have to do anything, it is cleared.
+    if (idx < storage_size_ * kWordBits) {
+      // Otherwise, go ahead and clear it.
+      storage_[WordIndex(idx)] &= ~BitMask(idx);
+    }
+  }
 
-    bool EnsureSizeAndClear(unsigned int num);
+  // Determine whether or not the specified bit is set.
+  bool IsBitSet(uint32_t idx) const {
+    // If the index is over the size, whether it is expandable or not, this bit does not exist:
+    // thus it is not set.
+    return (idx < (storage_size_ * kWordBits)) && IsBitSet(storage_, idx);
+  }
 
-    void Dump(std::ostream& os, const char* prefix) const;
+  // Mark all bits bit as "clear".
+  void ClearAllBits();
 
-    /**
-     * @brief last_entry is this the last entry for the dot dumping
-     * @details if not, a "|" is appended to the dump.
-     */
-    void DumpDot(FILE* file, const char* prefix, bool last_entry = false) const;
+  // Mark specified number of bits as "set". Cannot set all bits like ClearAll since there might
+  // be unused bits - setting those to one will confuse the iterator.
+  void SetInitialBits(uint32_t num_bits);
 
-    /**
-     * @brief last_entry is this the last entry for the dot dumping
-     * @details if not, a "|" is appended to the dump.
-     */
-    void DumpIndicesDot(FILE* file, const char* prefix, bool last_entry = false) const;
+  void Copy(const BitVector* src);
 
-  protected:
-    /**
-     * @brief Dump the bitvector into buffer in a 00101..01 format.
-     * @param buffer the ostringstream used to dump the bitvector into.
-     */
-    void DumpHelper(const char* prefix, std::ostringstream& buffer) const;
+  // Intersect with another bit vector.
+  void Intersect(const BitVector* src2);
 
-    /**
-     * @brief Dump the bitvector in a 1 2 5 8 format, where the numbers are the bit set.
-     * @param buffer the ostringstream used to dump the bitvector into.
-     */
-    void DumpIndicesHelper(const char* prefix, std::ostringstream& buffer) const;
+  // Union with another bit vector.
+  bool Union(const BitVector* src);
 
-    /**
-     * @brief Wrapper to perform the bitvector dumping with the .dot format.
-     * @param buffer the ostringstream used to dump the bitvector into.
-     */
-    void DumpDotHelper(bool last_entry, FILE* file, std::ostringstream& buffer) const;
+  // Set bits of union_with that are not in not_in.
+  bool UnionIfNotIn(const BitVector* union_with, const BitVector* not_in);
 
-  private:
-    static constexpr uint32_t kWordBytes = sizeof(uint32_t);
-    static constexpr uint32_t kWordBits = kWordBytes * 8;
+  void Subtract(const BitVector* src);
 
-    Allocator* const allocator_;
-    const bool expandable_;         // expand bitmap if we run out?
-    uint32_t   storage_size_;       // current size, in 32-bit words.
-    uint32_t*  storage_;
+  // Are we equal to another bit vector?  Note: expandability attributes must also match.
+  bool Equal(const BitVector* src) const;
+
+  /**
+   * @brief Are all the bits set the same?
+   * @details expandability and size can differ as long as the same bits are set.
+   */
+  bool SameBitsSet(const BitVector *src) const;
+
+  // Count the number of bits that are set.
+  uint32_t NumSetBits() const;
+
+  // Count the number of bits that are set in range [0, end).
+  uint32_t NumSetBits(uint32_t end) const;
+
+  IndexContainer Indexes() const {
+    return IndexContainer(this);
+  }
+
+  uint32_t GetStorageSize() const {
+    return storage_size_;
+  }
+
+  bool IsExpandable() const {
+    return expandable_;
+  }
+
+  uint32_t GetRawStorageWord(size_t idx) const {
+    return storage_[idx];
+  }
+
+  uint32_t* GetRawStorage() {
+    return storage_;
+  }
+
+  const uint32_t* GetRawStorage() const {
+    return storage_;
+  }
+
+  size_t GetSizeOf() const {
+    return storage_size_ * kWordBytes;
+  }
+
+  /**
+   * @return the highest bit set, -1 if none are set
+   */
+  int GetHighestBitSet() const;
+
+  // Is bit set in storage. (No range check.)
+  static bool IsBitSet(const uint32_t* storage, uint32_t idx) {
+    return (storage[WordIndex(idx)] & BitMask(idx)) != 0;
+  }
+
+  // Number of bits set in range [0, end) in storage. (No range check.)
+  static uint32_t NumSetBits(const uint32_t* storage, uint32_t end);
+
+  void Dump(std::ostream& os, const char* prefix) const;
+
+ private:
+  /**
+   * @brief Dump the bitvector into buffer in a 00101..01 format.
+   * @param buffer the ostringstream used to dump the bitvector into.
+   */
+  void DumpHelper(const char* prefix, std::ostringstream& buffer) const;
+
+  // Ensure there is space for a bit at idx.
+  void EnsureSize(uint32_t idx);
+
+  // The index of the word within storage.
+  static constexpr uint32_t WordIndex(uint32_t idx) {
+    return idx >> 5;
+  }
+
+  // A bit mask to extract the bit for the given index.
+  static constexpr uint32_t BitMask(uint32_t idx) {
+    return 1 << (idx & 0x1f);
+  }
+
+  static constexpr uint32_t kWordBytes = sizeof(uint32_t);
+  static constexpr uint32_t kWordBits = kWordBytes * 8;
+
+  uint32_t*  storage_;            // The storage for the bit vector.
+  uint32_t   storage_size_;       // Current size, in 32-bit words.
+  Allocator* const allocator_;    // Allocator if expandable.
+  const bool expandable_;         // Should the bitmap expand if too small?
 };
 
 
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
index 1403f50..df5d79d 100644
--- a/runtime/base/bit_vector_test.cc
+++ b/runtime/base/bit_vector_test.cc
@@ -16,7 +16,8 @@
 
 #include <memory>
 
-#include "bit_vector.h"
+#include "allocator.h"
+#include "bit_vector-inl.h"
 #include "gtest/gtest.h"
 
 namespace art {
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index caeb946..cf3e763 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -151,7 +151,7 @@
 
 template <typename LHS, typename RHS>
 struct EagerEvaluator {
-  EagerEvaluator(LHS lhs, RHS rhs) : lhs(lhs), rhs(rhs) { }
+  EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { }
   LHS lhs;
   RHS rhs;
 };
@@ -163,9 +163,9 @@
 // protect you against combinations not explicitly listed below.
 #define EAGER_PTR_EVALUATOR(T1, T2) \
   template <> struct EagerEvaluator<T1, T2> { \
-    EagerEvaluator(T1 lhs, T2 rhs) \
-        : lhs(reinterpret_cast<const void*>(lhs)), \
-          rhs(reinterpret_cast<const void*>(rhs)) { } \
+    EagerEvaluator(T1 l, T2 r) \
+        : lhs(reinterpret_cast<const void*>(l)), \
+          rhs(reinterpret_cast<const void*>(r)) { } \
     const void* lhs; \
     const void* rhs; \
   }
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index fae9271..b66d528 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -179,6 +179,7 @@
 #define WARN_UNUSED __attribute__((warn_unused_result))
 
 template<typename T> void UNUSED(const T&) {}
+#define UNREACHABLE  __builtin_unreachable
 
 // Annotalysis thread-safety analysis support.
 #if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 9b97411..4383a7c 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -39,6 +39,7 @@
 ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
 Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
 Mutex* Locks::intern_table_lock_ = nullptr;
+Mutex* Locks::jni_libraries_lock_ = nullptr;
 Mutex* Locks::logging_lock_ = nullptr;
 Mutex* Locks::mem_maps_lock_ = nullptr;
 Mutex* Locks::modify_ldt_lock_ = nullptr;
@@ -779,8 +780,9 @@
   guard_.recursion_count_ = old_recursion_count;
 }
 
-void ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) {
+bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) {
   DCHECK(self == NULL || self == Thread::Current());
+  bool timed_out = false;
   guard_.AssertExclusiveHeld(self);
   guard_.CheckSafeToWait(self);
   unsigned int old_recursion_count = guard_.recursion_count_;
@@ -796,6 +798,7 @@
   if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, &rel_ts, NULL, 0) != 0) {
     if (errno == ETIMEDOUT) {
       // Timed out we're done.
+      timed_out = true;
     } else if ((errno == EAGAIN) || (errno == EINTR)) {
       // A signal or ConditionVariable::Signal/Broadcast has come in.
     } else {
@@ -820,13 +823,16 @@
   timespec ts;
   InitTimeSpec(true, clock, ms, ns, &ts);
   int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &guard_.mutex_, &ts));
-  if (rc != 0 && rc != ETIMEDOUT) {
+  if (rc == ETIMEDOUT) {
+    timed_out = true;
+  } else if (rc != 0) {
     errno = rc;
     PLOG(FATAL) << "TimedWait failed for " << name_;
   }
   guard_.exclusive_owner_ = old_owner;
 #endif
   guard_.recursion_count_ = old_recursion_count;
+  return timed_out;
 }
 
 void Locks::Init() {
@@ -846,6 +852,7 @@
     DCHECK(deoptimization_lock_ != nullptr);
     DCHECK(heap_bitmap_lock_ != nullptr);
     DCHECK(intern_table_lock_ != nullptr);
+    DCHECK(jni_libraries_lock_ != nullptr);
     DCHECK(logging_lock_ != nullptr);
     DCHECK(mutator_lock_ != nullptr);
     DCHECK(profiler_lock_ != nullptr);
@@ -906,6 +913,10 @@
     DCHECK(thread_list_lock_ == nullptr);
     thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock);
+    DCHECK(jni_libraries_lock_ == nullptr);
+    jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kBreakpointLock);
     DCHECK(breakpoint_lock_ == nullptr);
     breakpoint_lock_ = new ReaderWriterMutex("breakpoint lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 1847f6b..516fa07 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -79,7 +79,6 @@
   kDefaultMutexLevel,
   kMarkSweepLargeObjectLock,
   kPinTableLock,
-  kLoadLibraryLock,
   kJdwpObjectRegistryLock,
   kModifyLdtLock,
   kAllocatedThreadIdsLock,
@@ -88,6 +87,7 @@
   kBreakpointLock,
   kMonitorLock,
   kMonitorListLock,
+  kJniLoadLibraryLock,
   kThreadListLock,
   kBreakpointInvokeLock,
   kAllocTrackerLock,
@@ -388,7 +388,7 @@
   // TODO: No thread safety analysis on Wait and TimedWait as they call mutex operations via their
   //       pointer copy, thereby defeating annotalysis.
   void Wait(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
-  void TimedWait(Thread* self, int64_t ms, int32_t ns) NO_THREAD_SAFETY_ANALYSIS;
+  bool TimedWait(Thread* self, int64_t ms, int32_t ns) NO_THREAD_SAFETY_ANALYSIS;
   // Variant of Wait that should be used with caution. Doesn't validate that no mutexes are held
   // when waiting.
   // TODO: remove this.
@@ -579,8 +579,11 @@
   // attaching and detaching.
   static Mutex* thread_list_lock_ ACQUIRED_AFTER(deoptimization_lock_);
 
+  // Guards maintaining loading library data structures.
+  static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
+
   // Guards breakpoints.
-  static ReaderWriterMutex* breakpoint_lock_ ACQUIRED_AFTER(trace_lock_);
+  static ReaderWriterMutex* breakpoint_lock_ ACQUIRED_AFTER(jni_libraries_lock_);
 
   // Guards lists of classes within the class linker.
   static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(breakpoint_lock_);
diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc
index 47140e3..824ee48 100644
--- a/runtime/base/stringpiece.cc
+++ b/runtime/base/stringpiece.cc
@@ -19,26 +19,34 @@
 #include <iostream>
 #include <utility>
 
+#include "logging.h"
+
 namespace art {
 
+#if !defined(NDEBUG)
+char StringPiece::operator[](size_type i) const {
+  CHECK_LT(i, length_);
+  return ptr_[i];
+}
+#endif
+
 void StringPiece::CopyToString(std::string* target) const {
   target->assign(ptr_, length_);
 }
 
-int StringPiece::copy(char* buf, size_type n, size_type pos) const {
-  int ret = std::min(length_ - pos, n);
+StringPiece::size_type StringPiece::copy(char* buf, size_type n, size_type pos) const {
+  size_type ret = std::min(length_ - pos, n);
   memcpy(buf, ptr_ + pos, ret);
   return ret;
 }
 
 StringPiece::size_type StringPiece::find(const StringPiece& s, size_type pos) const {
-  if (length_ < 0 || pos > static_cast<size_type>(length_))
+  if (length_ == 0 || pos > static_cast<size_type>(length_)) {
     return npos;
-
-  const char* result = std::search(ptr_ + pos, ptr_ + length_,
-                                   s.ptr_, s.ptr_ + s.length_);
+  }
+  const char* result = std::search(ptr_ + pos, ptr_ + length_, s.ptr_, s.ptr_ + s.length_);
   const size_type xpos = result - ptr_;
-  return xpos + s.length_ <= static_cast<size_type>(length_) ? xpos : npos;
+  return xpos + s.length_ <= length_ ? xpos : npos;
 }
 
 int StringPiece::compare(const StringPiece& x) const {
@@ -51,7 +59,7 @@
 }
 
 StringPiece::size_type StringPiece::find(char c, size_type pos) const {
-  if (length_ <= 0 || pos >= static_cast<size_type>(length_)) {
+  if (length_ == 0 || pos >= length_) {
     return npos;
   }
   const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
@@ -69,7 +77,7 @@
 }
 
 StringPiece::size_type StringPiece::rfind(char c, size_type pos) const {
-  if (length_ <= 0) return npos;
+  if (length_ == 0) return npos;
   for (int i = std::min(pos, static_cast<size_type>(length_ - 1));
        i >= 0; --i) {
     if (ptr_[i] == c) {
@@ -85,8 +93,6 @@
   return StringPiece(ptr_ + pos, n);
 }
 
-const StringPiece::size_type StringPiece::npos = size_type(-1);
-
 std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
   o.write(piece.data(), piece.size());
   return o;
diff --git a/runtime/base/stringpiece.h b/runtime/base/stringpiece.h
index 91b83f6..b8de308 100644
--- a/runtime/base/stringpiece.h
+++ b/runtime/base/stringpiece.h
@@ -14,6 +14,14 @@
  * limitations under the License.
  */
 
+#ifndef ART_RUNTIME_BASE_STRINGPIECE_H_
+#define ART_RUNTIME_BASE_STRINGPIECE_H_
+
+#include <string.h>
+#include <string>
+
+namespace art {
+
 // A string-like object that points to a sized piece of memory.
 //
 // Functions or methods may use const StringPiece& parameters to accept either
@@ -21,74 +29,75 @@
 // a StringPiece.  The implicit conversion means that it is often appropriate
 // to include this .h file in other files rather than forward-declaring
 // StringPiece as would be appropriate for most other Google classes.
-//
-// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
-// conversions from "const char*" to "string" and back again.
-
-#ifndef ART_RUNTIME_BASE_STRINGPIECE_H_
-#define ART_RUNTIME_BASE_STRINGPIECE_H_
-
-#include <string.h>
-#include <algorithm>
-#include <cstddef>
-#include <iosfwd>
-#include <string>
-
-namespace art {
-
 class StringPiece {
- private:
-  const char*   ptr_;
-  int           length_;
-
  public:
+  // standard STL container boilerplate
+  typedef char value_type;
+  typedef const char* pointer;
+  typedef const char& reference;
+  typedef const char& const_reference;
+  typedef size_t size_type;
+  typedef ptrdiff_t difference_type;
+  static constexpr size_type npos = size_type(-1);
+  typedef const char* const_iterator;
+  typedef const char* iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+
   // We provide non-explicit singleton constructors so users can pass
   // in a "const char*" or a "string" wherever a "StringPiece" is
   // expected.
-  StringPiece() : ptr_(NULL), length_(0) { }
-  StringPiece(const char* str)  // NOLINT
-    : ptr_(str), length_((str == NULL) ? 0 : static_cast<int>(strlen(str))) { }
-  StringPiece(const std::string& str)  // NOLINT
-    : ptr_(str.data()), length_(static_cast<int>(str.size())) { }
-  StringPiece(const char* offset, int len) : ptr_(offset), length_(len) { }
+  StringPiece() : ptr_(nullptr), length_(0) { }
+  StringPiece(const char* str)  // NOLINT implicit constructor desired
+    : ptr_(str), length_((str == nullptr) ? 0 : strlen(str)) { }
+  StringPiece(const std::string& str)  // NOLINT implicit constructor desired
+    : ptr_(str.data()), length_(str.size()) { }
+  StringPiece(const char* offset, size_t len) : ptr_(offset), length_(len) { }
 
   // data() may return a pointer to a buffer with embedded NULs, and the
   // returned buffer may or may not be null terminated.  Therefore it is
   // typically a mistake to pass data() to a routine that expects a NUL
   // terminated string.
   const char* data() const { return ptr_; }
-  int size() const { return length_; }
-  int length() const { return length_; }
+  size_type size() const { return length_; }
+  size_type length() const { return length_; }
   bool empty() const { return length_ == 0; }
 
   void clear() {
-    ptr_ = NULL;
+    ptr_ = nullptr;
     length_ = 0;
   }
-  void set(const char* data, int len) {
+  void set(const char* data, size_type len) {
     ptr_ = data;
     length_ = len;
   }
   void set(const char* str) {
     ptr_ = str;
-    if (str != NULL)
-      length_ = static_cast<int>(strlen(str));
-    else
+    if (str != nullptr) {
+      length_ = strlen(str);
+    } else {
       length_ = 0;
+    }
   }
-  void set(const void* data, int len) {
+  void set(const void* data, size_type len) {
     ptr_ = reinterpret_cast<const char*>(data);
     length_ = len;
   }
 
-  char operator[](int i) const { return ptr_[i]; }
+#if defined(NDEBUG)
+  char operator[](size_type i) const {
+    return ptr_[i];
+  }
+#else
+  char operator[](size_type i) const;
+#endif
 
-  void remove_prefix(int n) {
+  void remove_prefix(size_type n) {
     ptr_ += n;
     length_ -= n;
   }
 
-  void remove_suffix(int n) {
+  void remove_suffix(size_type n) {
     length_ -= n;
   }
 
@@ -121,18 +130,6 @@
             (memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
   }
 
-  // standard STL container boilerplate
-  typedef char value_type;
-  typedef const char* pointer;
-  typedef const char& reference;
-  typedef const char& const_reference;
-  typedef size_t size_type;
-  typedef ptrdiff_t difference_type;
-  static const size_type npos;
-  typedef const char* const_iterator;
-  typedef const char* iterator;
-  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-  typedef std::reverse_iterator<iterator> reverse_iterator;
   iterator begin() const { return ptr_; }
   iterator end() const { return ptr_ + length_; }
   const_reverse_iterator rbegin() const {
@@ -141,11 +138,8 @@
   const_reverse_iterator rend() const {
     return const_reverse_iterator(ptr_);
   }
-  // STLS says return size_type, but Google says return int
-  int max_size() const { return length_; }
-  int capacity() const { return length_; }
 
-  int copy(char* buf, size_type n, size_type pos = 0) const;
+  size_type copy(char* buf, size_type n, size_type pos = 0) const;
 
   size_type find(const StringPiece& s, size_type pos = 0) const;
   size_type find(char c, size_type pos = 0) const;
@@ -153,13 +147,19 @@
   size_type rfind(char c, size_type pos = npos) const;
 
   StringPiece substr(size_type pos, size_type n = npos) const;
+
+ private:
+  // Pointer to char data, not necessarily zero terminated.
+  const char* ptr_;
+  // Length of data.
+  size_type   length_;
 };
 
 // This large function is defined inline so that in a fairly common case where
 // one of the arguments is a literal, the compiler can elide a lot of the
 // following comparisons.
 inline bool operator==(const StringPiece& x, const StringPiece& y) {
-  int len = x.size();
+  StringPiece::size_type len = x.size();
   if (len != y.size()) {
     return false;
   }
@@ -169,7 +169,7 @@
   if (p1 == p2) {
     return true;
   }
-  if (len <= 0) {
+  if (len == 0) {
     return true;
   }
 
@@ -184,10 +184,22 @@
   return memcmp(p1, p2, len) == 0;
 }
 
+inline bool operator==(const StringPiece& x, const char* y) {
+  if (y == nullptr) {
+    return x.size() == 0;
+  } else {
+    return strncmp(x.data(), y, x.size()) == 0 && y[x.size()] == '\0';
+  }
+}
+
 inline bool operator!=(const StringPiece& x, const StringPiece& y) {
   return !(x == y);
 }
 
+inline bool operator!=(const StringPiece& x, const char* y) {
+  return !(x == y);
+}
+
 inline bool operator<(const StringPiece& x, const StringPiece& y) {
   const int r = memcmp(x.data(), y.data(),
                        std::min(x.size(), y.size()));
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 9036e3d..bfe44a2 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -25,6 +25,7 @@
 #include "dex_file-inl.h"
 #include "field_helper.h"
 #include "gc/space/space.h"
+#include "java_vm_ext.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
@@ -35,62 +36,16 @@
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
+#include "well_known_classes.h"
 
 namespace art {
 
-static void JniAbort(const char* jni_function_name, const char* msg) {
-  Thread* self = Thread::Current();
-  ScopedObjectAccess soa(self);
-  mirror::ArtMethod* current_method = self->GetCurrentMethod(nullptr);
-
-  std::ostringstream os;
-  os << "JNI DETECTED ERROR IN APPLICATION: " << msg;
-
-  if (jni_function_name != nullptr) {
-    os << "\n    in call to " << jni_function_name;
-  }
-  // TODO: is this useful given that we're about to dump the calling thread's stack?
-  if (current_method != nullptr) {
-    os << "\n    from " << PrettyMethod(current_method);
-  }
-  os << "\n";
-  self->Dump(os);
-
-  JavaVMExt* vm = Runtime::Current()->GetJavaVM();
-  if (vm->check_jni_abort_hook != nullptr) {
-    vm->check_jni_abort_hook(vm->check_jni_abort_hook_data, os.str());
-  } else {
-    // Ensure that we get a native stack trace for this thread.
-    self->TransitionFromRunnableToSuspended(kNative);
-    LOG(FATAL) << os.str();
-    self->TransitionFromSuspendedToRunnable();  // Unreachable, keep annotalysis happy.
-  }
-}
-
-static void JniAbortV(const char* jni_function_name, const char* fmt, va_list ap) {
-  std::string msg;
-  StringAppendV(&msg, fmt, ap);
-  JniAbort(jni_function_name, msg.c_str());
-}
-
-void JniAbortF(const char* jni_function_name, const char* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  JniAbortV(jni_function_name, fmt, args);
-  va_end(args);
-}
-
 /*
  * ===========================================================================
  *      JNI function helpers
  * ===========================================================================
  */
 
-static bool IsHandleScopeLocalRef(JNIEnv* env, jobject localRef) {
-  return GetIndirectRefKind(localRef) == kHandleScopeOrInvalid &&
-      reinterpret_cast<JNIEnvExt*>(env)->self->HandleScopeContains(localRef);
-}
-
 // Flags passed into ScopedCheck.
 #define kFlag_Default       0x0000
 
@@ -109,134 +64,88 @@
 #define kFlag_Invocation    0x8000      // Part of the invocation interface (JavaVM*).
 
 #define kFlag_ForceTrace    0x80000000  // Add this to a JNI function's flags if you want to trace every call.
-
-static const char* gBuiltInPrefixes[] = {
-  "Landroid/",
-  "Lcom/android/",
-  "Lcom/google/android/",
-  "Ldalvik/",
-  "Ljava/",
-  "Ljavax/",
-  "Llibcore/",
-  "Lorg/apache/harmony/",
-  nullptr
+/*
+ * Java primitive types:
+ * B - jbyte
+ * C - jchar
+ * D - jdouble
+ * F - jfloat
+ * I - jint
+ * J - jlong
+ * S - jshort
+ * Z - jboolean (shown as true and false)
+ * V - void
+ *
+ * Java reference types:
+ * L - jobject
+ * a - jarray
+ * c - jclass
+ * s - jstring
+ * t - jthrowable
+ *
+ * JNI types:
+ * b - jboolean (shown as JNI_TRUE and JNI_FALSE)
+ * f - jfieldID
+ * i - JNI error value (JNI_OK, JNI_ERR, JNI_EDETACHED, JNI_EVERSION)
+ * m - jmethodID
+ * p - void*
+ * r - jint (for release mode arguments)
+ * u - const char* (Modified UTF-8)
+ * z - jsize (for lengths; use i if negative values are okay)
+ * v - JavaVM*
+ * w - jobjectRefType
+ * E - JNIEnv*
+ * . - no argument; just print "..." (used for varargs JNI calls)
+ *
+ */
+union JniValueType {
+  jarray a;
+  jboolean b;
+  jclass c;
+  jfieldID f;
+  jint i;
+  jmethodID m;
+  const void* p;  // Pointer.
+  jint r;  // Release mode.
+  jstring s;
+  jthrowable t;
+  const char* u;  // Modified UTF-8.
+  JavaVM* v;
+  jobjectRefType w;
+  jsize z;
+  jbyte B;
+  jchar C;
+  jdouble D;
+  JNIEnv* E;
+  jfloat F;
+  jint I;
+  jlong J;
+  jobject L;
+  jshort S;
+  const void* V;  // void
+  jboolean Z;
 };
 
-static bool ShouldTrace(JavaVMExt* vm, mirror::ArtMethod* method)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
-  // when a native method that matches the -Xjnitrace argument calls a JNI function
-  // such as NewByteArray.
-  // If -verbose:third-party-jni is on, we want to log any JNI function calls
-  // made by a third-party native method.
-  std::string class_name(method->GetDeclaringClassDescriptor());
-  if (!vm->trace.empty() && class_name.find(vm->trace) != std::string::npos) {
-    return true;
-  }
-  if (VLOG_IS_ON(third_party_jni)) {
-    // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
-    // like part of Android.
-    for (size_t i = 0; gBuiltInPrefixes[i] != nullptr; ++i) {
-      if (StartsWith(class_name, gBuiltInPrefixes[i])) {
-        return false;
-      }
-    }
-    return true;
-  }
-  return false;
-}
-
 class ScopedCheck {
  public:
-  // For JNIEnv* functions.
-  explicit ScopedCheck(JNIEnv* env, int flags, const char* functionName)
-      SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
-      : soa_(env) {
-    Init(flags, functionName, true);
-    CheckThread(flags);
+  explicit ScopedCheck(int flags, const char* functionName, bool has_method = true)
+      : function_name_(functionName), flags_(flags), indent_(0), has_method_(has_method) {
   }
 
-  // For JavaVM* functions.
-  // TODO: it's not correct that this is a lock function, but making it so aids annotalysis.
-  explicit ScopedCheck(JavaVM* vm, bool has_method, const char* functionName)
-      SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
-      : soa_(vm) {
-    Init(kFlag_Invocation, functionName, has_method);
-  }
-
-  ~ScopedCheck() UNLOCK_FUNCTION(Locks::mutator_lock_) {}
-
-  const ScopedObjectAccess& soa() {
-    return soa_;
-  }
-
-  bool ForceCopy() {
-    return Runtime::Current()->GetJavaVM()->force_copy;
-  }
+  ~ScopedCheck() {}
 
   // Checks that 'class_name' is a valid "fully-qualified" JNI class name, like "java/lang/Thread"
   // or "[Ljava/lang/Object;". A ClassLoader can actually normalize class names a couple of
   // times, so using "java.lang.Thread" instead of "java/lang/Thread" might work in some
   // circumstances, but this is incorrect.
-  void CheckClassName(const char* class_name) {
+  bool CheckClassName(const char* class_name) {
     if ((class_name == nullptr) || !IsValidJniClassName(class_name)) {
-      JniAbortF(function_name_,
-                "illegal class name '%s'\n"
-                "    (should be of the form 'package/Class', [Lpackage/Class;' or '[[B')",
-                class_name);
+      AbortF("illegal class name '%s'\n"
+             "    (should be of the form 'package/Class', [Lpackage/Class;' or '[[B')",
+             class_name);
+      return false;
     }
-  }
-
-  /*
-   * Verify that the field is of the appropriate type.  If the field has an
-   * object type, "java_object" is the object we're trying to assign into it.
-   *
-   * Works for both static and instance fields.
-   */
-  void CheckFieldType(jvalue value, jfieldID fid, char prim, bool isStatic)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    StackHandleScope<1> hs(Thread::Current());
-    Handle<mirror::ArtField> f(hs.NewHandle(CheckFieldID(fid)));
-    if (f.Get() == nullptr) {
-      return;
-    }
-    mirror::Class* field_type = FieldHelper(f).GetType();
-    if (!field_type->IsPrimitive()) {
-      jobject java_object = value.l;
-      if (java_object != nullptr) {
-        mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object);
-        // If java_object is a weak global ref whose referent has been cleared,
-        // obj will be NULL.  Otherwise, obj should always be non-NULL
-        // and valid.
-        if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
-          Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
-          JniAbortF(function_name_, "field operation on invalid %s: %p",
-                    ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
-          return;
-        } else {
-          if (!obj->InstanceOf(field_type)) {
-            JniAbortF(function_name_, "attempt to set field %s with value of wrong type: %s",
-                      PrettyField(f.Get()).c_str(), PrettyTypeOf(obj).c_str());
-            return;
-          }
-        }
-      }
-    } else if (field_type != Runtime::Current()->GetClassLinker()->FindPrimitiveClass(prim)) {
-      JniAbortF(function_name_, "attempt to set field %s with value of wrong type: %c",
-                PrettyField(f.Get()).c_str(), prim);
-      return;
-    }
-
-    if (isStatic != f.Get()->IsStatic()) {
-      if (isStatic) {
-        JniAbortF(function_name_, "accessing non-static field %s as static",
-                  PrettyField(f.Get()).c_str());
-      } else {
-        JniAbortF(function_name_, "accessing static field %s as non-static",
-                  PrettyField(f.Get()).c_str());
-      }
-      return;
-    }
+    return true;
   }
 
   /*
@@ -244,59 +153,90 @@
    *
    * Assumes "jobj" has already been validated.
    */
-  void CheckInstanceFieldID(jobject java_object, jfieldID fid)
+  bool CheckInstanceFieldID(ScopedObjectAccess& soa, jobject java_object, jfieldID fid)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
-    if (o == nullptr || !Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+    if (o == nullptr) {
+      AbortF("field operation on NULL object: %p", java_object);
+      return false;
+    }
+    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
-      JniAbortF(function_name_, "field operation on invalid %s: %p",
-                ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
-      return;
+      AbortF("field operation on invalid %s: %p",
+             ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+             java_object);
+      return false;
     }
 
-    mirror::ArtField* f = CheckFieldID(fid);
+    mirror::ArtField* f = CheckFieldID(soa, fid);
     if (f == nullptr) {
-      return;
+      return false;
     }
     mirror::Class* c = o->GetClass();
     if (c->FindInstanceField(f->GetName(), f->GetTypeDescriptor()) == nullptr) {
-      JniAbortF(function_name_, "jfieldID %s not valid for an object of class %s",
-                PrettyField(f).c_str(), PrettyTypeOf(o).c_str());
+      AbortF("jfieldID %s not valid for an object of class %s",
+             PrettyField(f).c_str(), PrettyTypeOf(o).c_str());
+      return false;
     }
+    return true;
   }
 
   /*
    * Verify that the pointer value is non-NULL.
    */
-  void CheckNonNull(const void* ptr) {
-    if (ptr == nullptr) {
-      JniAbortF(function_name_, "non-nullable argument was NULL");
+  bool CheckNonNull(const void* ptr) {
+    if (UNLIKELY(ptr == nullptr)) {
+      AbortF("non-nullable argument was NULL");
+      return false;
     }
+    return true;
   }
 
   /*
    * Verify that the method's return type matches the type of call.
    * 'expectedType' will be "L" for all objects, including arrays.
    */
-  void CheckSig(jmethodID mid, const char* expectedType, bool isStatic)
+  bool CheckMethodAndSig(ScopedObjectAccess& soa, jobject jobj, jclass jc,
+                         jmethodID mid, Primitive::Type type, InvokeType invoke)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* m = CheckMethodID(mid);
+    mirror::ArtMethod* m = CheckMethodID(soa, mid);
     if (m == nullptr) {
-      return;
+      return false;
     }
-    if (*expectedType != m->GetShorty()[0]) {
-      JniAbortF(function_name_, "the return type of %s does not match %s",
-                function_name_, PrettyMethod(m).c_str());
+    if (type != Primitive::GetType(m->GetShorty()[0])) {
+      AbortF("the return type of %s does not match %s", function_name_, PrettyMethod(m).c_str());
+      return false;
     }
-    if (isStatic != m->IsStatic()) {
-      if (isStatic) {
-        JniAbortF(function_name_, "calling non-static method %s with %s",
-                  PrettyMethod(m).c_str(), function_name_);
+    bool is_static = (invoke == kStatic);
+    if (is_static != m->IsStatic()) {
+      if (is_static) {
+        AbortF("calling non-static method %s with %s",
+               PrettyMethod(m).c_str(), function_name_);
       } else {
-        JniAbortF(function_name_, "calling static method %s with %s",
-                  PrettyMethod(m).c_str(), function_name_);
+        AbortF("calling static method %s with %s",
+               PrettyMethod(m).c_str(), function_name_);
+      }
+      return false;
+    }
+    if (invoke != kVirtual) {
+      mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+      if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
+        AbortF("can't call %s %s with class %s", invoke == kStatic ? "static" : "nonvirtual",
+            PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+        return false;
       }
     }
+    if (invoke != kStatic) {
+      mirror::Object* o = soa.Decode<mirror::Object*>(jobj);
+      if (o == nullptr) {
+        AbortF("can't call %s on null object", PrettyMethod(m).c_str());
+        return false;
+      } else if (!o->InstanceOf(m->GetDeclaringClass())) {
+        AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+        return false;
+      }
+    }
+    return true;
   }
 
   /*
@@ -304,17 +244,18 @@
    *
    * Assumes "java_class" has already been validated.
    */
-  void CheckStaticFieldID(jclass java_class, jfieldID fid)
+  bool CheckStaticFieldID(ScopedObjectAccess& soa, jclass java_class, jfieldID fid)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::Class* c = soa_.Decode<mirror::Class*>(java_class);
-    mirror::ArtField* f = CheckFieldID(fid);
+    mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+    mirror::ArtField* f = CheckFieldID(soa, fid);
     if (f == nullptr) {
-      return;
+      return false;
     }
     if (f->GetDeclaringClass() != c) {
-      JniAbortF(function_name_, "static jfieldID %p not valid for class %s",
-                fid, PrettyClass(c).c_str());
+      AbortF("static jfieldID %p not valid for class %s", fid, PrettyClass(c).c_str());
+      return false;
     }
+    return true;
   }
 
   /*
@@ -326,17 +267,18 @@
    *
    * Instances of "java_class" must be instances of the method's declaring class.
    */
-  void CheckStaticMethod(jclass java_class, jmethodID mid)
+  bool CheckStaticMethod(ScopedObjectAccess& soa, jclass java_class, jmethodID mid)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* m = CheckMethodID(mid);
+    mirror::ArtMethod* m = CheckMethodID(soa, mid);
     if (m == nullptr) {
-      return;
+      return false;
     }
-    mirror::Class* c = soa_.Decode<mirror::Class*>(java_class);
+    mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
     if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
-      JniAbortF(function_name_, "can't call static %s on class %s",
-                PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+      AbortF("can't call static %s on class %s", PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+      return false;
     }
+    return true;
   }
 
   /*
@@ -346,19 +288,21 @@
    * (Note the mid might point to a declaration in an interface; this
    * will be handled automatically by the instanceof check.)
    */
-  void CheckVirtualMethod(jobject java_object, jmethodID mid)
+  bool CheckVirtualMethod(ScopedObjectAccess& soa, jobject java_object, jmethodID mid)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* m = CheckMethodID(mid);
+    mirror::ArtMethod* m = CheckMethodID(soa, mid);
     if (m == nullptr) {
-      return;
+      return false;
     }
-    mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
+    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
     if (o == nullptr) {
-      JniAbortF(function_name_, "can't call %s on null object", PrettyMethod(m).c_str());
+      AbortF("can't call %s on null object", PrettyMethod(m).c_str());
+      return false;
     } else if (!o->InstanceOf(m->GetDeclaringClass())) {
-      JniAbortF(function_name_, "can't call %s on instance of %s",
-                PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+      AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+      return false;
     }
+    return true;
   }
 
   /**
@@ -397,11 +341,10 @@
    *
    * Use the kFlag_NullableUtf flag where 'u' field(s) are nullable.
    */
-  void Check(bool entry, const char* fmt0, ...) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    va_list ap;
-
+  bool Check(ScopedObjectAccess& soa, bool entry, const char* fmt, JniValueType* args)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     mirror::ArtMethod* traceMethod = nullptr;
-    if (has_method_ && (!soa_.Vm()->trace.empty() || VLOG_IS_ON(third_party_jni))) {
+    if (has_method_ && soa.Vm()->IsTracingEnabled()) {
       // We need to guard some of the invocation interface's calls: a bad caller might
       // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
       Thread* self = Thread::Current();
@@ -411,124 +354,14 @@
     }
 
     if (((flags_ & kFlag_ForceTrace) != 0) ||
-        (traceMethod != nullptr && ShouldTrace(soa_.Vm(), traceMethod))) {
-      va_start(ap, fmt0);
+        (traceMethod != nullptr && soa.Vm()->ShouldTrace(traceMethod))) {
       std::string msg;
-      for (const char* fmt = fmt0; *fmt;) {
-        char ch = *fmt++;
-        if (ch == 'B') {  // jbyte
-          jbyte b = va_arg(ap, int);
-          if (b >= 0 && b < 10) {
-            StringAppendF(&msg, "%d", b);
-          } else {
-            StringAppendF(&msg, "%#x (%d)", b, b);
-          }
-        } else if (ch == 'C') {  // jchar
-          jchar c = va_arg(ap, int);
-          if (c < 0x7f && c >= ' ') {
-            StringAppendF(&msg, "U+%x ('%c')", c, c);
-          } else {
-            StringAppendF(&msg, "U+%x", c);
-          }
-        } else if (ch == 'F' || ch == 'D') {  // jfloat, jdouble
-          StringAppendF(&msg, "%g", va_arg(ap, double));
-        } else if (ch == 'I' || ch == 'S') {  // jint, jshort
-          StringAppendF(&msg, "%d", va_arg(ap, int));
-        } else if (ch == 'J') {  // jlong
-          StringAppendF(&msg, "%" PRId64, va_arg(ap, jlong));
-        } else if (ch == 'Z') {  // jboolean
-          StringAppendF(&msg, "%s", va_arg(ap, int) ? "true" : "false");
-        } else if (ch == 'V') {  // void
-          msg += "void";
-        } else if (ch == 'v') {  // JavaVM*
-          JavaVM* vm = va_arg(ap, JavaVM*);
-          StringAppendF(&msg, "(JavaVM*)%p", vm);
-        } else if (ch == 'E') {  // JNIEnv*
-          JNIEnv* env = va_arg(ap, JNIEnv*);
-          StringAppendF(&msg, "(JNIEnv*)%p", env);
-        } else if (ch == 'L' || ch == 'a' || ch == 's') {  // jobject, jarray, jstring
-          // For logging purposes, these are identical.
-          jobject o = va_arg(ap, jobject);
-          if (o == nullptr) {
-            msg += "NULL";
-          } else {
-            StringAppendF(&msg, "%p", o);
-          }
-        } else if (ch == 'b') {  // jboolean (JNI-style)
-          jboolean b = va_arg(ap, int);
-          msg += (b ? "JNI_TRUE" : "JNI_FALSE");
-        } else if (ch == 'c') {  // jclass
-          jclass jc = va_arg(ap, jclass);
-          mirror::Class* c = reinterpret_cast<mirror::Class*>(Thread::Current()->DecodeJObject(jc));
-          if (c == nullptr) {
-            msg += "NULL";
-          } else if (c == kInvalidIndirectRefObject ||
-              !Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
-            StringAppendF(&msg, "INVALID POINTER:%p", jc);
-          } else if (!c->IsClass()) {
-            msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
-          } else {
-            msg += PrettyClass(c);
-            if (!entry) {
-              StringAppendF(&msg, " (%p)", jc);
-            }
-          }
-        } else if (ch == 'f') {  // jfieldID
-          jfieldID fid = va_arg(ap, jfieldID);
-          mirror::ArtField* f = reinterpret_cast<mirror::ArtField*>(fid);
-          msg += PrettyField(f);
-          if (!entry) {
-            StringAppendF(&msg, " (%p)", fid);
-          }
-        } else if (ch == 'z') {  // non-negative jsize
-          // You might expect jsize to be size_t, but it's not; it's the same as jint.
-          // We only treat this specially so we can do the non-negative check.
-          // TODO: maybe this wasn't worth it?
-          jint i = va_arg(ap, jint);
-          StringAppendF(&msg, "%d", i);
-        } else if (ch == 'm') {  // jmethodID
-          jmethodID mid = va_arg(ap, jmethodID);
-          mirror::ArtMethod* m = reinterpret_cast<mirror::ArtMethod*>(mid);
-          msg += PrettyMethod(m);
-          if (!entry) {
-            StringAppendF(&msg, " (%p)", mid);
-          }
-        } else if (ch == 'p') {  // void* ("pointer")
-          void* p = va_arg(ap, void*);
-          if (p == nullptr) {
-            msg += "NULL";
-          } else {
-            StringAppendF(&msg, "(void*) %p", p);
-          }
-        } else if (ch == 'r') {  // jint (release mode)
-          jint releaseMode = va_arg(ap, jint);
-          if (releaseMode == 0) {
-            msg += "0";
-          } else if (releaseMode == JNI_ABORT) {
-            msg += "JNI_ABORT";
-          } else if (releaseMode == JNI_COMMIT) {
-            msg += "JNI_COMMIT";
-          } else {
-            StringAppendF(&msg, "invalid release mode %d", releaseMode);
-          }
-        } else if (ch == 'u') {  // const char* (Modified UTF-8)
-          const char* utf = va_arg(ap, const char*);
-          if (utf == nullptr) {
-            msg += "NULL";
-          } else {
-            StringAppendF(&msg, "\"%s\"", utf);
-          }
-        } else if (ch == '.') {
-          msg += "...";
-        } else {
-          JniAbortF(function_name_, "unknown trace format specifier: %c", ch);
-          return;
-        }
-        if (*fmt) {
+      for (size_t i = 0; fmt[i] != '\0'; ++i) {
+        TracePossibleHeapValue(soa, entry, fmt[i], args[i], &msg);
+        if (fmt[i + 1] != '\0') {
           StringAppendF(&msg, ", ");
         }
       }
-      va_end(ap);
 
       if ((flags_ & kFlag_ForceTrace) != 0) {
         LOG(INFO) << "JNI: call to " << function_name_ << "(" << msg << ")";
@@ -548,43 +381,227 @@
 
     // We always do the thorough checks on entry, and never on exit...
     if (entry) {
-      va_start(ap, fmt0);
-      for (const char* fmt = fmt0; *fmt; ++fmt) {
-        char ch = *fmt;
-        if (ch == 'a') {
-          CheckArray(va_arg(ap, jarray));
-        } else if (ch == 'c') {
-          CheckInstance(kClass, va_arg(ap, jclass));
-        } else if (ch == 'L') {
-          CheckObject(va_arg(ap, jobject));
-        } else if (ch == 'r') {
-          CheckReleaseMode(va_arg(ap, jint));
-        } else if (ch == 's') {
-          CheckInstance(kString, va_arg(ap, jstring));
-        } else if (ch == 'u') {
-          if ((flags_ & kFlag_Release) != 0) {
-            CheckNonNull(va_arg(ap, const char*));
-          } else {
-            bool nullable = ((flags_ & kFlag_NullableUtf) != 0);
-            CheckUtfString(va_arg(ap, const char*), nullable);
-          }
-        } else if (ch == 'z') {
-          CheckLengthPositive(va_arg(ap, jsize));
-        } else if (strchr("BCISZbfmpEv", ch) != nullptr) {
-          va_arg(ap, uint32_t);  // Skip this argument.
-        } else if (ch == 'D' || ch == 'F') {
-          va_arg(ap, double);  // Skip this argument.
-        } else if (ch == 'J') {
-          va_arg(ap, uint64_t);  // Skip this argument.
-        } else if (ch == '.') {
-        } else {
-          LOG(FATAL) << "Unknown check format specifier: " << ch;
+      for (size_t i = 0; fmt[i] != '\0'; ++i) {
+        if (!CheckPossibleHeapValue(soa, fmt[i], args[i])) {
+          return false;
         }
       }
-      va_end(ap);
     }
+    return true;
   }
 
+  bool CheckNonHeap(JavaVMExt* vm, bool entry, const char* fmt, JniValueType* args) {
+    bool should_trace = (flags_ & kFlag_ForceTrace) != 0;
+    if (!should_trace && vm->IsTracingEnabled()) {
+      // We need to guard some of the invocation interface's calls: a bad caller might
+      // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
+      Thread* self = Thread::Current();
+      if ((flags_ & kFlag_Invocation) == 0 || self != nullptr) {
+        ScopedObjectAccess soa(self);
+        mirror::ArtMethod* traceMethod = self->GetCurrentMethod(nullptr);
+        should_trace = (traceMethod != nullptr && vm->ShouldTrace(traceMethod));
+      }
+    }
+    if (should_trace) {
+      std::string msg;
+      for (size_t i = 0; fmt[i] != '\0'; ++i) {
+        TraceNonHeapValue(fmt[i], args[i], &msg);
+        if (fmt[i + 1] != '\0') {
+          StringAppendF(&msg, ", ");
+        }
+      }
+
+      if ((flags_ & kFlag_ForceTrace) != 0) {
+        LOG(INFO) << "JNI: call to " << function_name_ << "(" << msg << ")";
+      } else if (entry) {
+        if (has_method_) {
+          Thread* self = Thread::Current();
+          ScopedObjectAccess soa(self);
+          mirror::ArtMethod* traceMethod = self->GetCurrentMethod(nullptr);
+          std::string methodName(PrettyMethod(traceMethod, false));
+          LOG(INFO) << "JNI: " << methodName << " -> " << function_name_ << "(" << msg << ")";
+          indent_ = methodName.size() + 1;
+        } else {
+          LOG(INFO) << "JNI: -> " << function_name_ << "(" << msg << ")";
+          indent_ = 0;
+        }
+      } else {
+        LOG(INFO) << StringPrintf("JNI: %*s<- %s returned %s", indent_, "", function_name_, msg.c_str());
+      }
+    }
+
+    // We always do the thorough checks on entry, and never on exit...
+    if (entry) {
+      for (size_t i = 0; fmt[i] != '\0'; ++i) {
+        if (!CheckNonHeapValue(fmt[i], args[i])) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  bool CheckReflectedMethod(ScopedObjectAccess& soa, jobject jmethod)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::Object* method = soa.Decode<mirror::Object*>(jmethod);
+    if (method == nullptr) {
+      AbortF("expected non-null method");
+      return false;
+    }
+    mirror::Class* c = method->GetClass();
+    if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Method) != c &&
+        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Constructor) != c) {
+      AbortF("expected java.lang.reflect.Method or "
+          "java.lang.reflect.Constructor but got object of type %s: %p",
+          PrettyTypeOf(method).c_str(), jmethod);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckConstructor(ScopedObjectAccess& soa, jmethodID mid)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* method = soa.DecodeMethod(mid);
+    if (method == nullptr) {
+      AbortF("expected non-null constructor");
+      return false;
+    }
+    if (!method->IsConstructor() || method->IsStatic()) {
+      AbortF("expected a constructor but %s: %p", PrettyTypeOf(method).c_str(), mid);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckReflectedField(ScopedObjectAccess& soa, jobject jfield)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::Object* field = soa.Decode<mirror::Object*>(jfield);
+    if (field == nullptr) {
+      AbortF("expected non-null java.lang.reflect.Field");
+      return false;
+    }
+    mirror::Class* c = field->GetClass();
+    if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Field) != c) {
+      AbortF("expected java.lang.reflect.Field but got object of type %s: %p",
+             PrettyTypeOf(field).c_str(), jfield);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckThrowable(ScopedObjectAccess& soa, jthrowable jobj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+    if (!obj->GetClass()->IsThrowableClass()) {
+      AbortF("expected java.lang.Throwable but got object of type "
+             "%s: %p", PrettyTypeOf(obj).c_str(), obj);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckThrowableClass(ScopedObjectAccess& soa, jclass jc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+    if (!c->IsThrowableClass()) {
+      AbortF("expected java.lang.Throwable class but got object of "
+             "type %s: %p", PrettyDescriptor(c).c_str(), c);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckReferenceKind(IndirectRefKind expected_kind, JavaVMExt* vm, Thread* self, jobject obj) {
+    IndirectRefKind found_kind;
+    if (expected_kind == kLocal) {
+      found_kind = GetIndirectRefKind(obj);
+      if (found_kind == kHandleScopeOrInvalid && self->HandleScopeContains(obj)) {
+        found_kind = kLocal;
+      }
+    } else {
+      found_kind = GetIndirectRefKind(obj);
+    }
+    if (obj != nullptr && found_kind != expected_kind) {
+      AbortF("expected reference of kind %s but found %s: %p",
+             ToStr<IndirectRefKind>(expected_kind).c_str(),
+             ToStr<IndirectRefKind>(GetIndirectRefKind(obj)).c_str(),
+             obj);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckInstantiableNonArray(ScopedObjectAccess& soa, jclass jc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+    if (!c->IsInstantiableNonArray()) {
+      AbortF("can't make objects of type %s: %p", PrettyDescriptor(c).c_str(), c);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckPrimitiveArrayType(ScopedObjectAccess& soa, jarray array, Primitive::Type type)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (!CheckArray(soa, array)) {
+      return false;
+    }
+    mirror::Array* a = soa.Decode<mirror::Array*>(array);
+    if (a->GetClass()->GetComponentType()->GetPrimitiveType() != type) {
+      AbortF("incompatible array type %s expected %s[]: %p",
+             PrettyDescriptor(a->GetClass()).c_str(), PrettyDescriptor(type).c_str(), array);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckFieldAccess(ScopedObjectAccess& soa, jobject obj, jfieldID fid, bool is_static,
+                        Primitive::Type type)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (is_static && !CheckStaticFieldID(soa, down_cast<jclass>(obj), fid)) {
+      return false;
+    }
+    if (!is_static && !CheckInstanceFieldID(soa, obj, fid)) {
+      return false;
+    }
+    mirror::ArtField* field = soa.DecodeField(fid);
+    DCHECK(field != nullptr);  // Already checked by Check.
+    if (is_static != field->IsStatic()) {
+      AbortF("attempt to access %s field %s: %p",
+             field->IsStatic() ? "static" : "non-static", PrettyField(field).c_str(), fid);
+      return false;
+    }
+    if (type != field->GetTypeAsPrimitiveType()) {
+      AbortF("attempt to access field %s of type %s with the wrong type %s: %p",
+             PrettyField(field).c_str(), PrettyDescriptor(field->GetTypeDescriptor()).c_str(),
+             PrettyDescriptor(type).c_str(), fid);
+      return false;
+    }
+    if (is_static) {
+      mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+      if (o == nullptr || !o->IsClass()) {
+        AbortF("attempt to access static field %s with a class argument of type %s: %p",
+               PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
+        return false;
+      }
+      mirror::Class* c = o->AsClass();
+      if (field->GetDeclaringClass() != c) {
+        AbortF("attempt to access static field %s with an incompatible class argument of %s: %p",
+               PrettyField(field).c_str(), PrettyDescriptor(c).c_str(), fid);
+        return false;
+      }
+    } else {
+      mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+      if (o == nullptr || !field->GetDeclaringClass()->IsAssignableFrom(o->GetClass())) {
+        AbortF("attempt to access field %s from an object argument of type %s: %p",
+               PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
   enum InstanceKind {
     kClass,
     kDirectByteBuffer,
@@ -600,7 +617,7 @@
    * Because we're looking at an object on the GC heap, we have to switch
    * to "running" mode before doing the checks.
    */
-  bool CheckInstance(InstanceKind kind, jobject java_object)
+  bool CheckInstance(ScopedObjectAccess& soa, InstanceKind kind, jobject java_object, bool null_ok)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     const char* what = nullptr;
     switch (kind) {
@@ -624,15 +641,38 @@
     }
 
     if (java_object == nullptr) {
-      JniAbortF(function_name_, "%s received null %s", function_name_, what);
-      return false;
+      if (null_ok) {
+        return true;
+      } else {
+        AbortF("%s received NULL %s", function_name_, what);
+        return false;
+      }
     }
 
-    mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object);
+    mirror::Object* obj = soa.Decode<mirror::Object*>(java_object);
+    if (obj == nullptr) {
+      // Either java_object is invalid or is a cleared weak.
+      IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
+      bool okay;
+      if (GetIndirectRefKind(ref) != kWeakGlobal) {
+        okay = false;
+      } else {
+        obj = soa.Vm()->DecodeWeakGlobal(soa.Self(), ref);
+        okay = Runtime::Current()->IsClearedJniWeakGlobal(obj);
+      }
+      if (!okay) {
+        AbortF("%s is an invalid %s: %p (%p)",
+               what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+               java_object, obj);
+        return false;
+      }
+    }
+
     if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
-      JniAbortF(function_name_, "%s is an invalid %s: %p (%p)",
-                what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object, obj);
+      AbortF("%s is an invalid %s: %p (%p)",
+             what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+             java_object, obj);
       return false;
     }
 
@@ -654,114 +694,332 @@
       break;
     }
     if (!okay) {
-      JniAbortF(function_name_, "%s has wrong type: %s", what, PrettyTypeOf(obj).c_str());
+      AbortF("%s has wrong type: %s", what, PrettyTypeOf(obj).c_str());
       return false;
     }
 
     return true;
   }
 
- private:
-  // Set "has_method" to true if we have a valid thread with a method pointer.
-  // We won't have one before attaching a thread, after detaching a thread, or
-  // when shutting down the runtime.
-  void Init(int flags, const char* functionName, bool has_method) {
-    flags_ = flags;
-    function_name_ = functionName;
-    has_method_ = has_method;
+  /*
+   * Verify that the "mode" argument passed to a primitive array Release
+   * function is one of the valid values.
+   */
+  bool CheckReleaseMode(jint mode) {
+    if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
+      AbortF("unknown value for release mode: %d", mode);
+      return false;
+    }
+    return true;
   }
 
+  bool CheckPossibleHeapValue(ScopedObjectAccess& soa, char fmt, JniValueType arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    switch (fmt) {
+      case 'a':  // jarray
+        return CheckArray(soa, arg.a);
+      case 'c':  // jclass
+        return CheckInstance(soa, kClass, arg.c, false);
+      case 'f':  // jfieldID
+        return CheckFieldID(soa, arg.f) != nullptr;
+      case 'm':  // jmethodID
+        return CheckMethodID(soa, arg.m) != nullptr;
+      case 'r':  // release int
+        return CheckReleaseMode(arg.r);
+      case 's':  // jstring
+        return CheckInstance(soa, kString, arg.s, false);
+      case 't':  // jthrowable
+        return CheckInstance(soa, kThrowable, arg.t, false);
+      case 'E':  // JNIEnv*
+        return CheckThread(arg.E);
+      case 'L':  // jobject
+        return CheckInstance(soa, kObject, arg.L, true);
+      default:
+        return CheckNonHeapValue(fmt, arg);
+    }
+  }
+
+  bool CheckNonHeapValue(char fmt, JniValueType arg) {
+    switch (fmt) {
+      case '.':  // ...
+      case 'p':  // TODO: pointer - null or readable?
+      case 'v':  // JavaVM*
+      case 'B':  // jbyte
+      case 'C':  // jchar
+      case 'D':  // jdouble
+      case 'F':  // jfloat
+      case 'I':  // jint
+      case 'J':  // jlong
+      case 'S':  // jshort
+        break;  // Ignored.
+      case 'b':  // jboolean, why two? Fall-through.
+      case 'Z':
+        return CheckBoolean(arg.Z);
+      case 'u':  // utf8
+        if ((flags_ & kFlag_Release) != 0) {
+          return CheckNonNull(arg.u);
+        } else {
+          bool nullable = ((flags_ & kFlag_NullableUtf) != 0);
+          return CheckUtfString(arg.u, nullable);
+        }
+      case 'w':  // jobjectRefType
+        switch (arg.w) {
+          case JNIInvalidRefType:
+          case JNILocalRefType:
+          case JNIGlobalRefType:
+          case JNIWeakGlobalRefType:
+            break;
+          default:
+            AbortF("Unknown reference type");
+            return false;
+        }
+        break;
+      case 'z':  // jsize
+        return CheckLengthPositive(arg.z);
+      default:
+        AbortF("unknown format specifier: '%c'", fmt);
+        return false;
+    }
+    return true;
+  }
+
+  void TracePossibleHeapValue(ScopedObjectAccess& soa, bool entry, char fmt, JniValueType arg,
+                              std::string* msg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    switch (fmt) {
+      case 'L':  // jobject fall-through.
+      case 'a':  // jarray fall-through.
+      case 's':  // jstring fall-through.
+      case 't':  // jthrowable fall-through.
+        if (arg.L == nullptr) {
+          *msg += "NULL";
+        } else {
+          StringAppendF(msg, "%p", arg.L);
+        }
+        break;
+      case 'c': {  // jclass
+        jclass jc = arg.c;
+        mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+        if (c == nullptr) {
+          *msg += "NULL";
+        } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+          StringAppendF(msg, "INVALID POINTER:%p", jc);
+        } else if (!c->IsClass()) {
+          *msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
+        } else {
+          *msg += PrettyClass(c);
+          if (!entry) {
+            StringAppendF(msg, " (%p)", jc);
+          }
+        }
+        break;
+      }
+      case 'f': {  // jfieldID
+        jfieldID fid = arg.f;
+        mirror::ArtField* f = soa.DecodeField(fid);
+        *msg += PrettyField(f);
+        if (!entry) {
+          StringAppendF(msg, " (%p)", fid);
+        }
+        break;
+      }
+      case 'm': {  // jmethodID
+        jmethodID mid = arg.m;
+        mirror::ArtMethod* m = soa.DecodeMethod(mid);
+        *msg += PrettyMethod(m);
+        if (!entry) {
+          StringAppendF(msg, " (%p)", mid);
+        }
+        break;
+      }
+      default:
+        TraceNonHeapValue(fmt, arg, msg);
+        break;
+    }
+  }
+
+  void TraceNonHeapValue(char fmt, JniValueType arg, std::string* msg) {
+    switch (fmt) {
+      case 'B':  // jbyte
+        if (arg.B >= 0 && arg.B < 10) {
+          StringAppendF(msg, "%d", arg.B);
+        } else {
+          StringAppendF(msg, "%#x (%d)", arg.B, arg.B);
+        }
+        break;
+      case 'C':  // jchar
+        if (arg.C < 0x7f && arg.C >= ' ') {
+          StringAppendF(msg, "U+%x ('%c')", arg.C, arg.C);
+        } else {
+          StringAppendF(msg, "U+%x", arg.C);
+        }
+        break;
+      case 'F':  // jfloat
+        StringAppendF(msg, "%g", arg.F);
+        break;
+      case 'D':  // jdouble
+        StringAppendF(msg, "%g", arg.D);
+        break;
+      case 'S':  // jshort
+        StringAppendF(msg, "%d", arg.S);
+        break;
+      case 'i':  // jint - fall-through.
+      case 'I':  // jint
+        StringAppendF(msg, "%d", arg.I);
+        break;
+      case 'J':  // jlong
+        StringAppendF(msg, "%" PRId64, arg.J);
+        break;
+      case 'Z':  // jboolean
+      case 'b':  // jboolean (JNI-style)
+        *msg += arg.b == JNI_TRUE ? "true" : "false";
+        break;
+      case 'V':  // void
+        DCHECK(arg.V == nullptr);
+        *msg += "void";
+        break;
+      case 'v':  // JavaVM*
+        StringAppendF(msg, "(JavaVM*)%p", arg.v);
+        break;
+      case 'E':
+        StringAppendF(msg, "(JNIEnv*)%p", arg.E);
+        break;
+      case 'z':  // non-negative jsize
+        // You might expect jsize to be size_t, but it's not; it's the same as jint.
+        // We only treat this specially so we can do the non-negative check.
+        // TODO: maybe this wasn't worth it?
+        StringAppendF(msg, "%d", arg.z);
+        break;
+      case 'p':  // void* ("pointer")
+        if (arg.p == nullptr) {
+          *msg += "NULL";
+        } else {
+          StringAppendF(msg, "(void*) %p", arg.p);
+        }
+        break;
+      case 'r': {  // jint (release mode)
+        jint releaseMode = arg.r;
+        if (releaseMode == 0) {
+          *msg += "0";
+        } else if (releaseMode == JNI_ABORT) {
+          *msg += "JNI_ABORT";
+        } else if (releaseMode == JNI_COMMIT) {
+          *msg += "JNI_COMMIT";
+        } else {
+          StringAppendF(msg, "invalid release mode %d", releaseMode);
+        }
+        break;
+      }
+      case 'u':  // const char* (Modified UTF-8)
+        if (arg.u == nullptr) {
+          *msg += "NULL";
+        } else {
+          StringAppendF(msg, "\"%s\"", arg.u);
+        }
+        break;
+      case 'w':  // jobjectRefType
+        switch (arg.w) {
+          case JNIInvalidRefType:
+            *msg += "invalid reference type";
+            break;
+          case JNILocalRefType:
+            *msg += "local ref type";
+            break;
+          case JNIGlobalRefType:
+            *msg += "global ref type";
+            break;
+          case JNIWeakGlobalRefType:
+            *msg += "weak global ref type";
+            break;
+          default:
+            *msg += "unknown ref type";
+            break;
+        }
+        break;
+      case '.':
+        *msg += "...";
+        break;
+      default:
+        LOG(FATAL) << function_name_ << ": unknown trace format specifier: '" << fmt << "'";
+    }
+  }
   /*
    * Verify that "array" is non-NULL and points to an Array object.
    *
    * Since we're dealing with objects, switch to "running" mode.
    */
-  void CheckArray(jarray java_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (java_array == nullptr) {
-      JniAbortF(function_name_, "jarray was NULL");
-      return;
+  bool CheckArray(ScopedObjectAccess& soa, jarray java_array)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (UNLIKELY(java_array == nullptr)) {
+      AbortF("jarray was NULL");
+      return false;
     }
 
-    mirror::Array* a = soa_.Decode<mirror::Array*>(java_array);
-    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(a)) {
+    mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+    if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a))) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
-      JniAbortF(function_name_, "jarray is an invalid %s: %p (%p)",
-                ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(), java_array, a);
+      AbortF("jarray is an invalid %s: %p (%p)",
+             ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(),
+             java_array, a);
+      return false;
     } else if (!a->IsArrayInstance()) {
-      JniAbortF(function_name_, "jarray argument has non-array type: %s", PrettyTypeOf(a).c_str());
+      AbortF("jarray argument has non-array type: %s", PrettyTypeOf(a).c_str());
+      return false;
     }
+    return true;
   }
 
-  void CheckLengthPositive(jsize length) {
+  bool CheckBoolean(jboolean z) {
+    if (z != JNI_TRUE && z != JNI_FALSE) {
+      AbortF("unexpected jboolean value: %d", z);
+      return false;
+    }
+    return true;
+  }
+
+  bool CheckLengthPositive(jsize length) {
     if (length < 0) {
-      JniAbortF(function_name_, "negative jsize: %d", length);
+      AbortF("negative jsize: %d", length);
+      return false;
     }
+    return true;
   }
 
-  mirror::ArtField* CheckFieldID(jfieldID fid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* CheckFieldID(ScopedObjectAccess& soa, jfieldID fid)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (fid == nullptr) {
-      JniAbortF(function_name_, "jfieldID was NULL");
+      AbortF("jfieldID was NULL");
       return nullptr;
     }
-    mirror::ArtField* f = soa_.DecodeField(fid);
+    mirror::ArtField* f = soa.DecodeField(fid);
     if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f) || !f->IsArtField()) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
-      JniAbortF(function_name_, "invalid jfieldID: %p", fid);
+      AbortF("invalid jfieldID: %p", fid);
       return nullptr;
     }
     return f;
   }
 
-  mirror::ArtMethod* CheckMethodID(jmethodID mid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtMethod* CheckMethodID(ScopedObjectAccess& soa, jmethodID mid)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (mid == nullptr) {
-      JniAbortF(function_name_, "jmethodID was NULL");
+      AbortF("jmethodID was NULL");
       return nullptr;
     }
-    mirror::ArtMethod* m = soa_.DecodeMethod(mid);
+    mirror::ArtMethod* m = soa.DecodeMethod(mid);
     if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(m) || !m->IsArtMethod()) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
-      JniAbortF(function_name_, "invalid jmethodID: %p", mid);
+      AbortF("invalid jmethodID: %p", mid);
       return nullptr;
     }
     return m;
   }
 
-  /*
-   * Verify that "jobj" is a valid object, and that it's an object that JNI
-   * is allowed to know about.  We allow NULL references.
-   *
-   * Switches to "running" mode before performing checks.
-   */
-  void CheckObject(jobject java_object)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (java_object == nullptr) {
-      return;
-    }
-
-    mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
-    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
-      Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
-      // TODO: when we remove work_around_app_jni_bugs, this should be impossible.
-      JniAbortF(function_name_, "native code passing in reference to invalid %s: %p",
-                ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
-    }
-  }
-
-  /*
-   * Verify that the "mode" argument passed to a primitive array Release
-   * function is one of the valid values.
-   */
-  void CheckReleaseMode(jint mode) {
-    if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
-      JniAbortF(function_name_, "unknown value for release mode: %d", mode);
-    }
-  }
-
-  void CheckThread(int flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  bool CheckThread(JNIEnv* env) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     Thread* self = Thread::Current();
     if (self == nullptr) {
-      JniAbortF(function_name_, "a thread (tid %d) is making JNI calls without being attached", GetTid());
-      return;
+      AbortF("a thread (tid %d) is making JNI calls without being attached", GetTid());
+      return false;
     }
 
     // Get the *correct* JNIEnv by going through our TLS pointer.
@@ -769,21 +1027,22 @@
 
     // Verify that the current thread is (a) attached and (b) associated with
     // this particular instance of JNIEnv.
-    if (soa_.Env() != threadEnv) {
-      JniAbortF(function_name_, "thread %s using JNIEnv* from thread %s",
-                ToStr<Thread>(*self).c_str(), ToStr<Thread>(*soa_.Self()).c_str());
-      return;
+    if (env != threadEnv) {
+      AbortF("thread %s using JNIEnv* from thread %s",
+             ToStr<Thread>(*self).c_str(), ToStr<Thread>(*self).c_str());
+      return false;
     }
 
     // Verify that, if this thread previously made a critical "get" call, we
     // do the corresponding "release" call before we try anything else.
-    switch (flags & kFlag_CritMask) {
+    switch (flags_ & kFlag_CritMask) {
     case kFlag_CritOkay:    // okay to call this method
       break;
     case kFlag_CritBad:     // not okay to call
       if (threadEnv->critical) {
-        JniAbortF(function_name_, "thread %s using JNI after critical get", ToStr<Thread>(*self).c_str());
-        return;
+        AbortF("thread %s using JNI after critical get",
+               ToStr<Thread>(*self).c_str());
+        return false;
       }
       break;
     case kFlag_CritGet:     // this is a "get" call
@@ -793,44 +1052,46 @@
     case kFlag_CritRelease:  // this is a "release" call
       threadEnv->critical--;
       if (threadEnv->critical < 0) {
-        JniAbortF(function_name_, "thread %s called too many critical releases", ToStr<Thread>(*self).c_str());
-        return;
+        AbortF("thread %s called too many critical releases",
+               ToStr<Thread>(*self).c_str());
+        return false;
       }
       break;
     default:
-      LOG(FATAL) << "Bad flags (internal error): " << flags;
+      LOG(FATAL) << "Bad flags (internal error): " << flags_;
     }
 
     // Verify that, if an exception has been raised, the native code doesn't
     // make any JNI calls other than the Exception* methods.
-    if ((flags & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
+    if ((flags_ & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
       ThrowLocation throw_location;
       mirror::Throwable* exception = self->GetException(&throw_location);
       std::string type(PrettyTypeOf(exception));
-      JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s",
-                function_name_, type.c_str(), throw_location.Dump().c_str());
-      return;
+      AbortF("JNI %s called with pending exception '%s' thrown in %s",
+             function_name_, type.c_str(), throw_location.Dump().c_str());
+      return false;
     }
+    return true;
   }
 
   // Verifies that "bytes" points to valid Modified UTF-8 data.
-  void CheckUtfString(const char* bytes, bool nullable) {
+  bool CheckUtfString(const char* bytes, bool nullable) {
     if (bytes == nullptr) {
       if (!nullable) {
-        JniAbortF(function_name_, "non-nullable const char* was NULL");
-        return;
+        AbortF("non-nullable const char* was NULL");
+        return false;
       }
-      return;
+      return true;
     }
 
     const char* errorKind = nullptr;
     uint8_t utf8 = CheckUtfBytes(bytes, &errorKind);
     if (errorKind != nullptr) {
-      JniAbortF(function_name_,
-                "input is not valid Modified UTF-8: illegal %s byte %#x\n"
-                "    string: '%s'", errorKind, utf8, bytes);
-      return;
+      AbortF("input is not valid Modified UTF-8: illegal %s byte %#x\n"
+          "    string: '%s'", errorKind, utf8, bytes);
+      return false;
     }
+    return true;
   }
 
   static uint8_t CheckUtfBytes(const char* bytes, const char** errorKind) {
@@ -882,92 +1143,120 @@
     return 0;
   }
 
-  const ScopedObjectAccess soa_;
-  const char* function_name_;
-  int flags_;
-  bool has_method_;
+  void AbortF(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+    va_list args;
+    va_start(args, fmt);
+    Runtime::Current()->GetJavaVM()->JniAbortV(function_name_, fmt, args);
+    va_end(args);
+  }
+
+  // The name of the JNI function being checked.
+  const char* const function_name_;
+
+  const int flags_;
   int indent_;
 
+  const bool has_method_;
+
   DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
 };
 
-#define CHECK_JNI_ENTRY(flags, types, args...) \
-  ScopedCheck sc(env, flags, __FUNCTION__); \
-  sc.Check(true, types, ##args)
-
-#define CHECK_JNI_EXIT(type, exp) ({ \
-    auto _rc = (exp); \
-  sc.Check(false, type, _rc); \
-  _rc; })
-#define CHECK_JNI_EXIT_VOID() \
-  sc.Check(false, "V")
-
 /*
  * ===========================================================================
  *      Guarded arrays
  * ===========================================================================
  */
 
-#define kGuardLen       512         /* must be multiple of 2 */
-#define kGuardPattern   0xd5e3      /* uncommon values; d5e3d5e3 invalid addr */
-#define kGuardMagic     0xffd5aa96
-
 /* this gets tucked in at the start of the buffer; struct size must be even */
-struct GuardedCopy {
-  uint32_t magic;
-  uLong adler;
-  size_t original_length;
-  const void* original_ptr;
-
-  /* find the GuardedCopy given the pointer into the "live" data */
-  static inline const GuardedCopy* FromData(const void* dataBuf) {
-    return reinterpret_cast<const GuardedCopy*>(ActualBuffer(dataBuf));
-  }
-
+class GuardedCopy {
+ public:
   /*
    * Create an over-sized buffer to hold the contents of "buf".  Copy it in,
    * filling in the area around it with guard data.
-   *
-   * We use a 16-bit pattern to make a rogue memset less likely to elude us.
    */
-  static void* Create(const void* buf, size_t len, bool modOkay) {
-    size_t newLen = ActualLength(len);
-    uint8_t* newBuf = DebugAlloc(newLen);
-
-    // Fill it in with a pattern.
-    uint16_t* pat = reinterpret_cast<uint16_t*>(newBuf);
-    for (size_t i = 0; i < newLen / 2; i++) {
-      *pat++ = kGuardPattern;
-    }
-
-    // Copy the data in; note "len" could be zero.
-    memcpy(newBuf + kGuardLen / 2, buf, len);
+  static void* Create(const void* original_buf, size_t len, bool mod_okay) {
+    const size_t new_len = LengthIncludingRedZones(len);
+    uint8_t* const new_buf = DebugAlloc(new_len);
 
     // If modification is not expected, grab a checksum.
     uLong adler = 0;
-    if (!modOkay) {
-      adler = adler32(0L, Z_NULL, 0);
-      adler = adler32(adler, reinterpret_cast<const Bytef*>(buf), len);
-      *reinterpret_cast<uLong*>(newBuf) = adler;
+    if (!mod_okay) {
+      adler = adler32(adler32(0L, Z_NULL, 0), reinterpret_cast<const Bytef*>(original_buf), len);
     }
 
-    GuardedCopy* pExtra = reinterpret_cast<GuardedCopy*>(newBuf);
-    pExtra->magic = kGuardMagic;
-    pExtra->adler = adler;
-    pExtra->original_ptr = buf;
-    pExtra->original_length = len;
+    GuardedCopy* copy = new (new_buf) GuardedCopy(original_buf, len, adler);
 
-    return newBuf + kGuardLen / 2;
+    // Fill begin region with canary pattern.
+    const size_t kStartCanaryLength = (GuardedCopy::kRedZoneSize / 2) - sizeof(GuardedCopy);
+    for (size_t i = 0, j = 0; i < kStartCanaryLength; ++i) {
+      const_cast<char*>(copy->StartRedZone())[i] = kCanary[j];
+      if (kCanary[j] == '\0') {
+        j = 0;
+      }
+    }
+
+    // Copy the data in; note "len" could be zero.
+    memcpy(const_cast<uint8_t*>(copy->BufferWithinRedZones()), original_buf, len);
+
+    // Fill end region with canary pattern.
+    for (size_t i = 0, j = 0; i < kEndCanaryLength; ++i) {
+      const_cast<char*>(copy->EndRedZone())[i] = kCanary[j];
+      if (kCanary[j] == '\0') {
+        j = 0;
+      }
+    }
+
+    return const_cast<uint8_t*>(copy->BufferWithinRedZones());
   }
 
   /*
+   * Create a guarded copy of a primitive array.  Modifications to the copied
+   * data are allowed.  Returns a pointer to the copied data.
+   */
+  static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* is_copy) {
+    ScopedObjectAccess soa(env);
+
+    mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+    size_t component_size = a->GetClass()->GetComponentSize();
+    size_t byte_count = a->GetLength() * component_size;
+    void* result = Create(a->GetRawData(component_size, 0), byte_count, true);
+    if (is_copy != nullptr) {
+      *is_copy = JNI_TRUE;
+    }
+    return result;
+  }
+
+  /*
+   * Perform the array "release" operation, which may or may not copy data
+   * back into the managed heap, and may or may not release the underlying storage.
+   */
+  static void* ReleaseGuardedPACopy(const char* function_name, JNIEnv* env, jarray java_array,
+                                   void* embedded_buf, int mode) {
+    ScopedObjectAccess soa(env);
+    mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+
+    if (!GuardedCopy::Check(function_name, embedded_buf, true)) {
+      return nullptr;
+    }
+    if (mode != JNI_ABORT) {
+      size_t len = FromEmbedded(embedded_buf)->original_length_;
+      memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), embedded_buf, len);
+    }
+    if (mode != JNI_COMMIT) {
+      return Destroy(embedded_buf);
+    }
+    return embedded_buf;
+  }
+
+
+  /*
    * Free up the guard buffer, scrub it, and return the original pointer.
    */
-  static void* Destroy(void* dataBuf) {
-    const GuardedCopy* pExtra = GuardedCopy::FromData(dataBuf);
-    void* original_ptr = const_cast<void*>(pExtra->original_ptr);
-    size_t len = pExtra->original_length;
-    DebugFree(dataBuf, len);
+  static void* Destroy(void* embedded_buf) {
+    GuardedCopy* copy = FromEmbedded(embedded_buf);
+    void* original_ptr = const_cast<void*>(copy->original_ptr_);
+    size_t len = LengthIncludingRedZones(copy->original_length_);
+    DebugFree(copy, len);
     return original_ptr;
   }
 
@@ -977,67 +1266,16 @@
    *
    * The caller has already checked that "dataBuf" is non-NULL.
    */
-  static void Check(const char* functionName, const void* dataBuf, bool modOkay) {
-    static const uint32_t kMagicCmp = kGuardMagic;
-    const uint8_t* fullBuf = ActualBuffer(dataBuf);
-    const GuardedCopy* pExtra = GuardedCopy::FromData(dataBuf);
-
-    // Before we do anything with "pExtra", check the magic number.  We
-    // do the check with memcmp rather than "==" in case the pointer is
-    // unaligned.  If it points to completely bogus memory we're going
-    // to crash, but there's no easy way around that.
-    if (memcmp(&pExtra->magic, &kMagicCmp, 4) != 0) {
-      uint8_t buf[4];
-      memcpy(buf, &pExtra->magic, 4);
-      JniAbortF(functionName,
-          "guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
-          buf[3], buf[2], buf[1], buf[0], dataBuf);  // Assumes little-endian.
-    }
-
-    size_t len = pExtra->original_length;
-
-    // Check bottom half of guard; skip over optional checksum storage.
-    const uint16_t* pat = reinterpret_cast<const uint16_t*>(fullBuf);
-    for (size_t i = sizeof(GuardedCopy) / 2; i < (kGuardLen / 2 - sizeof(GuardedCopy)) / 2; i++) {
-      if (pat[i] != kGuardPattern) {
-        JniAbortF(functionName, "guard pattern(1) disturbed at %p +%zd", fullBuf, i*2);
-      }
-    }
-
-    int offset = kGuardLen / 2 + len;
-    if (offset & 0x01) {
-      // Odd byte; expected value depends on endian.
-      const uint16_t patSample = kGuardPattern;
-      uint8_t expected_byte = reinterpret_cast<const uint8_t*>(&patSample)[1];
-      if (fullBuf[offset] != expected_byte) {
-        JniAbortF(functionName, "guard pattern disturbed in odd byte after %p +%d 0x%02x 0x%02x",
-                  fullBuf, offset, fullBuf[offset], expected_byte);
-      }
-      offset++;
-    }
-
-    // Check top half of guard.
-    pat = reinterpret_cast<const uint16_t*>(fullBuf + offset);
-    for (size_t i = 0; i < kGuardLen / 4; i++) {
-      if (pat[i] != kGuardPattern) {
-        JniAbortF(functionName, "guard pattern(2) disturbed at %p +%zd", fullBuf, offset + i*2);
-      }
-    }
-
-    // If modification is not expected, verify checksum.  Strictly speaking
-    // this is wrong: if we told the client that we made a copy, there's no
-    // reason they can't alter the buffer.
-    if (!modOkay) {
-      uLong adler = adler32(0L, Z_NULL, 0);
-      adler = adler32(adler, (const Bytef*)dataBuf, len);
-      if (pExtra->adler != adler) {
-        JniAbortF(functionName, "buffer modified (0x%08lx vs 0x%08lx) at address %p",
-                  pExtra->adler, adler, dataBuf);
-      }
-    }
+  static bool Check(const char* function_name, const void* embedded_buf, bool mod_okay) {
+    const GuardedCopy* copy = FromEmbedded(embedded_buf);
+    return copy->CheckHeader(function_name, mod_okay) && copy->CheckRedZones(function_name);
   }
 
  private:
+  GuardedCopy(const void* original_buf, size_t len, uLong adler) :
+    magic_(kGuardMagic), adler_(adler), original_ptr_(original_buf), original_length_(len) {
+  }
+
   static uint8_t* DebugAlloc(size_t len) {
     void* result = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
     if (result == MAP_FAILED) {
@@ -1046,68 +1284,126 @@
     return reinterpret_cast<uint8_t*>(result);
   }
 
-  static void DebugFree(void* dataBuf, size_t len) {
-    uint8_t* fullBuf = ActualBuffer(dataBuf);
-    size_t totalByteCount = ActualLength(len);
-    // TODO: we could mprotect instead, and keep the allocation around for a while.
-    // This would be even more expensive, but it might catch more errors.
-    // if (mprotect(fullBuf, totalByteCount, PROT_NONE) != 0) {
-    //     PLOG(WARNING) << "mprotect(PROT_NONE) failed";
-    // }
-    if (munmap(fullBuf, totalByteCount) != 0) {
-      PLOG(FATAL) << "munmap(" << reinterpret_cast<void*>(fullBuf) << ", " << totalByteCount << ") failed";
+  static void DebugFree(void* buf, size_t len) {
+    if (munmap(buf, len) != 0) {
+      PLOG(FATAL) << "munmap(" << buf << ", " << len << ") failed";
     }
   }
 
-  static const uint8_t* ActualBuffer(const void* dataBuf) {
-    return reinterpret_cast<const uint8_t*>(dataBuf) - kGuardLen / 2;
+  static size_t LengthIncludingRedZones(size_t len) {
+    return len + kRedZoneSize;
   }
 
-  static uint8_t* ActualBuffer(void* dataBuf) {
-    return reinterpret_cast<uint8_t*>(dataBuf) - kGuardLen / 2;
+  // Get the GuardedCopy from the interior pointer.
+  static GuardedCopy* FromEmbedded(void* embedded_buf) {
+    return reinterpret_cast<GuardedCopy*>(
+        reinterpret_cast<uint8_t*>(embedded_buf) - (kRedZoneSize / 2));
   }
 
-  // Underlying length of a user allocation of 'length' bytes.
-  static size_t ActualLength(size_t length) {
-    return (length + kGuardLen + 1) & ~0x01;
+  static const GuardedCopy* FromEmbedded(const void* embedded_buf) {
+    return reinterpret_cast<const GuardedCopy*>(
+        reinterpret_cast<const uint8_t*>(embedded_buf) - (kRedZoneSize / 2));
   }
+
+  static void AbortF(const char* jni_function_name, const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    Runtime::Current()->GetJavaVM()->JniAbortV(jni_function_name, fmt, args);
+    va_end(args);
+  }
+
+  bool CheckHeader(const char* function_name, bool mod_okay) const {
+    static const uint32_t kMagicCmp = kGuardMagic;
+
+    // Before we do anything with "pExtra", check the magic number.  We
+    // do the check with memcmp rather than "==" in case the pointer is
+    // unaligned.  If it points to completely bogus memory we're going
+    // to crash, but there's no easy way around that.
+    if (UNLIKELY(memcmp(&magic_, &kMagicCmp, 4) != 0)) {
+      uint8_t buf[4];
+      memcpy(buf, &magic_, 4);
+      AbortF(function_name,
+             "guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
+             buf[3], buf[2], buf[1], buf[0], this);  // Assumes little-endian.
+      return false;
+    }
+
+    // If modification is not expected, verify checksum. Strictly speaking this is wrong: if we
+    // told the client that we made a copy, there's no reason they can't alter the buffer.
+    if (!mod_okay) {
+      uLong computed_adler =
+          adler32(adler32(0L, Z_NULL, 0), BufferWithinRedZones(), original_length_);
+      if (computed_adler != adler_) {
+        AbortF(function_name, "buffer modified (0x%08lx vs 0x%08lx) at address %p",
+               computed_adler, adler_, this);
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool CheckRedZones(const char* function_name) const {
+    // Check the begin red zone.
+    const size_t kStartCanaryLength = (GuardedCopy::kRedZoneSize / 2) - sizeof(GuardedCopy);
+    for (size_t i = 0, j = 0; i < kStartCanaryLength; ++i) {
+      if (UNLIKELY(StartRedZone()[i] != kCanary[j])) {
+        AbortF(function_name, "guard pattern before buffer disturbed at %p +%zd", this, i);
+        return false;
+      }
+      if (kCanary[j] == '\0') {
+        j = 0;
+      }
+    }
+
+    // Check end region.
+    for (size_t i = 0, j = 0; i < kEndCanaryLength; ++i) {
+      if (UNLIKELY(EndRedZone()[i] != kCanary[j])) {
+        size_t offset_from_buffer_start =
+            &(EndRedZone()[i]) - &(StartRedZone()[kStartCanaryLength]);
+        AbortF(function_name, "guard pattern after buffer disturbed at %p +%zd", this,
+               offset_from_buffer_start);
+        return false;
+      }
+      if (kCanary[j] == '\0') {
+        j = 0;
+      }
+    }
+    return true;
+  }
+
+  // Location that canary value will be written before the guarded region.
+  const char* StartRedZone() const {
+    const uint8_t* buf = reinterpret_cast<const uint8_t*>(this);
+    return reinterpret_cast<const char*>(buf + sizeof(GuardedCopy));
+  }
+
+  // Return the interior embedded buffer.
+  const uint8_t* BufferWithinRedZones() const {
+    const uint8_t* embedded_buf = reinterpret_cast<const uint8_t*>(this) + (kRedZoneSize / 2);
+    return embedded_buf;
+  }
+
+  // Location that canary value will be written after the guarded region.
+  const char* EndRedZone() const {
+    const uint8_t* buf = reinterpret_cast<const uint8_t*>(this);
+    size_t buf_len = LengthIncludingRedZones(original_length_);
+    return reinterpret_cast<const char*>(buf + (buf_len - (kRedZoneSize / 2)));
+  }
+
+  static constexpr size_t kRedZoneSize = 512;
+  static constexpr size_t kEndCanaryLength = kRedZoneSize / 2;
+
+  // Value written before and after the guarded array.
+  static const char* const kCanary;
+
+  static constexpr uint32_t kGuardMagic = 0xffd5aa96;
+
+  const uint32_t magic_;
+  const uLong adler_;
+  const void* const original_ptr_;
+  const size_t original_length_;
 };
-
-/*
- * Create a guarded copy of a primitive array.  Modifications to the copied
- * data are allowed.  Returns a pointer to the copied data.
- */
-static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* isCopy) {
-  ScopedObjectAccess soa(env);
-
-  mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
-  size_t component_size = a->GetClass()->GetComponentSize();
-  size_t byte_count = a->GetLength() * component_size;
-  void* result = GuardedCopy::Create(a->GetRawData(component_size, 0), byte_count, true);
-  if (isCopy != nullptr) {
-    *isCopy = JNI_TRUE;
-  }
-  return result;
-}
-
-/*
- * Perform the array "release" operation, which may or may not copy data
- * back into the managed heap, and may or may not release the underlying storage.
- */
-static void ReleaseGuardedPACopy(JNIEnv* env, jarray java_array, void* dataBuf, int mode) {
-  ScopedObjectAccess soa(env);
-  mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
-
-  GuardedCopy::Check(__FUNCTION__, dataBuf, true);
-
-  if (mode != JNI_ABORT) {
-    size_t len = GuardedCopy::FromData(dataBuf)->original_length;
-    memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), dataBuf, len);
-  }
-  if (mode != JNI_COMMIT) {
-    GuardedCopy::Destroy(dataBuf);
-  }
-}
+const char* const GuardedCopy::kCanary = "JNI BUFFER RED ZONE";
 
 /*
  * ===========================================================================
@@ -1118,668 +1414,1955 @@
 class CheckJNI {
  public:
   static jint GetVersion(JNIEnv* env) {
-    CHECK_JNI_ENTRY(kFlag_Default, "E", env);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->GetVersion(env));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[1] = {{.E = env }};
+    if (sc.Check(soa, true, "E", args)) {
+      JniValueType result;
+      result.I = baseEnv(env)->GetVersion(env);
+      if (sc.Check(soa, false, "I", &result)) {
+        return result.I;
+      }
+    }
+    return JNI_ERR;
   }
 
-  static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf, jsize bufLen) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EuLpz", env, name, loader, buf, bufLen);
-    sc.CheckClassName(name);
-    return CHECK_JNI_EXIT("c", baseEnv(env)->DefineClass(env, name, loader, buf, bufLen));
+  static jint GetJavaVM(JNIEnv *env, JavaVM **vm) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env }, {.p = vm}};
+    if (sc.Check(soa, true, "Ep", args)) {
+      JniValueType result;
+      result.i = baseEnv(env)->GetJavaVM(env, vm);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[4] = {{.E = env }, {.c = c}, {.p = methods}, {.I = nMethods}};
+    if (sc.Check(soa, true, "EcpI", args)) {
+      JniValueType result;
+      result.i = baseEnv(env)->RegisterNatives(env, c, methods, nMethods);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static jint UnregisterNatives(JNIEnv* env, jclass c) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env }, {.c = c}};
+    if (sc.Check(soa, true, "Ec", args)) {
+      JniValueType result;
+      result.i = baseEnv(env)->UnregisterNatives(env, c);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
+    // Note: we use "EL" here but "Ep" has been used in the past on the basis that we'd like to
+    // know the object is invalid. The spec says that passing invalid objects or even ones that
+    // are deleted isn't supported.
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env }, {.L = obj}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      result.w = baseEnv(env)->GetObjectRefType(env, obj);
+      if (sc.Check(soa, false, "w", &result)) {
+        return result.w;
+      }
+    }
+    return JNIInvalidRefType;
+  }
+
+  static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf,
+                            jsize bufLen) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[5] = {{.E = env}, {.u = name}, {.L = loader}, {.p = buf}, {.z = bufLen}};
+    if (sc.Check(soa, true, "EuLpz", args) && sc.CheckClassName(name)) {
+      JniValueType result;
+      result.c = baseEnv(env)->DefineClass(env, name, loader, buf, bufLen);
+      if (sc.Check(soa, false, "c", &result)) {
+        return result.c;
+      }
+    }
+    return nullptr;
   }
 
   static jclass FindClass(JNIEnv* env, const char* name) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Eu", env, name);
-    sc.CheckClassName(name);
-    return CHECK_JNI_EXIT("c", baseEnv(env)->FindClass(env, name));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.u = name}};
+    if (sc.Check(soa, true, "Eu", args) && sc.CheckClassName(name)) {
+      JniValueType result;
+      result.c = baseEnv(env)->FindClass(env, name);
+      if (sc.Check(soa, false, "c", &result)) {
+        return result.c;
+      }
+    }
+    return nullptr;
   }
 
   static jclass GetSuperclass(JNIEnv* env, jclass c) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
-    return CHECK_JNI_EXIT("c", baseEnv(env)->GetSuperclass(env, c));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.c = c}};
+    if (sc.Check(soa, true, "Ec", args)) {
+      JniValueType result;
+      result.c = baseEnv(env)->GetSuperclass(env, c);
+      if (sc.Check(soa, false, "c", &result)) {
+        return result.c;
+      }
+    }
+    return nullptr;
   }
 
   static jboolean IsAssignableFrom(JNIEnv* env, jclass c1, jclass c2) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecc", env, c1, c2);
-    return CHECK_JNI_EXIT("b", baseEnv(env)->IsAssignableFrom(env, c1, c2));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.c = c1}, {.c = c2}};
+    if (sc.Check(soa, true, "Ecc", args)) {
+      JniValueType result;
+      result.b = baseEnv(env)->IsAssignableFrom(env, c1, c2);
+      if (sc.Check(soa, false, "b", &result)) {
+        return result.b;
+      }
+    }
+    return JNI_FALSE;
   }
 
   static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, method);
-    // TODO: check that 'field' is a java.lang.reflect.Method.
-    return CHECK_JNI_EXIT("m", baseEnv(env)->FromReflectedMethod(env, method));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = method}};
+    if (sc.Check(soa, true, "EL", args) && sc.CheckReflectedMethod(soa, method)) {
+      JniValueType result;
+      result.m = baseEnv(env)->FromReflectedMethod(env, method);
+      if (sc.Check(soa, false, "m", &result)) {
+        return result.m;
+      }
+    }
+    return nullptr;
   }
 
   static jfieldID FromReflectedField(JNIEnv* env, jobject field) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, field);
-    // TODO: check that 'field' is a java.lang.reflect.Field.
-    return CHECK_JNI_EXIT("f", baseEnv(env)->FromReflectedField(env, field));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = field}};
+    if (sc.Check(soa, true, "EL", args) && sc.CheckReflectedField(soa, field)) {
+      JniValueType result;
+      result.f = baseEnv(env)->FromReflectedField(env, field);
+      if (sc.Check(soa, false, "f", &result)) {
+        return result.f;
+      }
+    }
+    return nullptr;
   }
 
   static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecmb", env, cls, mid, isStatic);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.b = isStatic}};
+    if (sc.Check(soa, true, "Ecmb", args)) {
+      JniValueType result;
+      result.L = baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic);
+      if (sc.Check(soa, false, "L", &result) && (result.L != nullptr)) {
+        DCHECK(sc.CheckReflectedMethod(soa, result.L));
+        return result.L;
+      }
+    }
+    return nullptr;
   }
 
   static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fid, jboolean isStatic) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecfb", env, cls, fid, isStatic);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedField(env, cls, fid, isStatic));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.b = isStatic}};
+    if (sc.Check(soa, true, "Ecfb", args)) {
+      JniValueType result;
+      result.L = baseEnv(env)->ToReflectedField(env, cls, fid, isStatic);
+      if (sc.Check(soa, false, "L", &result) && (result.L != nullptr)) {
+        DCHECK(sc.CheckReflectedField(soa, result.L));
+        return result.L;
+      }
+    }
+    return nullptr;
   }
 
   static jint Throw(JNIEnv* env, jthrowable obj) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
-    // TODO: check that 'obj' is a java.lang.Throwable.
-    return CHECK_JNI_EXIT("I", baseEnv(env)->Throw(env, obj));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.t = obj}};
+    if (sc.Check(soa, true, "Et", args) && sc.CheckThrowable(soa, obj)) {
+      JniValueType result;
+      result.i = baseEnv(env)->Throw(env, obj);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
   }
 
   static jint ThrowNew(JNIEnv* env, jclass c, const char* message) {
-    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Ecu", env, c, message);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->ThrowNew(env, c, message));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
+    JniValueType args[5] = {{.E = env}, {.c = c}, {.u = message}};
+    if (sc.Check(soa, true, "Ecu", args) && sc.CheckThrowableClass(soa, c)) {
+      JniValueType result;
+      result.i = baseEnv(env)->ThrowNew(env, c, message);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
   }
 
   static jthrowable ExceptionOccurred(JNIEnv* env) {
-    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->ExceptionOccurred(env));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+    JniValueType args[1] = {{.E = env}};
+    if (sc.Check(soa, true, "E", args)) {
+      JniValueType result;
+      result.t = baseEnv(env)->ExceptionOccurred(env);
+      if (sc.Check(soa, false, "t", &result)) {
+        return result.t;
+      }
+    }
+    return nullptr;
   }
 
   static void ExceptionDescribe(JNIEnv* env) {
-    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
-    baseEnv(env)->ExceptionDescribe(env);
-    CHECK_JNI_EXIT_VOID();
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+    JniValueType args[1] = {{.E = env}};
+    if (sc.Check(soa, true, "E", args)) {
+      JniValueType result;
+      baseEnv(env)->ExceptionDescribe(env);
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
   }
 
   static void ExceptionClear(JNIEnv* env) {
-    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
-    baseEnv(env)->ExceptionClear(env);
-    CHECK_JNI_EXIT_VOID();
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+    JniValueType args[1] = {{.E = env}};
+    if (sc.Check(soa, true, "E", args)) {
+      JniValueType result;
+      baseEnv(env)->ExceptionClear(env);
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
+
+  static jboolean ExceptionCheck(JNIEnv* env) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritOkay | kFlag_ExcepOkay, __FUNCTION__);
+    JniValueType args[1] = {{.E = env}};
+    if (sc.Check(soa, true, "E", args)) {
+      JniValueType result;
+      result.b = baseEnv(env)->ExceptionCheck(env);
+      if (sc.Check(soa, false, "b", &result)) {
+        return result.b;
+      }
+    }
+    return JNI_FALSE;
   }
 
   static void FatalError(JNIEnv* env, const char* msg) {
     // The JNI specification doesn't say it's okay to call FatalError with a pending exception,
     // but you're about to abort anyway, and it's quite likely that you have a pending exception,
     // and it's not unimaginable that you don't know that you do. So we allow it.
-    CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_NullableUtf, "Eu", env, msg);
-    baseEnv(env)->FatalError(env, msg);
-    CHECK_JNI_EXIT_VOID();
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay | kFlag_NullableUtf, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.u = msg}};
+    if (sc.Check(soa, true, "Eu", args)) {
+      JniValueType result;
+      baseEnv(env)->FatalError(env, msg);
+      // Unreachable.
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
   }
 
   static jint PushLocalFrame(JNIEnv* env, jint capacity) {
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EI", env, capacity);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->PushLocalFrame(env, capacity));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.I = capacity}};
+    if (sc.Check(soa, true, "EI", args)) {
+      JniValueType result;
+      result.i = baseEnv(env)->PushLocalFrame(env, capacity);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
   }
 
   static jobject PopLocalFrame(JNIEnv* env, jobject res) {
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, res);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->PopLocalFrame(env, res));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = res}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      result.L = baseEnv(env)->PopLocalFrame(env, res);
+      sc.Check(soa, false, "L", &result);
+      return result.L;
+    }
+    return nullptr;
   }
 
   static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->NewGlobalRef(env, obj));
+    return NewRef(__FUNCTION__, env, obj, kGlobal);
   }
 
-  static jobject NewLocalRef(JNIEnv* env, jobject ref) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, ref);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->NewLocalRef(env, ref));
-  }
-
-  static void DeleteGlobalRef(JNIEnv* env, jobject globalRef) {
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, globalRef);
-    if (globalRef != nullptr && GetIndirectRefKind(globalRef) != kGlobal) {
-      JniAbortF(__FUNCTION__, "DeleteGlobalRef on %s: %p",
-                ToStr<IndirectRefKind>(GetIndirectRefKind(globalRef)).c_str(), globalRef);
-    } else {
-      baseEnv(env)->DeleteGlobalRef(env, globalRef);
-      CHECK_JNI_EXIT_VOID();
-    }
-  }
-
-  static void DeleteWeakGlobalRef(JNIEnv* env, jweak weakGlobalRef) {
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, weakGlobalRef);
-    if (weakGlobalRef != nullptr && GetIndirectRefKind(weakGlobalRef) != kWeakGlobal) {
-      JniAbortF(__FUNCTION__, "DeleteWeakGlobalRef on %s: %p",
-                ToStr<IndirectRefKind>(GetIndirectRefKind(weakGlobalRef)).c_str(), weakGlobalRef);
-    } else {
-      baseEnv(env)->DeleteWeakGlobalRef(env, weakGlobalRef);
-      CHECK_JNI_EXIT_VOID();
-    }
-  }
-
-  static void DeleteLocalRef(JNIEnv* env, jobject localRef) {
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, localRef);
-    if (localRef != nullptr && GetIndirectRefKind(localRef) != kLocal && !IsHandleScopeLocalRef(env, localRef)) {
-      JniAbortF(__FUNCTION__, "DeleteLocalRef on %s: %p",
-                ToStr<IndirectRefKind>(GetIndirectRefKind(localRef)).c_str(), localRef);
-    } else {
-      baseEnv(env)->DeleteLocalRef(env, localRef);
-      CHECK_JNI_EXIT_VOID();
-    }
-  }
-
-  static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EI", env, capacity);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->EnsureLocalCapacity(env, capacity));
-  }
-
-  static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
-    CHECK_JNI_ENTRY(kFlag_Default, "ELL", env, ref1, ref2);
-    return CHECK_JNI_EXIT("b", baseEnv(env)->IsSameObject(env, ref1, ref2));
-  }
-
-  static jobject AllocObject(JNIEnv* env, jclass c) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->AllocObject(env, c));
-  }
-
-  static jobject NewObject(JNIEnv* env, jclass c, jmethodID mid, ...) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
-    va_list args;
-    va_start(args, mid);
-    jobject result = baseEnv(env)->NewObjectV(env, c, mid, args);
-    va_end(args);
-    return CHECK_JNI_EXIT("L", result);
-  }
-
-  static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list args) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectV(env, c, mid, args));
-  }
-
-  static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* args) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectA(env, c, mid, args));
-  }
-
-  static jclass GetObjectClass(JNIEnv* env, jobject obj) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
-    return CHECK_JNI_EXIT("c", baseEnv(env)->GetObjectClass(env, obj));
-  }
-
-  static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass c) {
-    CHECK_JNI_ENTRY(kFlag_Default, "ELc", env, obj, c);
-    return CHECK_JNI_EXIT("b", baseEnv(env)->IsInstanceOf(env, obj, c));
-  }
-
-  static jmethodID GetMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
-    return CHECK_JNI_EXIT("m", baseEnv(env)->GetMethodID(env, c, name, sig));
-  }
-
-  static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
-    return CHECK_JNI_EXIT("f", baseEnv(env)->GetFieldID(env, c, name, sig));
-  }
-
-  static jmethodID GetStaticMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
-    return CHECK_JNI_EXIT("m", baseEnv(env)->GetStaticMethodID(env, c, name, sig));
-  }
-
-  static jfieldID GetStaticFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
-    return CHECK_JNI_EXIT("f", baseEnv(env)->GetStaticFieldID(env, c, name, sig));
-  }
-
-#define FIELD_ACCESSORS(_ctype, _jname, _jvalue_type, _type) \
-    static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass c, jfieldID fid) { \
-        CHECK_JNI_ENTRY(kFlag_Default, "Ecf", env, c, fid); \
-        sc.CheckStaticFieldID(c, fid); \
-        return CHECK_JNI_EXIT(_type, baseEnv(env)->GetStatic##_jname##Field(env, c, fid)); \
-    } \
-    static _ctype Get##_jname##Field(JNIEnv* env, jobject obj, jfieldID fid) { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELf", env, obj, fid); \
-        sc.CheckInstanceFieldID(obj, fid); \
-        return CHECK_JNI_EXIT(_type, baseEnv(env)->Get##_jname##Field(env, obj, fid)); \
-    } \
-    static void SetStatic##_jname##Field(JNIEnv* env, jclass c, jfieldID fid, _ctype value) { \
-        CHECK_JNI_ENTRY(kFlag_Default, "Ecf" _type, env, c, fid, value); \
-        sc.CheckStaticFieldID(c, fid); \
-        /* "value" arg only used when type == ref */ \
-        jvalue java_type_value; \
-        java_type_value._jvalue_type = value; \
-        sc.CheckFieldType(java_type_value, fid, _type[0], true); \
-        baseEnv(env)->SetStatic##_jname##Field(env, c, fid, value); \
-        CHECK_JNI_EXIT_VOID(); \
-    } \
-    static void Set##_jname##Field(JNIEnv* env, jobject obj, jfieldID fid, _ctype value) { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELf" _type, env, obj, fid, value); \
-        sc.CheckInstanceFieldID(obj, fid); \
-        /* "value" arg only used when type == ref */ \
-        jvalue java_type_value; \
-        java_type_value._jvalue_type = value; \
-        sc.CheckFieldType(java_type_value, fid, _type[0], false); \
-        baseEnv(env)->Set##_jname##Field(env, obj, fid, value); \
-        CHECK_JNI_EXIT_VOID(); \
-    }
-
-FIELD_ACCESSORS(jobject, Object, l, "L");
-FIELD_ACCESSORS(jboolean, Boolean, z, "Z");
-FIELD_ACCESSORS(jbyte, Byte, b, "B");
-FIELD_ACCESSORS(jchar, Char, c, "C");
-FIELD_ACCESSORS(jshort, Short, s, "S");
-FIELD_ACCESSORS(jint, Int, i, "I");
-FIELD_ACCESSORS(jlong, Long, j, "J");
-FIELD_ACCESSORS(jfloat, Float, f, "F");
-FIELD_ACCESSORS(jdouble, Double, d, "D");
-
-#define CALL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \
-    /* Virtual... */ \
-    static _ctype Call##_jname##Method(JNIEnv* env, jobject obj, \
-        jmethodID mid, ...) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, false); \
-        sc.CheckVirtualMethod(obj, mid); \
-        _retdecl; \
-        va_list args; \
-        va_start(args, mid); \
-        _retasgn(baseEnv(env)->Call##_jname##MethodV(env, obj, mid, args)); \
-        va_end(args); \
-        _retok; \
-    } \
-    static _ctype Call##_jname##MethodV(JNIEnv* env, jobject obj, \
-        jmethodID mid, va_list args) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, false); \
-        sc.CheckVirtualMethod(obj, mid); \
-        _retdecl; \
-        _retasgn(baseEnv(env)->Call##_jname##MethodV(env, obj, mid, args)); \
-        _retok; \
-    } \
-    static _ctype Call##_jname##MethodA(JNIEnv* env, jobject obj, \
-        jmethodID mid, jvalue* args) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, false); \
-        sc.CheckVirtualMethod(obj, mid); \
-        _retdecl; \
-        _retasgn(baseEnv(env)->Call##_jname##MethodA(env, obj, mid, args)); \
-        _retok; \
-    } \
-    /* Non-virtual... */ \
-    static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, \
-        jobject obj, jclass c, jmethodID mid, ...) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, false); \
-        sc.CheckVirtualMethod(obj, mid); \
-        _retdecl; \
-        va_list args; \
-        va_start(args, mid); \
-        _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, c, mid, args)); \
-        va_end(args); \
-        _retok; \
-    } \
-    static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, \
-        jobject obj, jclass c, jmethodID mid, va_list args) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, false); \
-        sc.CheckVirtualMethod(obj, mid); \
-        _retdecl; \
-        _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, c, mid, args)); \
-        _retok; \
-    } \
-    static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, \
-        jobject obj, jclass c, jmethodID mid, jvalue* args) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, false); \
-        sc.CheckVirtualMethod(obj, mid); \
-        _retdecl; \
-        _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodA(env, obj, c, mid, args)); \
-        _retok; \
-    } \
-    /* Static... */ \
-    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass c, jmethodID mid, ...) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, true); \
-        sc.CheckStaticMethod(c, mid); \
-        _retdecl; \
-        va_list args; \
-        va_start(args, mid); \
-        _retasgn(baseEnv(env)->CallStatic##_jname##MethodV(env, c, mid, args)); \
-        va_end(args); \
-        _retok; \
-    } \
-    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass c, jmethodID mid, va_list args) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, true); \
-        sc.CheckStaticMethod(c, mid); \
-        _retdecl; \
-         _retasgn(baseEnv(env)->CallStatic##_jname##MethodV(env, c, mid, args)); \
-        _retok; \
-    } \
-    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* args) \
-    { \
-        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
-        sc.CheckSig(mid, _retsig, true); \
-        sc.CheckStaticMethod(c, mid); \
-        _retdecl; \
-        _retasgn(baseEnv(env)->CallStatic##_jname##MethodA(env, c, mid, args)); \
-        _retok; \
-    }
-
-#define NON_VOID_RETURN(_retsig, _ctype) return CHECK_JNI_EXIT(_retsig, (_ctype) result)
-#define VOID_RETURN CHECK_JNI_EXIT_VOID()
-
-CALL(jobject, Object, mirror::Object* result, result = reinterpret_cast<mirror::Object*>, NON_VOID_RETURN("L", jobject), "L");
-CALL(jboolean, Boolean, jboolean result, result =, NON_VOID_RETURN("Z", jboolean), "Z");
-CALL(jbyte, Byte, jbyte result, result =, NON_VOID_RETURN("B", jbyte), "B");
-CALL(jchar, Char, jchar result, result =, NON_VOID_RETURN("C", jchar), "C");
-CALL(jshort, Short, jshort result, result =, NON_VOID_RETURN("S", jshort), "S");
-CALL(jint, Int, jint result, result =, NON_VOID_RETURN("I", jint), "I");
-CALL(jlong, Long, jlong result, result =, NON_VOID_RETURN("J", jlong), "J");
-CALL(jfloat, Float, jfloat result, result =, NON_VOID_RETURN("F", jfloat), "F");
-CALL(jdouble, Double, jdouble result, result =, NON_VOID_RETURN("D", jdouble), "D");
-CALL(void, Void, , , VOID_RETURN, "V");
-
-  static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Epz", env, unicodeChars, len);
-    return CHECK_JNI_EXIT("s", baseEnv(env)->NewString(env, unicodeChars, len));
-  }
-
-  static jsize GetStringLength(JNIEnv* env, jstring string) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringLength(env, string));
-  }
-
-  static const jchar* GetStringChars(JNIEnv* env, jstring java_string, jboolean* isCopy) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, java_string, isCopy);
-    const jchar* result = baseEnv(env)->GetStringChars(env, java_string, isCopy);
-    if (sc.ForceCopy() && result != nullptr) {
-      mirror::String* s = sc.soa().Decode<mirror::String*>(java_string);
-      int byteCount = s->GetLength() * 2;
-      result = (const jchar*) GuardedCopy::Create(result, byteCount, false);
-      if (isCopy != nullptr) {
-        *isCopy = JNI_TRUE;
-      }
-    }
-    return CHECK_JNI_EXIT("p", result);
-  }
-
-  static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Esp", env, string, chars);
-    sc.CheckNonNull(chars);
-    if (sc.ForceCopy()) {
-      GuardedCopy::Check(__FUNCTION__, chars, false);
-      chars = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<jchar*>(chars)));
-    }
-    baseEnv(env)->ReleaseStringChars(env, string, chars);
-    CHECK_JNI_EXIT_VOID();
-  }
-
-  static jstring NewStringUTF(JNIEnv* env, const char* bytes) {
-    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, bytes);  // TODO: show pointer and truncate string.
-    return CHECK_JNI_EXIT("s", baseEnv(env)->NewStringUTF(env, bytes));
-  }
-
-  static jsize GetStringUTFLength(JNIEnv* env, jstring string) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringUTFLength(env, string));
-  }
-
-  static const char* GetStringUTFChars(JNIEnv* env, jstring string, jboolean* isCopy) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, string, isCopy);
-    const char* result = baseEnv(env)->GetStringUTFChars(env, string, isCopy);
-    if (sc.ForceCopy() && result != nullptr) {
-      result = (const char*) GuardedCopy::Create(result, strlen(result) + 1, false);
-      if (isCopy != nullptr) {
-        *isCopy = JNI_TRUE;
-      }
-    }
-    return CHECK_JNI_EXIT("u", result);  // TODO: show pointer and truncate string.
-  }
-
-  static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) {
-    CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_Release, "Esu", env, string, utf);  // TODO: show pointer and truncate string.
-    if (sc.ForceCopy()) {
-      GuardedCopy::Check(__FUNCTION__, utf, false);
-      utf = reinterpret_cast<const char*>(GuardedCopy::Destroy(const_cast<char*>(utf)));
-    }
-    baseEnv(env)->ReleaseStringUTFChars(env, string, utf);
-    CHECK_JNI_EXIT_VOID();
-  }
-
-  static jsize GetArrayLength(JNIEnv* env, jarray array) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay, "Ea", env, array);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->GetArrayLength(env, array));
-  }
-
-  static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass elementClass, jobject initialElement) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EzcL", env, length, elementClass, initialElement);
-    return CHECK_JNI_EXIT("a", baseEnv(env)->NewObjectArray(env, length, elementClass, initialElement));
-  }
-
-  static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EaI", env, array, index);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->GetObjectArrayElement(env, array, index));
-  }
-
-  static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EaIL", env, array, index, value);
-    baseEnv(env)->SetObjectArrayElement(env, array, index, value);
-    CHECK_JNI_EXIT_VOID();
-  }
-
-#define NEW_PRIMITIVE_ARRAY(_artype, _jname) \
-    static _artype New##_jname##Array(JNIEnv* env, jsize length) { \
-        CHECK_JNI_ENTRY(kFlag_Default, "Ez", env, length); \
-        return CHECK_JNI_EXIT("a", baseEnv(env)->New##_jname##Array(env, length)); \
-    }
-NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean);
-NEW_PRIMITIVE_ARRAY(jbyteArray, Byte);
-NEW_PRIMITIVE_ARRAY(jcharArray, Char);
-NEW_PRIMITIVE_ARRAY(jshortArray, Short);
-NEW_PRIMITIVE_ARRAY(jintArray, Int);
-NEW_PRIMITIVE_ARRAY(jlongArray, Long);
-NEW_PRIMITIVE_ARRAY(jfloatArray, Float);
-NEW_PRIMITIVE_ARRAY(jdoubleArray, Double);
-
-struct ForceCopyGetChecker {
- public:
-  ForceCopyGetChecker(ScopedCheck& sc, jboolean* isCopy) {
-    force_copy = sc.ForceCopy();
-    no_copy = 0;
-    if (force_copy && isCopy != nullptr) {
-      // Capture this before the base call tramples on it.
-      no_copy = *reinterpret_cast<uint32_t*>(isCopy);
-    }
-  }
-
-  template<typename ResultT>
-  ResultT Check(JNIEnv* env, jarray array, jboolean* isCopy, ResultT result) {
-    if (force_copy && result != nullptr) {
-      result = reinterpret_cast<ResultT>(CreateGuardedPACopy(env, array, isCopy));
-    }
-    return result;
-  }
-
-  uint32_t no_copy;
-  bool force_copy;
-};
-
-#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
-  static _ctype* Get##_jname##ArrayElements(JNIEnv* env, _ctype##Array array, jboolean* isCopy) { \
-    CHECK_JNI_ENTRY(kFlag_Default, "Eap", env, array, isCopy); \
-    _ctype* result = ForceCopyGetChecker(sc, isCopy).Check(env, array, isCopy, baseEnv(env)->Get##_jname##ArrayElements(env, array, isCopy)); \
-    return CHECK_JNI_EXIT("p", result); \
-  }
-
-#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
-  static void Release##_jname##ArrayElements(JNIEnv* env, _ctype##Array array, _ctype* elems, jint mode) { \
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Eapr", env, array, elems, mode); \
-    sc.CheckNonNull(elems); \
-    if (sc.ForceCopy()) { \
-      ReleaseGuardedPACopy(env, array, elems, mode); \
-    } \
-    baseEnv(env)->Release##_jname##ArrayElements(env, array, elems, mode); \
-    CHECK_JNI_EXIT_VOID(); \
-  }
-
-#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
-    static void Get##_jname##ArrayRegion(JNIEnv* env, _ctype##Array array, jsize start, jsize len, _ctype* buf) { \
-        CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
-        baseEnv(env)->Get##_jname##ArrayRegion(env, array, start, len, buf); \
-        CHECK_JNI_EXIT_VOID(); \
-    }
-
-#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
-    static void Set##_jname##ArrayRegion(JNIEnv* env, _ctype##Array array, jsize start, jsize len, const _ctype* buf) { \
-        CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
-        baseEnv(env)->Set##_jname##ArrayRegion(env, array, start, len, buf); \
-        CHECK_JNI_EXIT_VOID(); \
-    }
-
-#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname, _typechar) \
-    GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
-    RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
-    GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
-    SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
-
-// TODO: verify primitive array type matches call type.
-PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, 'Z');
-PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, 'B');
-PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, 'C');
-PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, 'S');
-PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, 'I');
-PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, 'J');
-PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, 'F');
-PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D');
-
-  static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EcpI", env, c, methods, nMethods);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->RegisterNatives(env, c, methods, nMethods));
-  }
-
-  static jint UnregisterNatives(JNIEnv* env, jclass c) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->UnregisterNatives(env, c));
-  }
-
-  static jint MonitorEnter(JNIEnv* env, jobject obj) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
-    if (!sc.CheckInstance(ScopedCheck::kObject, obj)) {
-      return JNI_ERR;  // Only for jni_internal_test. Real code will have aborted already.
-    }
-    return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorEnter(env, obj));
-  }
-
-  static jint MonitorExit(JNIEnv* env, jobject obj) {
-    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, obj);
-    if (!sc.CheckInstance(ScopedCheck::kObject, obj)) {
-      return JNI_ERR;  // Only for jni_internal_test. Real code will have aborted already.
-    }
-    return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorExit(env, obj));
-  }
-
-  static jint GetJavaVM(JNIEnv *env, JavaVM **vm) {
-    CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, vm);
-    return CHECK_JNI_EXIT("I", baseEnv(env)->GetJavaVM(env, vm));
-  }
-
-  static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, jchar* buf) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
-    baseEnv(env)->GetStringRegion(env, str, start, len, buf);
-    CHECK_JNI_EXIT_VOID();
-  }
-
-  static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, jsize len, char* buf) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
-    baseEnv(env)->GetStringUTFRegion(env, str, start, len, buf);
-    CHECK_JNI_EXIT_VOID();
-  }
-
-  static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* isCopy) {
-    CHECK_JNI_ENTRY(kFlag_CritGet, "Eap", env, array, isCopy);
-    void* result = baseEnv(env)->GetPrimitiveArrayCritical(env, array, isCopy);
-    if (sc.ForceCopy() && result != nullptr) {
-      result = CreateGuardedPACopy(env, array, isCopy);
-    }
-    return CHECK_JNI_EXIT("p", result);
-  }
-
-  static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
-    CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Eapr", env, array, carray, mode);
-    sc.CheckNonNull(carray);
-    if (sc.ForceCopy()) {
-      ReleaseGuardedPACopy(env, array, carray, mode);
-    }
-    baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
-    CHECK_JNI_EXIT_VOID();
-  }
-
-  static const jchar* GetStringCritical(JNIEnv* env, jstring java_string, jboolean* isCopy) {
-    CHECK_JNI_ENTRY(kFlag_CritGet, "Esp", env, java_string, isCopy);
-    const jchar* result = baseEnv(env)->GetStringCritical(env, java_string, isCopy);
-    if (sc.ForceCopy() && result != nullptr) {
-      mirror::String* s = sc.soa().Decode<mirror::String*>(java_string);
-      int byteCount = s->GetLength() * 2;
-      result = (const jchar*) GuardedCopy::Create(result, byteCount, false);
-      if (isCopy != nullptr) {
-        *isCopy = JNI_TRUE;
-      }
-    }
-    return CHECK_JNI_EXIT("p", result);
-  }
-
-  static void ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* carray) {
-    CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Esp", env, string, carray);
-    sc.CheckNonNull(carray);
-    if (sc.ForceCopy()) {
-      GuardedCopy::Check(__FUNCTION__, carray, false);
-      carray = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<jchar*>(carray)));
-    }
-    baseEnv(env)->ReleaseStringCritical(env, string, carray);
-    CHECK_JNI_EXIT_VOID();
+  static jobject NewLocalRef(JNIEnv* env, jobject obj) {
+    return NewRef(__FUNCTION__, env, obj, kLocal);
   }
 
   static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
-    return CHECK_JNI_EXIT("L", baseEnv(env)->NewWeakGlobalRef(env, obj));
+    return NewRef(__FUNCTION__, env, obj, kWeakGlobal);
   }
 
-  static jboolean ExceptionCheck(JNIEnv* env) {
-    CHECK_JNI_ENTRY(kFlag_CritOkay | kFlag_ExcepOkay, "E", env);
-    return CHECK_JNI_EXIT("b", baseEnv(env)->ExceptionCheck(env));
+  static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
+    DeleteRef(__FUNCTION__, env, obj, kGlobal);
   }
 
-  static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
-    // Note: we use "Ep" rather than "EL" because this is the one JNI function
-    // that it's okay to pass an invalid reference to.
-    CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, obj);
-    // TODO: proper decoding of jobjectRefType!
-    return CHECK_JNI_EXIT("I", baseEnv(env)->GetObjectRefType(env, obj));
+  static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
+    DeleteRef(__FUNCTION__, env, obj, kWeakGlobal);
+  }
+
+  static void DeleteLocalRef(JNIEnv* env, jobject obj) {
+    DeleteRef(__FUNCTION__, env, obj, kLocal);
+  }
+
+  static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.I = capacity}};
+    if (sc.Check(soa, true, "EI", args)) {
+      JniValueType result;
+      result.i = baseEnv(env)->EnsureLocalCapacity(env, capacity);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.L = ref1}, {.L = ref2}};
+    if (sc.Check(soa, true, "ELL", args)) {
+      JniValueType result;
+      result.b = baseEnv(env)->IsSameObject(env, ref1, ref2);
+      if (sc.Check(soa, false, "b", &result)) {
+        return result.b;
+      }
+    }
+    return JNI_FALSE;
+  }
+
+  static jobject AllocObject(JNIEnv* env, jclass c) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.c = c}};
+    if (sc.Check(soa, true, "Ec", args) && sc.CheckInstantiableNonArray(soa, c)) {
+      JniValueType result;
+      result.L = baseEnv(env)->AllocObject(env, c);
+      if (sc.Check(soa, false, "L", &result)) {
+        return result.L;
+      }
+    }
+    return nullptr;
+  }
+
+  static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+    if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) &&
+        sc.CheckConstructor(soa, mid)) {
+      JniValueType result;
+      result.L = baseEnv(env)->NewObjectV(env, c, mid, vargs);
+      if (sc.Check(soa, false, "L", &result)) {
+        return result.L;
+      }
+    }
+    return nullptr;
+  }
+
+  static jobject NewObject(JNIEnv* env, jclass c, jmethodID mid, ...) {
+    va_list args;
+    va_start(args, mid);
+    jobject result = NewObjectV(env, c, mid, args);
+    va_end(args);
+    return result;
+  }
+
+  static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+    if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) &&
+        sc.CheckConstructor(soa, mid)) {
+      JniValueType result;
+      result.L = baseEnv(env)->NewObjectA(env, c, mid, vargs);
+      if (sc.Check(soa, false, "L", &result)) {
+        return result.L;
+      }
+    }
+    return nullptr;
+  }
+
+  static jclass GetObjectClass(JNIEnv* env, jobject obj) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = obj}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      result.c = baseEnv(env)->GetObjectClass(env, obj);
+      if (sc.Check(soa, false, "c", &result)) {
+        return result.c;
+      }
+    }
+    return nullptr;
+  }
+
+  static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass c) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.L = obj}, {.c = c}};
+    if (sc.Check(soa, true, "ELc", args)) {
+      JniValueType result;
+      result.b = baseEnv(env)->IsInstanceOf(env, obj, c);
+      if (sc.Check(soa, false, "b", &result)) {
+        return result.b;
+      }
+    }
+    return JNI_FALSE;
+  }
+
+  static jmethodID GetMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+    return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, false);
+  }
+
+  static jmethodID GetStaticMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+    return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, true);
+  }
+
+  static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+    return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, false);
+  }
+
+  static jfieldID GetStaticFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+    return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, true);
+  }
+
+#define FIELD_ACCESSORS(jtype, name, ptype, shorty) \
+  static jtype GetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid) { \
+    return GetField(__FUNCTION__, env, c, fid, true, ptype).shorty; \
+  } \
+  \
+  static jtype Get##name##Field(JNIEnv* env, jobject obj, jfieldID fid) { \
+    return GetField(__FUNCTION__, env, obj, fid, false, ptype).shorty; \
+  } \
+  \
+  static void SetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid, jtype v) { \
+    JniValueType value; \
+    value.shorty = v; \
+    SetField(__FUNCTION__, env, c, fid, true, ptype, value); \
+  } \
+  \
+  static void Set##name##Field(JNIEnv* env, jobject obj, jfieldID fid, jtype v) { \
+    JniValueType value; \
+    value.shorty = v; \
+    SetField(__FUNCTION__, env, obj, fid, false, ptype, value); \
+  }
+
+  FIELD_ACCESSORS(jobject, Object, Primitive::kPrimNot, L)
+  FIELD_ACCESSORS(jboolean, Boolean, Primitive::kPrimBoolean, Z)
+  FIELD_ACCESSORS(jbyte, Byte, Primitive::kPrimByte, B)
+  FIELD_ACCESSORS(jchar, Char, Primitive::kPrimChar, C)
+  FIELD_ACCESSORS(jshort, Short, Primitive::kPrimShort, S)
+  FIELD_ACCESSORS(jint, Int, Primitive::kPrimInt, I)
+  FIELD_ACCESSORS(jlong, Long, Primitive::kPrimLong, J)
+  FIELD_ACCESSORS(jfloat, Float, Primitive::kPrimFloat, F)
+  FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D)
+#undef FIELD_ACCESSORS
+
+  static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) {
+    CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+  }
+
+  static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid,
+                                        jvalue* vargs) {
+    CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+  }
+
+  static void CallStaticVoidMethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) {
+    CallMethodA(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+  }
+
+  static void CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list vargs) {
+    CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+  }
+
+  static void CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass c, jmethodID mid,
+                                        va_list vargs) {
+    CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+  }
+
+  static void CallStaticVoidMethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
+    CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+  }
+
+  static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
+    va_list vargs;
+    va_start(vargs, mid);
+    CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+    va_end(vargs);
+  }
+
+  static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass c, jmethodID mid, ...) {
+    va_list vargs;
+    va_start(vargs, mid);
+    CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+    va_end(vargs);
+  }
+
+  static void CallStaticVoidMethod(JNIEnv* env, jclass c, jmethodID mid, ...) {
+    va_list vargs;
+    va_start(vargs, mid);
+    CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+    va_end(vargs);
+  }
+
+#define CALL(rtype, name, ptype, shorty) \
+  static rtype Call##name##MethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) { \
+    return CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+  } \
+  \
+  static rtype CallNonvirtual##name##MethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+                                             jvalue* vargs) { \
+    return CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+  } \
+  \
+  static rtype CallStatic##name##MethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { \
+    return CallMethodA(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+  } \
+  \
+  static rtype Call##name##MethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list vargs) { \
+    return CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+  } \
+  \
+  static rtype CallNonvirtual##name##MethodV(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+                                             va_list vargs) { \
+    return CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+  } \
+  \
+  static rtype CallStatic##name##MethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) { \
+    return CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+  } \
+  \
+  static rtype Call##name##Method(JNIEnv* env, jobject obj, jmethodID mid, ...) { \
+    va_list vargs; \
+    va_start(vargs, mid); \
+    rtype result = \
+        CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+    va_end(vargs); \
+    return result; \
+  } \
+  \
+  static rtype CallNonvirtual##name##Method(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+                                            ...) { \
+    va_list vargs; \
+    va_start(vargs, mid); \
+    rtype result = \
+        CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+    va_end(vargs); \
+    return result; \
+  } \
+  \
+  static rtype CallStatic##name##Method(JNIEnv* env, jclass c, jmethodID mid, ...) { \
+    va_list vargs; \
+    va_start(vargs, mid); \
+    rtype result = \
+        CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+    va_end(vargs); \
+    return result; \
+  }
+
+  CALL(jobject, Object, Primitive::kPrimNot, L)
+  CALL(jboolean, Boolean, Primitive::kPrimBoolean, Z)
+  CALL(jbyte, Byte, Primitive::kPrimByte, B)
+  CALL(jchar, Char, Primitive::kPrimChar, C)
+  CALL(jshort, Short, Primitive::kPrimShort, S)
+  CALL(jint, Int, Primitive::kPrimInt, I)
+  CALL(jlong, Long, Primitive::kPrimLong, J)
+  CALL(jfloat, Float, Primitive::kPrimFloat, F)
+  CALL(jdouble, Double, Primitive::kPrimDouble, D)
+#undef CALL
+
+  static jstring NewString(JNIEnv* env, const jchar* unicode_chars, jsize len) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.p = unicode_chars}, {.z = len}};
+    if (sc.Check(soa, true, "Epz", args)) {
+      JniValueType result;
+      result.s = baseEnv(env)->NewString(env, unicode_chars, len);
+      if (sc.Check(soa, false, "s", &result)) {
+        return result.s;
+      }
+    }
+    return nullptr;
+  }
+
+  static jstring NewStringUTF(JNIEnv* env, const char* chars) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.u = chars}};
+    if (sc.Check(soa, true, "Eu", args)) {
+      JniValueType result;
+      // TODO: stale? show pointer and truncate string.
+      result.s = baseEnv(env)->NewStringUTF(env, chars);
+      if (sc.Check(soa, false, "s", &result)) {
+        return result.s;
+      }
+    }
+    return nullptr;
+  }
+
+  static jsize GetStringLength(JNIEnv* env, jstring string) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.s = string}};
+    if (sc.Check(soa, true, "Es", args)) {
+      JniValueType result;
+      result.z = baseEnv(env)->GetStringLength(env, string);
+      if (sc.Check(soa, false, "z", &result)) {
+        return result.z;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static jsize GetStringUTFLength(JNIEnv* env, jstring string) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.s = string}};
+    if (sc.Check(soa, true, "Es", args)) {
+      JniValueType result;
+      result.z = baseEnv(env)->GetStringUTFLength(env, string);
+      if (sc.Check(soa, false, "z", &result)) {
+        return result.z;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static const jchar* GetStringChars(JNIEnv* env, jstring string, jboolean* is_copy) {
+    return reinterpret_cast<const jchar*>(GetStringCharsInternal(__FUNCTION__, env, string,
+                                                                 is_copy, false, false));
+  }
+
+  static const char* GetStringUTFChars(JNIEnv* env, jstring string, jboolean* is_copy) {
+    return reinterpret_cast<const char*>(GetStringCharsInternal(__FUNCTION__, env, string,
+                                                                is_copy, true, false));
+  }
+
+  static const jchar* GetStringCritical(JNIEnv* env, jstring string, jboolean* is_copy) {
+    return reinterpret_cast<const jchar*>(GetStringCharsInternal(__FUNCTION__, env, string,
+                                                                 is_copy, false, true));
+  }
+
+  static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
+    ReleaseStringCharsInternal(__FUNCTION__, env, string, chars, false, false);
+  }
+
+  static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) {
+    ReleaseStringCharsInternal(__FUNCTION__, env, string, utf, true, false);
+  }
+
+  static void ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* chars) {
+    ReleaseStringCharsInternal(__FUNCTION__, env, string, chars, false, true);
+  }
+
+  static void GetStringRegion(JNIEnv* env, jstring string, jsize start, jsize len, jchar* buf) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+    JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
+    // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+    // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+    if (sc.Check(soa, true, "EsIIp", args)) {
+      baseEnv(env)->GetStringRegion(env, string, start, len, buf);
+      JniValueType result;
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
+
+  static void GetStringUTFRegion(JNIEnv* env, jstring string, jsize start, jsize len, char* buf) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+    JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
+    // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+    // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+    if (sc.Check(soa, true, "EsIIp", args)) {
+      baseEnv(env)->GetStringUTFRegion(env, string, start, len, buf);
+      JniValueType result;
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
+
+  static jsize GetArrayLength(JNIEnv* env, jarray array) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.a = array}};
+    if (sc.Check(soa, true, "Ea", args)) {
+      JniValueType result;
+      result.z = baseEnv(env)->GetArrayLength(env, array);
+      if (sc.Check(soa, false, "z", &result)) {
+        return result.z;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_class,
+                                     jobject initial_element) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[4] =
+        {{.E = env}, {.z = length}, {.c = element_class}, {.L = initial_element}};
+    if (sc.Check(soa, true, "EzcL", args)) {
+      JniValueType result;
+      // Note: assignability tests of initial_element are done in the base implementation.
+      result.a = baseEnv(env)->NewObjectArray(env, length, element_class, initial_element);
+      if (sc.Check(soa, false, "a", &result)) {
+        return down_cast<jobjectArray>(result.a);
+      }
+    }
+    return nullptr;
+  }
+
+  static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.a = array}, {.z = index}};
+    if (sc.Check(soa, true, "Eaz", args)) {
+      JniValueType result;
+      result.L = baseEnv(env)->GetObjectArrayElement(env, array, index);
+      if (sc.Check(soa, false, "L", &result)) {
+        return result.L;
+      }
+    }
+    return nullptr;
+  }
+
+  static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[4] = {{.E = env}, {.a = array}, {.z = index}, {.L = value}};
+    // Note: the index arguments is checked as 'I' rather than 'z' as invalid indices result in
+    // ArrayIndexOutOfBoundsExceptions in the base implementation. Similarly invalid stores result
+    // in ArrayStoreExceptions.
+    if (sc.Check(soa, true, "EaIL", args)) {
+      baseEnv(env)->SetObjectArrayElement(env, array, index, value);
+      JniValueType result;
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
+
+  static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) {
+    return down_cast<jbooleanArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+                                                      Primitive::kPrimBoolean));
+  }
+
+  static jbyteArray NewByteArray(JNIEnv* env, jsize length) {
+    return down_cast<jbyteArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+                                                   Primitive::kPrimByte));
+  }
+
+  static jcharArray NewCharArray(JNIEnv* env, jsize length) {
+    return down_cast<jcharArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+                                                   Primitive::kPrimChar));
+  }
+
+  static jshortArray NewShortArray(JNIEnv* env, jsize length) {
+    return down_cast<jshortArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+                                                    Primitive::kPrimShort));
+  }
+
+  static jintArray NewIntArray(JNIEnv* env, jsize length) {
+    return down_cast<jintArray>(NewPrimitiveArray(__FUNCTION__, env, length, Primitive::kPrimInt));
+  }
+
+  static jlongArray NewLongArray(JNIEnv* env, jsize length) {
+    return down_cast<jlongArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+                                                   Primitive::kPrimLong));
+  }
+
+  static jfloatArray NewFloatArray(JNIEnv* env, jsize length) {
+    return down_cast<jfloatArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+                                                    Primitive::kPrimFloat));
+  }
+
+  static jdoubleArray NewDoubleArray(JNIEnv* env, jsize length) {
+    return down_cast<jdoubleArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+                                                     Primitive::kPrimDouble));
+  }
+
+#define PRIMITIVE_ARRAY_FUNCTIONS(ctype, name, ptype) \
+  static ctype* Get##name##ArrayElements(JNIEnv* env, ctype##Array array, jboolean* is_copy) { \
+    return reinterpret_cast<ctype*>( \
+        GetPrimitiveArrayElements(__FUNCTION__, ptype, env, array, is_copy)); \
+  } \
+  \
+  static void Release##name##ArrayElements(JNIEnv* env, ctype##Array array, ctype* elems, \
+                                           jint mode) { \
+    ReleasePrimitiveArrayElements(__FUNCTION__, ptype, env, array, elems, mode); \
+  } \
+  \
+  static void Get##name##ArrayRegion(JNIEnv* env, ctype##Array array, jsize start, jsize len, \
+                                     ctype* buf) { \
+    GetPrimitiveArrayRegion(__FUNCTION__, ptype, env, array, start, len, buf); \
+  } \
+  \
+  static void Set##name##ArrayRegion(JNIEnv* env, ctype##Array array, jsize start, jsize len, \
+                                     const ctype* buf) { \
+    SetPrimitiveArrayRegion(__FUNCTION__, ptype, env, array, start, len, buf); \
+  }
+
+  PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, Primitive::kPrimBoolean)
+  PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, Primitive::kPrimByte)
+  PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, Primitive::kPrimChar)
+  PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, Primitive::kPrimShort)
+  PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, Primitive::kPrimInt)
+  PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, Primitive::kPrimLong)
+  PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, Primitive::kPrimFloat)
+  PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, Primitive::kPrimDouble)
+#undef PRIMITIVE_ARRAY_FUNCTIONS
+
+  static jint MonitorEnter(JNIEnv* env, jobject obj) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = obj}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      result.i = baseEnv(env)->MonitorEnter(env, obj);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static jint MonitorExit(JNIEnv* env, jobject obj) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = obj}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      result.i = baseEnv(env)->MonitorExit(env, obj);
+      if (sc.Check(soa, false, "i", &result)) {
+        return result.i;
+      }
+    }
+    return JNI_ERR;
+  }
+
+  static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* is_copy) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritGet, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
+    if (sc.Check(soa, true, "Eap", args)) {
+      JniValueType result;
+      result.p = baseEnv(env)->GetPrimitiveArrayCritical(env, array, is_copy);
+      if (result.p != nullptr && soa.ForceCopy()) {
+        result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+      }
+      if (sc.Check(soa, false, "p", &result)) {
+        return const_cast<void*>(result.p);
+      }
+    }
+    return nullptr;
+  }
+
+  static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_CritRelease | kFlag_ExcepOkay, __FUNCTION__);
+    sc.CheckNonNull(carray);
+    JniValueType args[4] = {{.E = env}, {.a = array}, {.p = carray}, {.r = mode}};
+    if (sc.Check(soa, true, "Eapr", args)) {
+      if (soa.ForceCopy()) {
+        GuardedCopy::ReleaseGuardedPACopy(__FUNCTION__, env, array, carray, mode);
+      }
+      baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
+      JniValueType result;
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
   }
 
   static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EpJ", env, address, capacity);
-    if (address == nullptr) {
-      JniAbortF(__FUNCTION__, "non-nullable address is NULL");
-      return nullptr;
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[3] = {{.E = env}, {.p = address}, {.J = capacity}};
+    if (sc.Check(soa, true, "EpJ", args)) {
+      JniValueType result;
+      // Note: the validity of address and capacity are checked in the base implementation.
+      result.L = baseEnv(env)->NewDirectByteBuffer(env, address, capacity);
+      if (sc.Check(soa, false, "L", &result)) {
+        return result.L;
+      }
     }
-    return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity));
+    return nullptr;
   }
 
   static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
-    // TODO: check that 'buf' is a java.nio.Buffer.
-    return CHECK_JNI_EXIT("p", baseEnv(env)->GetDirectBufferAddress(env, buf));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = buf}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      // Note: this is implemented in the base environment by a GetLongField which will sanity
+      // check the type of buf in GetLongField above.
+      result.p = baseEnv(env)->GetDirectBufferAddress(env, buf);
+      if (sc.Check(soa, false, "p", &result)) {
+        return const_cast<void*>(result.p);
+      }
+    }
+    return nullptr;
   }
 
   static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
-    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
-    // TODO: check that 'buf' is a java.nio.Buffer.
-    return CHECK_JNI_EXIT("J", baseEnv(env)->GetDirectBufferCapacity(env, buf));
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.L = buf}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      // Note: this is implemented in the base environment by a GetIntField which will sanity
+      // check the type of buf in GetIntField above.
+      result.J = baseEnv(env)->GetDirectBufferCapacity(env, buf);
+      if (sc.Check(soa, false, "J", &result)) {
+        return result.J;
+      }
+    }
+    return JNI_ERR;
   }
 
  private:
-  static inline const JNINativeInterface* baseEnv(JNIEnv* env) {
+  static JavaVMExt* GetJavaVMExt(JNIEnv* env) {
+    return reinterpret_cast<JNIEnvExt*>(env)->vm;
+  }
+
+  static const JNINativeInterface* baseEnv(JNIEnv* env) {
     return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions;
   }
+
+  static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[2] = {{.E = env}, {.L = obj}};
+    if (sc.Check(soa, true, "EL", args)) {
+      JniValueType result;
+      switch (kind) {
+        case kGlobal:
+          result.L = baseEnv(env)->NewGlobalRef(env, obj);
+          break;
+        case kLocal:
+          result.L = baseEnv(env)->NewLocalRef(env, obj);
+          break;
+        case kWeakGlobal:
+          result.L = baseEnv(env)->NewWeakGlobalRef(env, obj);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected reference kind: " << kind;
+      }
+      if (sc.Check(soa, false, "L", &result)) {
+        DCHECK_EQ(IsSameObject(env, obj, result.L), JNI_TRUE);
+        DCHECK(sc.CheckReferenceKind(kind, soa.Vm(), soa.Self(), result.L));
+        return result.L;
+      }
+    }
+    return nullptr;
+  }
+
+  static void DeleteRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, function_name);
+    JniValueType args[2] = {{.E = env}, {.L = obj}};
+    sc.Check(soa, true, "EL", args);
+    if (sc.CheckReferenceKind(kind, soa.Vm(), soa.Self(), obj)) {
+      JniValueType result;
+      switch (kind) {
+        case kGlobal:
+          baseEnv(env)->DeleteGlobalRef(env, obj);
+          break;
+        case kLocal:
+          baseEnv(env)->DeleteLocalRef(env, obj);
+          break;
+        case kWeakGlobal:
+          baseEnv(env)->DeleteWeakGlobalRef(env, obj);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected reference kind: " << kind;
+      }
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
+
+  static jmethodID GetMethodIDInternal(const char* function_name, JNIEnv* env, jclass c,
+                                       const char* name, const char* sig, bool is_static) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
+    if (sc.Check(soa, true, "Ecuu", args)) {
+      JniValueType result;
+      if (is_static) {
+        result.m = baseEnv(env)->GetStaticMethodID(env, c, name, sig);
+      } else {
+        result.m = baseEnv(env)->GetMethodID(env, c, name, sig);
+      }
+      if (sc.Check(soa, false, "m", &result)) {
+        return result.m;
+      }
+    }
+    return nullptr;
+  }
+
+  static jfieldID GetFieldIDInternal(const char* function_name, JNIEnv* env, jclass c,
+                                     const char* name, const char* sig, bool is_static) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
+    if (sc.Check(soa, true, "Ecuu", args)) {
+      JniValueType result;
+      if (is_static) {
+        result.f = baseEnv(env)->GetStaticFieldID(env, c, name, sig);
+      } else {
+        result.f = baseEnv(env)->GetFieldID(env, c, name, sig);
+      }
+      if (sc.Check(soa, false, "f", &result)) {
+        return result.f;
+      }
+    }
+    return nullptr;
+  }
+
+  static JniValueType GetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
+                               bool is_static, Primitive::Type type) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[3] = {{.E = env}, {.L = obj}, {.f = fid}};
+    JniValueType result;
+    if (sc.Check(soa, true, is_static ? "Ecf" : "ELf", args) &&
+        sc.CheckFieldAccess(soa, obj, fid, is_static, type)) {
+      const char* result_check = nullptr;
+      switch (type) {
+        case Primitive::kPrimNot:
+          if (is_static) {
+            result.L = baseEnv(env)->GetStaticObjectField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.L = baseEnv(env)->GetObjectField(env, obj, fid);
+          }
+          result_check = "L";
+          break;
+        case Primitive::kPrimBoolean:
+          if (is_static) {
+            result.Z = baseEnv(env)->GetStaticBooleanField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.Z = baseEnv(env)->GetBooleanField(env, obj, fid);
+          }
+          result_check = "Z";
+          break;
+        case Primitive::kPrimByte:
+          if (is_static) {
+            result.B = baseEnv(env)->GetStaticByteField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.B = baseEnv(env)->GetByteField(env, obj, fid);
+          }
+          result_check = "B";
+          break;
+        case Primitive::kPrimChar:
+          if (is_static) {
+            result.C = baseEnv(env)->GetStaticCharField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.C = baseEnv(env)->GetCharField(env, obj, fid);
+          }
+          result_check = "C";
+          break;
+        case Primitive::kPrimShort:
+          if (is_static) {
+            result.S = baseEnv(env)->GetStaticShortField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.S = baseEnv(env)->GetShortField(env, obj, fid);
+          }
+          result_check = "S";
+          break;
+        case Primitive::kPrimInt:
+          if (is_static) {
+            result.I = baseEnv(env)->GetStaticIntField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.I = baseEnv(env)->GetIntField(env, obj, fid);
+          }
+          result_check = "I";
+          break;
+        case Primitive::kPrimLong:
+          if (is_static) {
+            result.J = baseEnv(env)->GetStaticLongField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.J = baseEnv(env)->GetLongField(env, obj, fid);
+          }
+          result_check = "J";
+          break;
+        case Primitive::kPrimFloat:
+          if (is_static) {
+            result.F = baseEnv(env)->GetStaticFloatField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.F = baseEnv(env)->GetFloatField(env, obj, fid);
+          }
+          result_check = "F";
+          break;
+        case Primitive::kPrimDouble:
+          if (is_static) {
+            result.D = baseEnv(env)->GetStaticDoubleField(env, down_cast<jclass>(obj), fid);
+          } else {
+            result.D = baseEnv(env)->GetDoubleField(env, obj, fid);
+          }
+          result_check = "D";
+          break;
+        case Primitive::kPrimVoid:
+          LOG(FATAL) << "Unexpected type: " << type;
+          break;
+      }
+      if (sc.Check(soa, false, result_check, &result)) {
+        return result;
+      }
+    }
+    result.J = 0;
+    return result;
+  }
+
+  static void SetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
+                       bool is_static, Primitive::Type type, JniValueType value) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[4] = {{.E = env}, {.L = obj}, {.f = fid}, value};
+    char sig[5] = { 'E', is_static ? 'c' : 'L', 'f',
+        type == Primitive::kPrimNot ? 'L' : Primitive::Descriptor(type)[0], '\0'};
+    if (sc.Check(soa, true, sig, args) &&
+        sc.CheckFieldAccess(soa, obj, fid, is_static, type)) {
+      switch (type) {
+        case Primitive::kPrimNot:
+          if (is_static) {
+            baseEnv(env)->SetStaticObjectField(env, down_cast<jclass>(obj), fid, value.L);
+          } else {
+            baseEnv(env)->SetObjectField(env, obj, fid, value.L);
+          }
+          break;
+        case Primitive::kPrimBoolean:
+          if (is_static) {
+            baseEnv(env)->SetStaticBooleanField(env, down_cast<jclass>(obj), fid, value.Z);
+          } else {
+            baseEnv(env)->SetBooleanField(env, obj, fid, value.Z);
+          }
+          break;
+        case Primitive::kPrimByte:
+          if (is_static) {
+            baseEnv(env)->SetStaticByteField(env, down_cast<jclass>(obj), fid, value.B);
+          } else {
+            baseEnv(env)->SetByteField(env, obj, fid, value.B);
+          }
+          break;
+        case Primitive::kPrimChar:
+          if (is_static) {
+            baseEnv(env)->SetStaticCharField(env, down_cast<jclass>(obj), fid, value.C);
+          } else {
+            baseEnv(env)->SetCharField(env, obj, fid, value.C);
+          }
+          break;
+        case Primitive::kPrimShort:
+          if (is_static) {
+            baseEnv(env)->SetStaticShortField(env, down_cast<jclass>(obj), fid, value.S);
+          } else {
+            baseEnv(env)->SetShortField(env, obj, fid, value.S);
+          }
+          break;
+        case Primitive::kPrimInt:
+          if (is_static) {
+            baseEnv(env)->SetStaticIntField(env, down_cast<jclass>(obj), fid, value.I);
+          } else {
+            baseEnv(env)->SetIntField(env, obj, fid, value.I);
+          }
+          break;
+        case Primitive::kPrimLong:
+          if (is_static) {
+            baseEnv(env)->SetStaticLongField(env, down_cast<jclass>(obj), fid, value.J);
+          } else {
+            baseEnv(env)->SetLongField(env, obj, fid, value.J);
+          }
+          break;
+        case Primitive::kPrimFloat:
+          if (is_static) {
+            baseEnv(env)->SetStaticFloatField(env, down_cast<jclass>(obj), fid, value.F);
+          } else {
+            baseEnv(env)->SetFloatField(env, obj, fid, value.F);
+          }
+          break;
+        case Primitive::kPrimDouble:
+          if (is_static) {
+            baseEnv(env)->SetStaticDoubleField(env, down_cast<jclass>(obj), fid, value.D);
+          } else {
+            baseEnv(env)->SetDoubleField(env, obj, fid, value.D);
+          }
+          break;
+        case Primitive::kPrimVoid:
+          LOG(FATAL) << "Unexpected type: " << type;
+          break;
+      }
+      JniValueType result;
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
+
+  static bool CheckCallArgs(ScopedObjectAccess& soa, ScopedCheck& sc, JNIEnv* env, jobject obj,
+                            jclass c, jmethodID mid, InvokeType invoke)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    bool checked;
+    switch (invoke) {
+      case kVirtual: {
+        DCHECK(c == nullptr);
+        JniValueType args[3] = {{.E = env}, {.L = obj}, {.m = mid}};
+        checked = sc.Check(soa, true, "ELm.", args);
+        break;
+      }
+      case kDirect: {
+        JniValueType args[4] = {{.E = env}, {.L = obj}, {.c = c}, {.m = mid}};
+        checked = sc.Check(soa, true, "ELcm.", args);
+        break;
+      }
+      case kStatic: {
+        DCHECK(obj == nullptr);
+        JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+        checked = sc.Check(soa, true, "Ecm.", args);
+        break;
+      }
+      default:
+        LOG(FATAL) << "Unexpected invoke: " << invoke;
+        checked = false;
+        break;
+    }
+    return checked;
+  }
+
+  static JniValueType CallMethodA(const char* function_name, JNIEnv* env, jobject obj, jclass c,
+                                  jmethodID mid, jvalue* vargs, Primitive::Type type,
+                                  InvokeType invoke) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType result;
+    if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) &&
+        sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) {
+      const char* result_check;
+      switch (type) {
+        case Primitive::kPrimNot:
+          result_check = "L";
+          switch (invoke) {
+            case kVirtual:
+              result.L = baseEnv(env)->CallObjectMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.L = baseEnv(env)->CallNonvirtualObjectMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.L = baseEnv(env)->CallStaticObjectMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimBoolean:
+          result_check = "Z";
+          switch (invoke) {
+            case kVirtual:
+              result.Z = baseEnv(env)->CallBooleanMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.Z = baseEnv(env)->CallNonvirtualBooleanMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.Z = baseEnv(env)->CallStaticBooleanMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimByte:
+          result_check = "B";
+          switch (invoke) {
+            case kVirtual:
+              result.B = baseEnv(env)->CallByteMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.B = baseEnv(env)->CallNonvirtualByteMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.B = baseEnv(env)->CallStaticByteMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimChar:
+          result_check = "C";
+          switch (invoke) {
+            case kVirtual:
+              result.C = baseEnv(env)->CallCharMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.C = baseEnv(env)->CallNonvirtualCharMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.C = baseEnv(env)->CallStaticCharMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimShort:
+          result_check = "S";
+          switch (invoke) {
+            case kVirtual:
+              result.S = baseEnv(env)->CallShortMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.S = baseEnv(env)->CallNonvirtualShortMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.S = baseEnv(env)->CallStaticShortMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimInt:
+          result_check = "I";
+          switch (invoke) {
+            case kVirtual:
+              result.I = baseEnv(env)->CallIntMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.I = baseEnv(env)->CallNonvirtualIntMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.I = baseEnv(env)->CallStaticIntMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimLong:
+          result_check = "J";
+          switch (invoke) {
+            case kVirtual:
+              result.J = baseEnv(env)->CallLongMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.J = baseEnv(env)->CallNonvirtualLongMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.J = baseEnv(env)->CallStaticLongMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimFloat:
+          result_check = "F";
+          switch (invoke) {
+            case kVirtual:
+              result.F = baseEnv(env)->CallFloatMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.F = baseEnv(env)->CallNonvirtualFloatMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.F = baseEnv(env)->CallStaticFloatMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimDouble:
+          result_check = "D";
+          switch (invoke) {
+            case kVirtual:
+              result.D = baseEnv(env)->CallDoubleMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.D = baseEnv(env)->CallNonvirtualDoubleMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.D = baseEnv(env)->CallStaticDoubleMethodA(env, c, mid, vargs);
+              break;
+            default:
+              break;
+          }
+          break;
+        case Primitive::kPrimVoid:
+          result_check = "V";
+          result.V = nullptr;
+          switch (invoke) {
+            case kVirtual:
+              baseEnv(env)->CallVoidMethodA(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              baseEnv(env)->CallNonvirtualVoidMethodA(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              baseEnv(env)->CallStaticVoidMethodA(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        default:
+          LOG(FATAL) << "Unexpected return type: " << type;
+          result_check = nullptr;
+      }
+      if (sc.Check(soa, false, result_check, &result)) {
+        return result;
+      }
+    }
+    result.J = 0;
+    return result;
+  }
+
+  static JniValueType CallMethodV(const char* function_name, JNIEnv* env, jobject obj, jclass c,
+                                  jmethodID mid, va_list vargs, Primitive::Type type,
+                                  InvokeType invoke) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType result;
+    if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) &&
+        sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) {
+      const char* result_check;
+      switch (type) {
+        case Primitive::kPrimNot:
+          result_check = "L";
+          switch (invoke) {
+            case kVirtual:
+              result.L = baseEnv(env)->CallObjectMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.L = baseEnv(env)->CallNonvirtualObjectMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.L = baseEnv(env)->CallStaticObjectMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimBoolean:
+          result_check = "Z";
+          switch (invoke) {
+            case kVirtual:
+              result.Z = baseEnv(env)->CallBooleanMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.Z = baseEnv(env)->CallNonvirtualBooleanMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.Z = baseEnv(env)->CallStaticBooleanMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimByte:
+          result_check = "B";
+          switch (invoke) {
+            case kVirtual:
+              result.B = baseEnv(env)->CallByteMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.B = baseEnv(env)->CallNonvirtualByteMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.B = baseEnv(env)->CallStaticByteMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimChar:
+          result_check = "C";
+          switch (invoke) {
+            case kVirtual:
+              result.C = baseEnv(env)->CallCharMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.C = baseEnv(env)->CallNonvirtualCharMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.C = baseEnv(env)->CallStaticCharMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimShort:
+          result_check = "S";
+          switch (invoke) {
+            case kVirtual:
+              result.S = baseEnv(env)->CallShortMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.S = baseEnv(env)->CallNonvirtualShortMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.S = baseEnv(env)->CallStaticShortMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimInt:
+          result_check = "I";
+          switch (invoke) {
+            case kVirtual:
+              result.I = baseEnv(env)->CallIntMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.I = baseEnv(env)->CallNonvirtualIntMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.I = baseEnv(env)->CallStaticIntMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimLong:
+          result_check = "J";
+          switch (invoke) {
+            case kVirtual:
+              result.J = baseEnv(env)->CallLongMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.J = baseEnv(env)->CallNonvirtualLongMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.J = baseEnv(env)->CallStaticLongMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimFloat:
+          result_check = "F";
+          switch (invoke) {
+            case kVirtual:
+              result.F = baseEnv(env)->CallFloatMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.F = baseEnv(env)->CallNonvirtualFloatMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.F = baseEnv(env)->CallStaticFloatMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimDouble:
+          result_check = "D";
+          switch (invoke) {
+            case kVirtual:
+              result.D = baseEnv(env)->CallDoubleMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              result.D = baseEnv(env)->CallNonvirtualDoubleMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              result.D = baseEnv(env)->CallStaticDoubleMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        case Primitive::kPrimVoid:
+          result_check = "V";
+          result.V = nullptr;
+          switch (invoke) {
+            case kVirtual:
+              baseEnv(env)->CallVoidMethodV(env, obj, mid, vargs);
+              break;
+            case kDirect:
+              baseEnv(env)->CallNonvirtualVoidMethodV(env, obj, c, mid, vargs);
+              break;
+            case kStatic:
+              baseEnv(env)->CallStaticVoidMethodV(env, c, mid, vargs);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected invoke: " << invoke;
+          }
+          break;
+        default:
+          LOG(FATAL) << "Unexpected return type: " << type;
+          result_check = nullptr;
+      }
+      if (sc.Check(soa, false, result_check, &result)) {
+        return result;
+      }
+    }
+    result.J = 0;
+    return result;
+  }
+
+  static const void* GetStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
+                                            jboolean* is_copy, bool utf, bool critical) {
+    ScopedObjectAccess soa(env);
+    int flags = critical ? kFlag_CritGet : kFlag_CritOkay;
+    ScopedCheck sc(flags, function_name);
+    JniValueType args[3] = {{.E = env}, {.s = string}, {.p = is_copy}};
+    if (sc.Check(soa, true, "Esp", args)) {
+      JniValueType result;
+      if (utf) {
+        CHECK(!critical);
+        result.u = baseEnv(env)->GetStringUTFChars(env, string, is_copy);
+      } else {
+        if (critical) {
+          result.p = baseEnv(env)->GetStringCritical(env, string, is_copy);
+        } else {
+          result.p = baseEnv(env)->GetStringChars(env, string, is_copy);
+        }
+      }
+      // TODO: could we be smarter about not copying when local_is_copy?
+      if (result.p != nullptr && soa.ForceCopy()) {
+        if (utf) {
+          size_t length_in_bytes = strlen(result.u) + 1;
+          result.u =
+              reinterpret_cast<const char*>(GuardedCopy::Create(result.u, length_in_bytes, false));
+        } else {
+          size_t length_in_bytes = baseEnv(env)->GetStringLength(env, string) * 2;
+          result.p =
+              reinterpret_cast<const jchar*>(GuardedCopy::Create(result.p, length_in_bytes, false));
+        }
+        if (is_copy != nullptr) {
+          *is_copy = JNI_TRUE;
+        }
+      }
+      if (sc.Check(soa, false, utf ? "u" : "p", &result)) {
+        return utf ? result.u : result.p;
+      }
+    }
+    return nullptr;
+  }
+
+  static void ReleaseStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
+                                         const void* chars, bool utf, bool critical) {
+    ScopedObjectAccess soa(env);
+    int flags = kFlag_ExcepOkay | kFlag_Release;
+    if (critical) {
+      flags |= kFlag_CritRelease;
+    }
+    ScopedCheck sc(flags, function_name);
+    sc.CheckNonNull(chars);
+    bool force_copy_ok = !soa.ForceCopy() || GuardedCopy::Check(function_name, chars, false);
+    if (force_copy_ok && soa.ForceCopy()) {
+      chars = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<void*>(chars)));
+    }
+    if (force_copy_ok) {
+      JniValueType args[3] = {{.E = env}, {.s = string}, {.p = chars}};
+      if (sc.Check(soa, true, utf ? "Esu" : "Esp", args)) {
+        if (utf) {
+          CHECK(!critical);
+          baseEnv(env)->ReleaseStringUTFChars(env, string, reinterpret_cast<const char*>(chars));
+        } else {
+          if (critical) {
+            baseEnv(env)->ReleaseStringCritical(env, string, reinterpret_cast<const jchar*>(chars));
+          } else {
+            baseEnv(env)->ReleaseStringChars(env, string, reinterpret_cast<const jchar*>(chars));
+          }
+        }
+        JniValueType result;
+        sc.Check(soa, false, "V", &result);
+      }
+    }
+  }
+
+  static jarray NewPrimitiveArray(const char* function_name, JNIEnv* env, jsize length,
+                                  Primitive::Type type) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, __FUNCTION__);
+    JniValueType args[2] = {{.E = env}, {.z = length}};
+    if (sc.Check(soa, true, "Ez", args)) {
+      JniValueType result;
+      switch (type) {
+        case Primitive::kPrimBoolean:
+          result.a = baseEnv(env)->NewBooleanArray(env, length);
+          break;
+        case Primitive::kPrimByte:
+          result.a = baseEnv(env)->NewByteArray(env, length);
+          break;
+        case Primitive::kPrimChar:
+          result.a = baseEnv(env)->NewCharArray(env, length);
+          break;
+        case Primitive::kPrimShort:
+          result.a = baseEnv(env)->NewShortArray(env, length);
+          break;
+        case Primitive::kPrimInt:
+          result.a = baseEnv(env)->NewIntArray(env, length);
+          break;
+        case Primitive::kPrimLong:
+          result.a = baseEnv(env)->NewLongArray(env, length);
+          break;
+        case Primitive::kPrimFloat:
+          result.a = baseEnv(env)->NewFloatArray(env, length);
+          break;
+        case Primitive::kPrimDouble:
+          result.a = baseEnv(env)->NewDoubleArray(env, length);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected primitive type: " << type;
+      }
+      if (sc.Check(soa, false, "a", &result)) {
+        return result.a;
+      }
+    }
+    return nullptr;
+  }
+
+  static void* GetPrimitiveArrayElements(const char* function_name, Primitive::Type type,
+                                         JNIEnv* env, jarray array, jboolean* is_copy) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
+    if (sc.Check(soa, true, "Eap", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+      JniValueType result;
+      switch (type) {
+        case Primitive::kPrimBoolean:
+          result.p = baseEnv(env)->GetBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+                                                           is_copy);
+          break;
+        case Primitive::kPrimByte:
+          result.p = baseEnv(env)->GetByteArrayElements(env, down_cast<jbyteArray>(array),
+                                                        is_copy);
+          break;
+        case Primitive::kPrimChar:
+          result.p = baseEnv(env)->GetCharArrayElements(env, down_cast<jcharArray>(array),
+                                                        is_copy);
+          break;
+        case Primitive::kPrimShort:
+          result.p = baseEnv(env)->GetShortArrayElements(env, down_cast<jshortArray>(array),
+                                                         is_copy);
+          break;
+        case Primitive::kPrimInt:
+          result.p = baseEnv(env)->GetIntArrayElements(env, down_cast<jintArray>(array), is_copy);
+          break;
+        case Primitive::kPrimLong:
+          result.p = baseEnv(env)->GetLongArrayElements(env, down_cast<jlongArray>(array),
+                                                        is_copy);
+          break;
+        case Primitive::kPrimFloat:
+          result.p = baseEnv(env)->GetFloatArrayElements(env, down_cast<jfloatArray>(array),
+                                                         is_copy);
+          break;
+        case Primitive::kPrimDouble:
+          result.p = baseEnv(env)->GetDoubleArrayElements(env, down_cast<jdoubleArray>(array),
+                                                          is_copy);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected primitive type: " << type;
+      }
+      if (result.p != nullptr && soa.ForceCopy()) {
+        result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+        if (is_copy != nullptr) {
+          *is_copy = JNI_TRUE;
+        }
+      }
+      if (sc.Check(soa, false, "p", &result)) {
+        return const_cast<void*>(result.p);
+      }
+    }
+    return nullptr;
+  }
+
+  static void ReleasePrimitiveArrayElements(const char* function_name, Primitive::Type type,
+                                            JNIEnv* env, jarray array, void* elems, jint mode) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_ExcepOkay, function_name);
+    if (sc.CheckNonNull(elems) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+      if (soa.ForceCopy()) {
+        elems = GuardedCopy::ReleaseGuardedPACopy(function_name, env, array, elems, mode);
+      }
+      if (!soa.ForceCopy() || elems != nullptr) {
+        JniValueType args[4] = {{.E = env}, {.a = array}, {.p = elems}, {.r = mode}};
+        if (sc.Check(soa, true, "Eapr", args)) {
+          switch (type) {
+            case Primitive::kPrimBoolean:
+              baseEnv(env)->ReleaseBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+                                                        reinterpret_cast<jboolean*>(elems), mode);
+              break;
+            case Primitive::kPrimByte:
+              baseEnv(env)->ReleaseByteArrayElements(env, down_cast<jbyteArray>(array),
+                                                     reinterpret_cast<jbyte*>(elems), mode);
+              break;
+            case Primitive::kPrimChar:
+              baseEnv(env)->ReleaseCharArrayElements(env, down_cast<jcharArray>(array),
+                                                     reinterpret_cast<jchar*>(elems), mode);
+              break;
+            case Primitive::kPrimShort:
+              baseEnv(env)->ReleaseShortArrayElements(env, down_cast<jshortArray>(array),
+                                                      reinterpret_cast<jshort*>(elems), mode);
+              break;
+            case Primitive::kPrimInt:
+              baseEnv(env)->ReleaseIntArrayElements(env, down_cast<jintArray>(array),
+                                                    reinterpret_cast<jint*>(elems), mode);
+              break;
+            case Primitive::kPrimLong:
+              baseEnv(env)->ReleaseLongArrayElements(env, down_cast<jlongArray>(array),
+                                                     reinterpret_cast<jlong*>(elems), mode);
+              break;
+            case Primitive::kPrimFloat:
+              baseEnv(env)->ReleaseFloatArrayElements(env, down_cast<jfloatArray>(array),
+                                                      reinterpret_cast<jfloat*>(elems), mode);
+              break;
+            case Primitive::kPrimDouble:
+              baseEnv(env)->ReleaseDoubleArrayElements(env, down_cast<jdoubleArray>(array),
+                                                       reinterpret_cast<jdouble*>(elems), mode);
+              break;
+            default:
+              LOG(FATAL) << "Unexpected primitive type: " << type;
+          }
+          JniValueType result;
+          result.V = nullptr;
+          sc.Check(soa, false, "V", &result);
+        }
+      }
+    }
+  }
+
+  static void GetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
+                                      jarray array, jsize start, jsize len, void* buf) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
+    // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+    // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+    if (sc.Check(soa, true, "EaIIp", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+      switch (type) {
+        case Primitive::kPrimBoolean:
+          baseEnv(env)->GetBooleanArrayRegion(env, down_cast<jbooleanArray>(array), start, len,
+                                              reinterpret_cast<jboolean*>(buf));
+          break;
+        case Primitive::kPrimByte:
+          baseEnv(env)->GetByteArrayRegion(env, down_cast<jbyteArray>(array), start, len,
+                                           reinterpret_cast<jbyte*>(buf));
+          break;
+        case Primitive::kPrimChar:
+          baseEnv(env)->GetCharArrayRegion(env, down_cast<jcharArray>(array), start, len,
+                                           reinterpret_cast<jchar*>(buf));
+          break;
+        case Primitive::kPrimShort:
+          baseEnv(env)->GetShortArrayRegion(env, down_cast<jshortArray>(array), start, len,
+                                            reinterpret_cast<jshort*>(buf));
+          break;
+        case Primitive::kPrimInt:
+          baseEnv(env)->GetIntArrayRegion(env, down_cast<jintArray>(array), start, len,
+                                          reinterpret_cast<jint*>(buf));
+          break;
+        case Primitive::kPrimLong:
+          baseEnv(env)->GetLongArrayRegion(env, down_cast<jlongArray>(array), start, len,
+                                           reinterpret_cast<jlong*>(buf));
+          break;
+        case Primitive::kPrimFloat:
+          baseEnv(env)->GetFloatArrayRegion(env, down_cast<jfloatArray>(array), start, len,
+                                            reinterpret_cast<jfloat*>(buf));
+          break;
+        case Primitive::kPrimDouble:
+          baseEnv(env)->GetDoubleArrayRegion(env, down_cast<jdoubleArray>(array), start, len,
+                                             reinterpret_cast<jdouble*>(buf));
+          break;
+        default:
+          LOG(FATAL) << "Unexpected primitive type: " << type;
+      }
+      JniValueType result;
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
+
+  static void SetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
+                                      jarray array, jsize start, jsize len, const void* buf) {
+    ScopedObjectAccess soa(env);
+    ScopedCheck sc(kFlag_Default, function_name);
+    JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
+    // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+    // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+    if (sc.Check(soa, true, "EaIIp", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+      switch (type) {
+        case Primitive::kPrimBoolean:
+          baseEnv(env)->SetBooleanArrayRegion(env, down_cast<jbooleanArray>(array), start, len,
+                                              reinterpret_cast<const jboolean*>(buf));
+          break;
+        case Primitive::kPrimByte:
+          baseEnv(env)->SetByteArrayRegion(env, down_cast<jbyteArray>(array), start, len,
+                                           reinterpret_cast<const jbyte*>(buf));
+          break;
+        case Primitive::kPrimChar:
+          baseEnv(env)->SetCharArrayRegion(env, down_cast<jcharArray>(array), start, len,
+                                           reinterpret_cast<const jchar*>(buf));
+          break;
+        case Primitive::kPrimShort:
+          baseEnv(env)->SetShortArrayRegion(env, down_cast<jshortArray>(array), start, len,
+                                              reinterpret_cast<const jshort*>(buf));
+          break;
+        case Primitive::kPrimInt:
+          baseEnv(env)->SetIntArrayRegion(env, down_cast<jintArray>(array), start, len,
+                                          reinterpret_cast<const jint*>(buf));
+          break;
+        case Primitive::kPrimLong:
+          baseEnv(env)->SetLongArrayRegion(env, down_cast<jlongArray>(array), start, len,
+                                              reinterpret_cast<const jlong*>(buf));
+          break;
+        case Primitive::kPrimFloat:
+          baseEnv(env)->SetFloatArrayRegion(env, down_cast<jfloatArray>(array), start, len,
+                                            reinterpret_cast<const jfloat*>(buf));
+          break;
+        case Primitive::kPrimDouble:
+          baseEnv(env)->SetDoubleArrayRegion(env, down_cast<jdoubleArray>(array), start, len,
+                                             reinterpret_cast<const jdouble*>(buf));
+          break;
+        default:
+          LOG(FATAL) << "Unexpected primitive type: " << type;
+      }
+      JniValueType result;
+      result.V = nullptr;
+      sc.Check(soa, false, "V", &result);
+    }
+  }
 };
 
 const JNINativeInterface gCheckNativeInterface = {
@@ -2025,38 +3608,58 @@
 class CheckJII {
  public:
   static jint DestroyJavaVM(JavaVM* vm) {
-    ScopedCheck sc(vm, false, __FUNCTION__);
-    sc.Check(true, "v", vm);
-    return CHECK_JNI_EXIT("I", BaseVm(vm)->DestroyJavaVM(vm));
+    ScopedCheck sc(kFlag_Invocation, __FUNCTION__, false);
+    JniValueType args[1] = {{.v = vm}};
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "v", args);
+    JniValueType result;
+    result.i = BaseVm(vm)->DestroyJavaVM(vm);
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+    return result.i;
   }
 
   static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
-    ScopedCheck sc(vm, false, __FUNCTION__);
-    sc.Check(true, "vpp", vm, p_env, thr_args);
-    return CHECK_JNI_EXIT("I", BaseVm(vm)->AttachCurrentThread(vm, p_env, thr_args));
+    ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+    JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.p = thr_args}};
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpp", args);
+    JniValueType result;
+    result.i = BaseVm(vm)->AttachCurrentThread(vm, p_env, thr_args);
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+    return result.i;
   }
 
   static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
-    ScopedCheck sc(vm, false, __FUNCTION__);
-    sc.Check(true, "vpp", vm, p_env, thr_args);
-    return CHECK_JNI_EXIT("I", BaseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args));
+    ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+    JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.p = thr_args}};
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpp", args);
+    JniValueType result;
+    result.i = BaseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args);
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+    return result.i;
   }
 
   static jint DetachCurrentThread(JavaVM* vm) {
-    ScopedCheck sc(vm, true, __FUNCTION__);
-    sc.Check(true, "v", vm);
-    return CHECK_JNI_EXIT("I", BaseVm(vm)->DetachCurrentThread(vm));
+    ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+    JniValueType args[1] = {{.v = vm}};
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "v", args);
+    JniValueType result;
+    result.i = BaseVm(vm)->DetachCurrentThread(vm);
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+    return result.i;
   }
 
-  static jint GetEnv(JavaVM* vm, void** env, jint version) {
-    ScopedCheck sc(vm, true, __FUNCTION__);
-    sc.Check(true, "vpI", vm);
-    return CHECK_JNI_EXIT("I", BaseVm(vm)->GetEnv(vm, env, version));
+  static jint GetEnv(JavaVM* vm, void** p_env, jint version) {
+    ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+    JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.I = version}};
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpI", args);
+    JniValueType result;
+    result.i = BaseVm(vm)->GetEnv(vm, p_env, version);
+    sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+    return result.i;
   }
 
  private:
-  static inline const JNIInvokeInterface* BaseVm(JavaVM* vm) {
-    return reinterpret_cast<JavaVMExt*>(vm)->unchecked_functions;
+  static const JNIInvokeInterface* BaseVm(JavaVM* vm) {
+    return reinterpret_cast<JavaVMExt*>(vm)->GetUncheckedFunctions();
   }
 };
 
diff --git a/runtime/check_jni.h b/runtime/check_jni.h
new file mode 100644
index 0000000..f41abf8
--- /dev/null
+++ b/runtime/check_jni.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_CHECK_JNI_H_
+#define ART_RUNTIME_CHECK_JNI_H_
+
+#include <jni.h>
+
+namespace art {
+
+const JNINativeInterface* GetCheckJniNativeInterface();
+const JNIInvokeInterface* GetCheckJniInvokeInterface();
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_CHECK_JNI_H_
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
new file mode 100644
index 0000000..1a78d72
--- /dev/null
+++ b/runtime/check_reference_map_visitor.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_
+#define ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_
+
+#include "gc_map.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack_map.h"
+
+namespace art {
+
+// Helper class for tests checking that the compiler keeps track of dex registers
+// holding references.
+class CheckReferenceMapVisitor : public StackVisitor {
+ public:
+  explicit CheckReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, nullptr) {}
+
+  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* m = GetMethod();
+    if (m->IsCalleeSaveMethod() || m->IsNative()) {
+      CHECK_EQ(GetDexPc(), DexFile::kDexNoIndex);
+    }
+
+    if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) {
+      return true;
+    }
+
+    LOG(INFO) << "At " << PrettyMethod(m, false);
+
+    if (m->IsCalleeSaveMethod()) {
+      LOG(WARNING) << "no PC for " << PrettyMethod(m);
+      return true;
+    }
+
+    return false;
+  }
+
+  void CheckReferences(int* registers, int number_of_references, uint32_t native_pc_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (GetMethod()->IsOptimized()) {
+      CheckOptimizedMethod(registers, number_of_references, native_pc_offset);
+    } else {
+      CheckQuickMethod(registers, number_of_references, native_pc_offset);
+    }
+  }
+
+ private:
+  void CheckOptimizedMethod(int* registers, int number_of_references, uint32_t native_pc_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* m = GetMethod();
+    CodeInfo code_info = m->GetOptimizedCodeInfo();
+    StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+    DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, m->GetCodeItem()->registers_size_);
+    MemoryRegion stack_mask = stack_map.GetStackMask();
+    uint32_t register_mask = stack_map.GetRegisterMask();
+    for (int i = 0; i < number_of_references; ++i) {
+      int reg = registers[i];
+      CHECK(reg < m->GetCodeItem()->registers_size_);
+      DexRegisterMap::LocationKind location = dex_register_map.GetLocationKind(reg);
+      switch (location) {
+        case DexRegisterMap::kNone:
+          // Not set, should not be a reference.
+          CHECK(false);
+          break;
+        case DexRegisterMap::kInStack:
+          CHECK(stack_mask.LoadBit(dex_register_map.GetValue(reg) >> 2));
+          break;
+        case DexRegisterMap::kInRegister:
+          CHECK_NE(register_mask & dex_register_map.GetValue(reg), 0u);
+          break;
+        case DexRegisterMap::kConstant:
+          CHECK_EQ(dex_register_map.GetValue(0), 0);
+          break;
+      }
+    }
+  }
+
+  void CheckQuickMethod(int* registers, int number_of_references, uint32_t native_pc_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* m = GetMethod();
+    NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
+    const uint8_t* ref_bitmap = map.FindBitMap(native_pc_offset);
+    CHECK(ref_bitmap);
+    for (int i = 0; i < number_of_references; ++i) {
+      int reg = registers[i];
+      CHECK(reg < m->GetCodeItem()->registers_size_);
+      CHECK((*((ref_bitmap) + reg / 8) >> (reg % 8) ) & 0x01)
+          << "Error: Reg @" << i << " is not in GC map";
+    }
+  }
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index d05f7af..875efbb 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -155,6 +155,11 @@
   return resolved_field;
 }
 
+inline mirror::Object* ClassLinker::AllocObject(Thread* self) {
+  return GetClassRoot(kJavaLangObject)->Alloc<true, false>(self,
+      Runtime::Current()->GetHeap()->GetCurrentAllocator());
+}
+
 template <class T>
 inline mirror::ObjectArray<T>* ClassLinker::AllocObjectArray(Thread* self, size_t length) {
   return mirror::ObjectArray<T>::Alloc(self, GetClassRoot(kObjectArrayClass), length);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d718367..6ed27bb 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -16,11 +16,9 @@
 
 #include "class_linker.h"
 
-#include <fcntl.h>
-#include <sys/file.h>
-#include <sys/stat.h>
 #include <deque>
 #include <memory>
+#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
@@ -66,7 +64,7 @@
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "handle_scope-inl.h"
-#include "thread.h"
+#include "thread-inl.h"
 #include "utils.h"
 #include "verifier/method_verifier.h"
 #include "well_known_classes.h"
@@ -91,21 +89,29 @@
   // a NoClassDefFoundError (v2 2.17.5).  The exception to this rule is if we
   // failed in verification, in which case v2 5.4.1 says we need to re-throw
   // the previous error.
-  if (!Runtime::Current()->IsCompiler()) {  // Give info if this occurs at runtime.
+  Runtime* runtime = Runtime::Current();
+  bool is_compiler = runtime->IsCompiler();
+  if (!is_compiler) {  // Give info if this occurs at runtime.
     LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
   }
 
   CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
   Thread* self = Thread::Current();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  if (c->GetVerifyErrorClass() != nullptr) {
-    // TODO: change the verifier to store an _instance_, with a useful detail message?
-    std::string temp;
-    self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp),
-                            PrettyDescriptor(c).c_str());
+  if (is_compiler) {
+    // At compile time, accurate errors and NCDFE are disabled to speed compilation.
+    mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
+    self->SetException(ThrowLocation(), pre_allocated);
   } else {
-    self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
-                            PrettyDescriptor(c).c_str());
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    if (c->GetVerifyErrorClass() != NULL) {
+      // TODO: change the verifier to store an _instance_, with a useful detail message?
+      std::string temp;
+      self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp),
+                              PrettyDescriptor(c).c_str());
+    } else {
+      self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
+                              PrettyDescriptor(c).c_str());
+    }
   }
 }
 
@@ -137,6 +143,88 @@
   return hash;
 }
 
+// Gap between two fields in object layout.
+struct FieldGap {
+  uint32_t start_offset;  // The offset from the start of the object.
+  uint32_t size;  // The gap size of 1, 2, or 4 bytes.
+};
+struct FieldGapsComparator {
+  explicit FieldGapsComparator() {
+  }
+  bool operator() (const FieldGap& lhs, const FieldGap& rhs)
+      NO_THREAD_SAFETY_ANALYSIS {
+    // Sort by gap size, largest first.
+    return lhs.size > rhs.size;
+  }
+};
+typedef std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator> FieldGaps;
+
+// Adds largest aligned gaps to queue of gaps.
+void AddFieldGap(uint32_t gap_start, uint32_t gap_end, FieldGaps* gaps) {
+  DCHECK(gaps != nullptr);
+
+  uint32_t current_offset = gap_start;
+  while (current_offset != gap_end) {
+    size_t remaining = gap_end - current_offset;
+    if (remaining >= sizeof(uint32_t) && IsAligned<4>(current_offset)) {
+      gaps->push(FieldGap {current_offset, sizeof(uint32_t)});
+      current_offset += sizeof(uint32_t);
+    } else if (remaining >= sizeof(uint16_t) && IsAligned<2>(current_offset)) {
+      gaps->push(FieldGap {current_offset, sizeof(uint16_t)});
+      current_offset += sizeof(uint16_t);
+    } else {
+      gaps->push(FieldGap {current_offset, sizeof(uint8_t)});
+      current_offset += sizeof(uint8_t);
+    }
+    DCHECK_LE(current_offset, gap_end) << "Overran gap";
+  }
+}
+// Shuffle fields forward, making use of gaps whenever possible.
+template<int n>
+static void ShuffleForward(const size_t num_fields, size_t* current_field_idx,
+                           MemberOffset* field_offset,
+                           mirror::ObjectArray<mirror::ArtField>* fields,
+                           std::deque<mirror::ArtField*>* grouped_and_sorted_fields,
+                           FieldGaps* gaps)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  DCHECK(current_field_idx != nullptr);
+  DCHECK(grouped_and_sorted_fields != nullptr);
+  DCHECK(fields != nullptr || (num_fields == 0 && grouped_and_sorted_fields->empty()));
+  DCHECK(gaps != nullptr);
+  DCHECK(field_offset != nullptr);
+
+  DCHECK(IsPowerOfTwo(n));
+  while (!grouped_and_sorted_fields->empty()) {
+    mirror::ArtField* field = grouped_and_sorted_fields->front();
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    if (Primitive::ComponentSize(type) < n) {
+      break;
+    }
+    if (!IsAligned<n>(field_offset->Uint32Value())) {
+      MemberOffset old_offset = *field_offset;
+      *field_offset = MemberOffset(RoundUp(field_offset->Uint32Value(), n));
+      AddFieldGap(old_offset.Uint32Value(), field_offset->Uint32Value(), gaps);
+    }
+    CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
+    grouped_and_sorted_fields->pop_front();
+    fields->Set<false>(*current_field_idx, field);
+    if (!gaps->empty() && gaps->top().size >= n) {
+      FieldGap gap = gaps->top();
+      gaps->pop();
+      DCHECK(IsAligned<n>(gap.start_offset));
+      field->SetOffset(MemberOffset(gap.start_offset));
+      if (gap.size > n) {
+        AddFieldGap(gap.start_offset + n, gap.start_offset + gap.size, gaps);
+      }
+    } else {
+      DCHECK(IsAligned<n>(field_offset->Uint32Value()));
+      field->SetOffset(*field_offset);
+      *field_offset = MemberOffset(field_offset->Uint32Value() + n);
+    }
+    ++(*current_field_idx);
+  }
+}
+
 const char* ClassLinker::class_roots_descriptors_[] = {
   "Ljava/lang/Class;",
   "Ljava/lang/Object;",
@@ -226,6 +314,7 @@
     java_lang_Class->AssertReadBarrierPointer();
   }
   java_lang_Class->SetClassSize(mirror::Class::ClassClassSize());
+  java_lang_Class->SetPrimitiveType(Primitive::kPrimNot);
   heap->DecrementDisableMovingGC(self);
   // AllocClass(mirror::Class*) can now be used
 
@@ -250,6 +339,12 @@
   // Setup the char (primitive) class to be used for char[].
   Handle<mirror::Class> char_class(hs.NewHandle(
       AllocClass(self, java_lang_Class.Get(), mirror::Class::PrimitiveClassSize())));
+  // The primitive char class won't be initialized by
+  // InitializePrimitiveClass until line 459, but strings (and
+  // internal char arrays) will be allocated before that and the
+  // component size, which is computed from the primitive type, needs
+  // to be set here.
+  char_class->SetPrimitiveType(Primitive::kPrimChar);
 
   // Setup the char[] class to be used for String.
   Handle<mirror::Class> char_array_class(hs.NewHandle(
@@ -359,7 +454,7 @@
   for (size_t i = 0; i != boot_class_path.size(); ++i) {
     const DexFile* dex_file = boot_class_path[i];
     CHECK(dex_file != nullptr);
-    AppendToBootClassPath(*dex_file);
+    AppendToBootClassPath(self, *dex_file);
   }
 
   // now we can use FindSystemClass
@@ -587,7 +682,7 @@
     if (!c->IsArrayClass() && !c->IsPrimitive()) {
       StackHandleScope<1> hs(self);
       Handle<mirror::Class> h_class(hs.NewHandle(GetClassRoot(ClassRoot(i))));
-      EnsureInitialized(h_class, true, true);
+      EnsureInitialized(self, h_class, true, true);
       self->AssertNoPendingException();
     }
   }
@@ -1134,13 +1229,7 @@
     return false;
   }
 
-  if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
-    *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)",
-                              oat_file->GetLocation().c_str(),
-                              oat_dex_file->GetDexFileLocationChecksum(),
-                              dex_location, dex_location_checksum);
-    return false;
-  }
+  DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
   return true;
 }
 
@@ -1294,7 +1383,6 @@
     if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
                                                        &odex_checksum_verified,
                                                        &odex_error_msg)) {
-      error_msgs->push_back(odex_error_msg);
       return odex_oat_file.release();
     } else {
       if (odex_checksum_verified) {
@@ -1317,7 +1405,6 @@
     if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
                                                         &cache_checksum_verified,
                                                         &cache_error_msg)) {
-      error_msgs->push_back(cache_error_msg);
       return cache_oat_file.release();
     } else if (cache_checksum_verified) {
       // We can just relocate
@@ -1493,17 +1580,16 @@
 bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
                                bool* checksum_verified,
                                std::string* error_msg) {
-  std::string compound_msg("Oat file failed to verify: ");
   Runtime* runtime = Runtime::Current();
-  uint32_t real_image_checksum;
-  void* real_image_oat_offset;
-  int32_t real_patch_delta;
   const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
   if (image_space == nullptr) {
     *error_msg = "No image space present";
     return false;
   }
-  if (isa == Runtime::Current()->GetInstructionSet()) {
+  uint32_t real_image_checksum;
+  void* real_image_oat_offset;
+  int32_t real_patch_delta;
+  if (isa == runtime->GetInstructionSet()) {
     const ImageHeader& image_header = image_space->GetImageHeader();
     real_image_checksum = image_header.GetOatChecksum();
     real_image_oat_offset = image_header.GetOatDataBegin();
@@ -1517,32 +1603,33 @@
   }
 
   const OatHeader& oat_header = oat_file->GetOatHeader();
+  std::string compound_msg;
 
   uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
   *checksum_verified = oat_image_checksum == real_image_checksum;
   if (!*checksum_verified) {
-    compound_msg += StringPrintf(" Oat Image Checksum Incorrect (expected 0x%x, recieved 0x%x)",
-                                 real_image_checksum, oat_image_checksum);
+    StringAppendF(&compound_msg, " Oat Image Checksum Incorrect (expected 0x%x, received 0x%x)",
+                  real_image_checksum, oat_image_checksum);
   }
 
   void* oat_image_oat_offset =
       reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
   bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
   if (!offset_verified) {
-    compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
-                                 real_image_oat_offset, oat_image_oat_offset);
+    StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
+                  real_image_oat_offset, oat_image_oat_offset);
   }
 
   int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
   bool patch_delta_verified = oat_patch_delta == real_patch_delta;
   if (!patch_delta_verified) {
-    compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
-                                 real_patch_delta, oat_patch_delta);
+    StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, received 0x%x)",
+                  real_patch_delta, oat_patch_delta);
   }
 
   bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
-  if (ret) {
-    *error_msg = compound_msg;
+  if (!ret) {
+    *error_msg = "Oat file failed to verify:" + compound_msg;
   }
   return ret;
 }
@@ -1571,9 +1658,7 @@
       method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
       if (method != Runtime::Current()->GetResolutionMethod()) {
         method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
         method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
       }
     }
   }
@@ -1798,7 +1883,7 @@
   } else {
     Thread* self = Thread::Current();
     StackHandleScope<1> hs(self);
-    Handle<mirror::ObjectArray<mirror::Class>> classes =
+    MutableHandle<mirror::ObjectArray<mirror::Class>> classes =
         hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
     GetClassesVisitorArrayArg local_arg;
     local_arg.classes = &classes;
@@ -1808,7 +1893,7 @@
     while (!local_arg.success) {
       size_t class_table_size;
       {
-        ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+        ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
         class_table_size = class_table_.size();
       }
       mirror::Class* class_type = mirror::Class::GetJavaLangClass();
@@ -1952,7 +2037,7 @@
     }
     CHECK(h_class->IsRetired());
     // Get the updated class from class table.
-    klass = LookupClass(descriptor, h_class.Get()->GetClassLoader());
+    klass = LookupClass(self, descriptor, h_class.Get()->GetClassLoader());
   }
 
   // Wait for the class if it has not already been linked.
@@ -2011,11 +2096,11 @@
   ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
   // Check if this would be found in the parent boot class loader.
   if (pair.second != nullptr) {
-    mirror::Class* klass = LookupClass(descriptor, nullptr);
+    mirror::Class* klass = LookupClass(self, descriptor, nullptr);
     if (klass != nullptr) {
       return EnsureResolved(self, descriptor, klass);
     }
-    klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+    klass = DefineClass(self, descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
                         *pair.second);
     if (klass != nullptr) {
       return klass;
@@ -2031,7 +2116,7 @@
         hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
     Handle<mirror::ArtField> dex_file_field =
         hs.NewHandle(
-            soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
+            soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile));
     mirror::Object* dex_path_list =
         soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
         GetObject(class_loader.Get());
@@ -2067,7 +2152,7 @@
               if (dex_class_def != nullptr) {
                 RegisterDexFile(*dex_file);
                 mirror::Class* klass =
-                    DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+                    DefineClass(self, descriptor, class_loader, *dex_file, *dex_class_def);
                 if (klass == nullptr) {
                   CHECK(self->IsExceptionPending()) << descriptor;
                   self->ClearException();
@@ -2095,7 +2180,7 @@
     return FindPrimitiveClass(descriptor[0]);
   }
   // Find the class in the loaded classes table.
-  mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
+  mirror::Class* klass = LookupClass(self, descriptor, class_loader.Get());
   if (klass != nullptr) {
     return EnsureResolved(self, descriptor, klass);
   }
@@ -2106,7 +2191,8 @@
     // The boot class loader, search the boot class path.
     ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
     if (pair.second != nullptr) {
-      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+      return DefineClass(self, descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+                         *pair.second);
     } else {
       // The boot class loader is searched ahead of the application class loader, failures are
       // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
@@ -2118,7 +2204,7 @@
   } else if (Runtime::Current()->UseCompileTimeClassPath()) {
     // First try with the bootstrap class loader.
     if (class_loader.Get() != nullptr) {
-      klass = LookupClass(descriptor, nullptr);
+      klass = LookupClass(self, descriptor, nullptr);
       if (klass != nullptr) {
         return EnsureResolved(self, descriptor, klass);
       }
@@ -2127,7 +2213,8 @@
     // a NoClassDefFoundError being allocated.
     ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
     if (pair.second != nullptr) {
-      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+      return DefineClass(self, descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+                         *pair.second);
     }
     // Next try the compile time class path.
     const std::vector<const DexFile*>* class_path;
@@ -2139,7 +2226,12 @@
     }
     pair = FindInClassPath(descriptor, *class_path);
     if (pair.second != nullptr) {
-      return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
+      return DefineClass(self, descriptor, class_loader, *pair.first, *pair.second);
+    } else {
+      // Use the pre-allocated NCDFE at compile time to avoid wasting time constructing exceptions.
+      mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+      self->SetException(ThrowLocation(), pre_allocated);
+      return nullptr;
     }
   } else {
     ScopedObjectAccessUnchecked soa(self);
@@ -2177,16 +2269,13 @@
       return soa.Decode<mirror::Class*>(result.get());
     }
   }
-
-  ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
-  return nullptr;
+  UNREACHABLE();
 }
 
-mirror::Class* ClassLinker::DefineClass(const char* descriptor,
+mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor,
                                         Handle<mirror::ClassLoader> class_loader,
                                         const DexFile& dex_file,
                                         const DexFile::ClassDef& dex_class_def) {
-  Thread* self = Thread::Current();
   StackHandleScope<3> hs(self);
   auto klass = hs.NewHandle<mirror::Class>(nullptr);
   bool should_allocate = false;
@@ -2227,7 +2316,7 @@
     return nullptr;
   }
   klass->SetDexCache(FindDexCache(dex_file));
-  LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
+  LoadClass(self, dex_file, dex_class_def, klass, class_loader.Get());
   ObjectLock<mirror::Class> lock(self, klass);
   if (self->IsExceptionPending()) {
     // An exception occured during load, set status to erroneous while holding klass' lock in case
@@ -2296,6 +2385,8 @@
                                                        const DexFile::ClassDef& dex_class_def) {
   const byte* class_data = dex_file.GetClassData(dex_class_def);
   size_t num_ref = 0;
+  size_t num_8 = 0;
+  size_t num_16 = 0;
   size_t num_32 = 0;
   size_t num_64 = 0;
   if (class_data != nullptr) {
@@ -2303,29 +2394,45 @@
       const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex());
       const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id);
       char c = descriptor[0];
-      if (c == 'L' || c == '[') {
-        num_ref++;
-      } else if (c == 'J' || c == 'D') {
-        num_64++;
-      } else {
-        num_32++;
+      switch (c) {
+        case 'L':
+        case '[':
+          num_ref++;
+          break;
+        case 'J':
+        case 'D':
+          num_64++;
+          break;
+        case 'I':
+        case 'F':
+          num_32++;
+          break;
+        case 'S':
+        case 'C':
+          num_16++;
+          break;
+        case 'B':
+        case 'Z':
+          num_8++;
+          break;
+        default:
+          LOG(FATAL) << "Unknown descriptor: " << c;
       }
     }
   }
-  return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref);
+  return mirror::Class::ComputeClassSize(false, 0, num_8, num_16, num_32, num_64, num_ref);
 }
 
-bool ClassLinker::FindOatClass(const DexFile& dex_file,
-                               uint16_t class_def_idx,
-                               OatFile::OatClass* oat_class) {
-  DCHECK(oat_class != nullptr);
+OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
+                                            bool* found) {
   DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
   const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file);
   if (oat_dex_file == nullptr) {
-    return false;
+    *found = false;
+    return OatFile::OatClass::Invalid();
   }
-  *oat_class = oat_dex_file->GetOatClass(class_def_idx);
-  return true;
+  *found = true;
+  return oat_dex_file->GetOatClass(class_def_idx);
 }
 
 static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx,
@@ -2362,8 +2469,7 @@
   return 0;
 }
 
-bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method) {
-  DCHECK(oat_method != nullptr);
+const OatFile::OatMethod ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, bool* found) {
   // Although we overwrite the trampoline of non-static methods, we may get here via the resolution
   // method for direct methods (or virtual methods made direct).
   mirror::Class* declaring_class = method->GetDeclaringClass();
@@ -2392,15 +2498,14 @@
             GetOatMethodIndexFromMethodIndex(*declaring_class->GetDexCache()->GetDexFile(),
                                              method->GetDeclaringClass()->GetDexClassDefIndex(),
                                              method->GetDexMethodIndex()));
-  OatFile::OatClass oat_class;
-  if (!FindOatClass(*declaring_class->GetDexCache()->GetDexFile(),
-                    declaring_class->GetDexClassDefIndex(),
-                    &oat_class)) {
-    return false;
+  OatFile::OatClass oat_class = FindOatClass(*declaring_class->GetDexCache()->GetDexFile(),
+                                             declaring_class->GetDexClassDefIndex(),
+                                             found);
+  if (!found) {
+    return OatFile::OatMethod::Invalid();
   }
-
-  *oat_method = oat_class.GetOatMethod(oat_method_index);
-  return true;
+  *found = true;
+  return oat_class.GetOatMethod(oat_method_index);
 }
 
 // Special case to get oat code without overwriting a trampoline.
@@ -2409,9 +2514,10 @@
   if (method->IsProxyMethod()) {
     return GetQuickProxyInvokeHandler();
   }
-  OatFile::OatMethod oat_method;
+  bool found;
+  OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
   const void* result = nullptr;
-  if (FindOatMethodFor(method, &oat_method)) {
+  if (found) {
     result = oat_method.GetQuickCode();
   }
 
@@ -2419,11 +2525,9 @@
     if (method->IsNative()) {
       // No code and native? Use generic trampoline.
       result = GetQuickGenericJniTrampoline();
-#if defined(ART_USE_PORTABLE_COMPILER)
     } else if (method->IsPortableCompiled()) {
       // No code? Do we expect portable code?
       result = GetQuickToPortableBridge();
-#endif
     } else {
       // No code? You must mean to go into the interpreter.
       result = GetQuickToInterpreterBridge();
@@ -2432,7 +2536,6 @@
   return result;
 }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
 const void* ClassLinker::GetPortableOatCodeFor(mirror::ArtMethod* method,
                                                bool* have_portable_code) {
   CHECK(!method->IsAbstract()) << PrettyMethod(method);
@@ -2440,10 +2543,11 @@
   if (method->IsProxyMethod()) {
     return GetPortableProxyInvokeHandler();
   }
-  OatFile::OatMethod oat_method;
+  bool found;
+  OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
   const void* result = nullptr;
   const void* quick_code = nullptr;
-  if (FindOatMethodFor(method, &oat_method)) {
+  if (found) {
     result = oat_method.GetPortableCode();
     quick_code = oat_method.GetQuickCode();
   }
@@ -2461,29 +2565,46 @@
   }
   return result;
 }
-#endif
+
+const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) {
+  if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
+    return nullptr;
+  }
+  bool found;
+  OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
+  return found ? oat_method.GetQuickCode() : nullptr;
+}
+
+const void* ClassLinker::GetOatMethodPortableCodeFor(mirror::ArtMethod* method) {
+  if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
+    return nullptr;
+  }
+  bool found;
+  OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
+  return found ? oat_method.GetPortableCode() : nullptr;
+}
 
 const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
                                             uint32_t method_idx) {
-  OatFile::OatClass oat_class;
-  if (!FindOatClass(dex_file, class_def_idx, &oat_class)) {
+  bool found;
+  OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
+  if (!found) {
     return nullptr;
   }
   uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
   return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
 }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
 const void* ClassLinker::GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
                                                uint32_t method_idx) {
-  OatFile::OatClass oat_class;
-  if (!FindOatClass(dex_file, class_def_idx, &oat_class)) {
+  bool found;
+  OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
+  if (!found) {
     return nullptr;
   }
   uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
   return oat_class.GetOatMethod(oat_method_idx).GetPortableCode();
 }
-#endif
 
 // Returns true if the method must run with interpreter, false otherwise.
 static bool NeedsInterpreter(
@@ -2534,8 +2655,9 @@
   while (it.HasNextInstanceField()) {
     it.Next();
   }
-  OatFile::OatClass oat_class;
-  bool has_oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class);
+  bool has_oat_class;
+  OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
+                                             &has_oat_class);
   // Link the code of methods skipped by LinkCode.
   for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) {
     mirror::ArtMethod* method = klass->GetDirectMethod(method_index);
@@ -2557,17 +2679,12 @@
       // Check whether the method is native, in which case it's generic JNI.
       if (quick_code == nullptr && portable_code == nullptr && method->IsNative()) {
         quick_code = GetQuickGenericJniTrampoline();
-#if defined(ART_USE_PORTABLE_COMPILER)
         portable_code = GetPortableToQuickBridge();
-#endif
       } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
         portable_code = GetPortableToInterpreterBridge();
-#endif
         quick_code = GetQuickToInterpreterBridge();
       }
     } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
       if (portable_code == nullptr) {
         portable_code = GetPortableToQuickBridge();
       } else {
@@ -2576,11 +2693,6 @@
       if (quick_code == nullptr) {
         quick_code = GetQuickToPortableBridge();
       }
-#else
-      if (quick_code == nullptr) {
-        quick_code = GetQuickToInterpreterBridge();
-      }
-#endif
     }
     runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code, portable_code,
                                                      have_portable_code);
@@ -2588,7 +2700,8 @@
   // Ignore virtual methods on the iterator.
 }
 
-void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
+void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method,
+                           const OatFile::OatClass* oat_class,
                            const DexFile& dex_file, uint32_t dex_method_index,
                            uint32_t method_index) {
   if (Runtime::Current()->IsCompiler()) {
@@ -2597,9 +2710,7 @@
   }
   // Method shouldn't have already been linked.
   DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
-#if defined(ART_USE_PORTABLE_COMPILER)
   DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
-#endif
   if (oat_class != nullptr) {
     // Every kind of method should at least get an invoke stub from the oat_method.
     // non-abstract methods also get their code pointers.
@@ -2610,11 +2721,7 @@
   // Install entry point from interpreter.
   bool enter_interpreter = NeedsInterpreter(method.Get(),
                                             method->GetEntryPointFromQuickCompiledCode(),
-#if defined(ART_USE_PORTABLE_COMPILER)
                                             method->GetEntryPointFromPortableCompiledCode());
-#else
-                                            nullptr);
-#endif
   if (enter_interpreter && !method->IsNative()) {
     method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
   } else {
@@ -2623,9 +2730,7 @@
 
   if (method->IsAbstract()) {
     method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
     return;
   }
 
@@ -2635,33 +2740,23 @@
     // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
     // after initializing class (see ClassLinker::InitializeClass method).
     method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline());
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline());
-#endif
   } else if (enter_interpreter) {
     if (!method->IsNative()) {
       // Set entry point from compiled code if there's no code or in interpreter only mode.
       method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
       method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
     } else {
       method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline());
-#if defined(ART_USE_PORTABLE_COMPILER)
       method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
-#endif
     }
-#if defined(ART_USE_PORTABLE_COMPILER)
   } else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {
     DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
     have_portable_code = true;
     method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
-#endif
   } else {
     DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
-#endif
   }
 
   if (method->IsNative()) {
@@ -2680,15 +2775,13 @@
   Runtime* runtime = Runtime::Current();
   runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
                                                    method->GetEntryPointFromQuickCompiledCode(),
-#if defined(ART_USE_PORTABLE_COMPILER)
                                                    method->GetEntryPointFromPortableCompiledCode(),
-#else
-                                                   nullptr,
-#endif
                                                    have_portable_code);
 }
 
-void ClassLinker::LoadClass(const DexFile& dex_file,
+
+
+void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file,
                             const DexFile::ClassDef& dex_class_def,
                             Handle<mirror::Class> klass,
                             mirror::ClassLoader* class_loader) {
@@ -2717,24 +2810,27 @@
     return;  // no fields or methods - for example a marker interface
   }
 
-  OatFile::OatClass oat_class;
-  if (Runtime::Current()->IsStarted()
-      && !Runtime::Current()->UseCompileTimeClassPath()
-      && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
-    LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);
-  } else {
-    LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
+
+  bool has_oat_class = false;
+  if (Runtime::Current()->IsStarted() && !Runtime::Current()->UseCompileTimeClassPath()) {
+    OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
+                                               &has_oat_class);
+    if (has_oat_class) {
+      LoadClassMembers(self, dex_file, class_data, klass, class_loader, &oat_class);
+    }
+  }
+  if (!has_oat_class) {
+    LoadClassMembers(self, dex_file, class_data, klass, class_loader, nullptr);
   }
 }
 
-void ClassLinker::LoadClassMembers(const DexFile& dex_file,
+void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
                                    const byte* class_data,
                                    Handle<mirror::Class> klass,
                                    mirror::ClassLoader* class_loader,
                                    const OatFile::OatClass* oat_class) {
   // Load fields.
   ClassDataItemIterator it(dex_file, class_data);
-  Thread* self = Thread::Current();
   if (it.NumStaticFields() != 0) {
     mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields());
     if (UNLIKELY(statics == nullptr)) {
@@ -2753,6 +2849,7 @@
     klass->SetIFields(fields);
   }
   for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
+    self->AllowThreadSuspension();
     StackHandleScope<1> hs(self);
     Handle<mirror::ArtField> sfield(hs.NewHandle(AllocArtField(self)));
     if (UNLIKELY(sfield.Get() == nullptr)) {
@@ -2763,6 +2860,7 @@
     LoadField(dex_file, it, klass, sfield);
   }
   for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
+    self->AllowThreadSuspension();
     StackHandleScope<1> hs(self);
     Handle<mirror::ArtField> ifield(hs.NewHandle(AllocArtField(self)));
     if (UNLIKELY(ifield.Get() == nullptr)) {
@@ -2798,6 +2896,7 @@
   uint32_t last_dex_method_index = DexFile::kDexNoIndex;
   size_t last_class_def_method_index = 0;
   for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
+    self->AllowThreadSuspension();
     StackHandleScope<1> hs(self);
     Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
     if (UNLIKELY(method.Get() == nullptr)) {
@@ -2818,6 +2917,7 @@
     class_def_method_index++;
   }
   for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
+    self->AllowThreadSuspension();
     StackHandleScope<1> hs(self);
     Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
     if (UNLIKELY(method.Get() == nullptr)) {
@@ -2833,7 +2933,8 @@
 }
 
 void ClassLinker::LoadField(const DexFile& /*dex_file*/, const ClassDataItemIterator& it,
-                            Handle<mirror::Class> klass, Handle<mirror::ArtField> dst) {
+                            Handle<mirror::Class> klass,
+                            Handle<mirror::ArtField> dst) {
   uint32_t field_idx = it.GetMemberIndex();
   dst->SetDexFieldIndex(field_idx);
   dst->SetDeclaringClass(klass.Get());
@@ -2854,7 +2955,7 @@
   }
   DCHECK(dst->IsArtMethod()) << PrettyDescriptor(dst->GetClass());
 
-  const char* old_cause = self->StartAssertNoThreadSuspension("LoadMethod");
+  ScopedAssertNoThreadSuspension ants(self, "LoadMethod");
   dst->SetDexMethodIndex(dex_method_idx);
   dst->SetDeclaringClass(klass.Get());
   dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
@@ -2901,12 +3002,10 @@
   }
   dst->SetAccessFlags(access_flags);
 
-  self->EndAssertNoThreadSuspension(old_cause);
   return dst;
 }
 
-void ClassLinker::AppendToBootClassPath(const DexFile& dex_file) {
-  Thread* self = Thread::Current();
+void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile& dex_file) {
   StackHandleScope<1> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));
   CHECK(dex_cache.Get() != nullptr) << "Failed to allocate dex cache for "
@@ -3058,11 +3157,12 @@
   // Identify the underlying component type
   CHECK_EQ('[', descriptor[0]);
   StackHandleScope<2> hs(self);
-  Handle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1, class_loader)));
+  MutableHandle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1,
+                                                                     class_loader)));
   if (component_type.Get() == nullptr) {
     DCHECK(self->IsExceptionPending());
     // We need to accept erroneous classes as component types.
-    component_type.Assign(LookupClass(descriptor + 1, class_loader.Get()));
+    component_type.Assign(LookupClass(self, descriptor + 1, class_loader.Get()));
     if (component_type.Get() == nullptr) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
@@ -3092,7 +3192,7 @@
   // class to the hash table --- necessary because of possible races with
   // other threads.)
   if (class_loader.Get() != component_type->GetClassLoader()) {
-    mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader());
+    mirror::Class* new_class = LookupClass(self, descriptor, component_type->GetClassLoader());
     if (new_class != nullptr) {
       return new_class;
     }
@@ -3311,11 +3411,11 @@
   return false;
 }
 
-mirror::Class* ClassLinker::LookupClass(const char* descriptor,
+mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor,
                                         const mirror::ClassLoader* class_loader) {
   size_t hash = Hash(descriptor);
   {
-    ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+    ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
     mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
     if (result != nullptr) {
       return result;
@@ -3378,8 +3478,7 @@
   if (!dex_cache_image_class_lookup_required_) {
     return;  // All dex cache classes are already in the class table.
   }
-  const char* old_no_suspend_cause =
-      self->StartAssertNoThreadSuspension("Moving image classes to class table");
+  ScopedAssertNoThreadSuspension ants(self, "Moving image classes to class table");
   mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
   std::string temp;
   for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
@@ -3405,13 +3504,10 @@
     }
   }
   dex_cache_image_class_lookup_required_ = false;
-  self->EndAssertNoThreadSuspension(old_no_suspend_cause);
 }
 
 mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
-  Thread* self = Thread::Current();
-  const char* old_no_suspend_cause =
-      self->StartAssertNoThreadSuspension("Image class lookup");
+  ScopedAssertNoThreadSuspension ants(Thread::Current(), "Image class lookup");
   mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
   for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
     mirror::DexCache* dex_cache = dex_caches->Get(i);
@@ -3425,13 +3521,11 @@
         uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
         mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
         if (klass != nullptr) {
-          self->EndAssertNoThreadSuspension(old_no_suspend_cause);
           return klass;
         }
       }
     }
   }
-  self->EndAssertNoThreadSuspension(old_no_suspend_cause);
   return nullptr;
 }
 
@@ -3451,9 +3545,8 @@
   }
 }
 
-void ClassLinker::VerifyClass(Handle<mirror::Class> klass) {
+void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
   // TODO: assert that the monitor on the Class is held
-  Thread* self = Thread::Current();
   ObjectLock<mirror::Class> lock(self, klass);
 
   // Don't attempt to re-verify if already sufficiently verified.
@@ -3496,7 +3589,7 @@
     ObjectLock<mirror::Class> lock(self, super);
 
     if (!super->IsVerified() && !super->IsErroneous()) {
-      VerifyClass(super);
+      VerifyClass(self, super);
     }
     if (!super->IsCompileTimeVerified()) {
       std::string error_msg(
@@ -3537,7 +3630,7 @@
   verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
   std::string error_msg;
   if (!preverified) {
-    verifier_failure = verifier::MethodVerifier::VerifyClass(klass.Get(),
+    verifier_failure = verifier::MethodVerifier::VerifyClass(self, klass.Get(),
                                                              Runtime::Current()->IsCompiler(),
                                                              &error_msg);
   }
@@ -3572,7 +3665,7 @@
         klass->SetStatus(mirror::Class::kStatusVerified, self);
         // As this is a fake verified status, make sure the methods are _not_ marked preverified
         // later.
-        klass->SetAccessFlags(klass->GetAccessFlags() | kAccPreverified);
+        klass->SetPreverified();
       }
     }
   } else {
@@ -3595,9 +3688,9 @@
 }
 
 void ClassLinker::EnsurePreverifiedMethods(Handle<mirror::Class> klass) {
-  if ((klass->GetAccessFlags() & kAccPreverified) == 0) {
+  if (!klass->IsPreverified()) {
     klass->SetPreverifiedFlagOnAllMethods();
-    klass->SetAccessFlags(klass->GetAccessFlags() | kAccPreverified);
+    klass->SetPreverified();
   }
 }
 
@@ -3721,7 +3814,7 @@
                                              jobjectArray methods, jobjectArray throws) {
   Thread* self = soa.Self();
   StackHandleScope<8> hs(self);
-  Handle<mirror::Class> klass(hs.NewHandle(
+  MutableHandle<mirror::Class> klass(hs.NewHandle(
       AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class))));
   if (klass.Get() == nullptr) {
     CHECK(self->IsExceptionPending());  // OOME.
@@ -3965,15 +4058,14 @@
   // At runtime the method looks like a reference and argument saving method, clone the code
   // related parameters from this method.
   method->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
-#if defined(ART_USE_PORTABLE_COMPILER)
   method->SetEntryPointFromPortableCompiledCode(GetPortableProxyInvokeHandler());
-#endif
   method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
 
   return method;
 }
 
-static void CheckProxyMethod(Handle<mirror::ArtMethod> method, Handle<mirror::ArtMethod> prototype)
+static void CheckProxyMethod(Handle<mirror::ArtMethod> method,
+                             Handle<mirror::ArtMethod> prototype)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Basic sanity
   CHECK(!prototype->IsFinal());
@@ -3987,8 +4079,9 @@
   CHECK(prototype->HasSameDexCacheResolvedTypes(method.Get()));
   CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
 
-  MethodHelper mh(method);
-  MethodHelper mh2(prototype);
+  StackHandleScope<2> hs(Thread::Current());
+  MethodHelper mh(hs.NewHandle(method.Get()));
+  MethodHelper mh2(hs.NewHandle(prototype.Get()));
   CHECK_STREQ(method->GetName(), prototype->GetName());
   CHECK_STREQ(method->GetShorty(), prototype->GetShorty());
   // More complex sanity - via dex cache
@@ -4029,12 +4122,8 @@
   return true;
 }
 
-bool ClassLinker::IsInitialized() const {
-  return init_done_;
-}
-
-bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_statics,
-                                  bool can_init_parents) {
+bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
+                                  bool can_init_statics, bool can_init_parents) {
   // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol
 
   // Are we already initialized and therefore done?
@@ -4049,7 +4138,7 @@
     return false;
   }
 
-  Thread* self = Thread::Current();
+  self->AllowThreadSuspension();
   uint64_t t0;
   {
     ObjectLock<mirror::Class> lock(self, klass);
@@ -4068,7 +4157,7 @@
     CHECK(klass->IsResolved()) << PrettyClass(klass.Get()) << ": state=" << klass->GetStatus();
 
     if (!klass->IsVerified()) {
-      VerifyClass(klass);
+      VerifyClass(self, klass);
       if (!klass->IsVerified()) {
         // We failed to verify, expect either the klass to be erroneous or verification failed at
         // compile time.
@@ -4107,6 +4196,7 @@
       klass->SetStatus(mirror::Class::kStatusError, self);
       return false;
     }
+    self->AllowThreadSuspension();
 
     CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass.Get());
 
@@ -4126,7 +4216,7 @@
       CHECK(can_init_parents);
       StackHandleScope<1> hs(self);
       Handle<mirror::Class> handle_scope_super(hs.NewHandle(super_class));
-      bool super_initialized = InitializeClass(handle_scope_super, can_init_statics, true);
+      bool super_initialized = InitializeClass(self, handle_scope_super, can_init_statics, true);
       if (!super_initialized) {
         // The super class was verified ahead of entering initializing, we should only be here if
         // the super class became erroneous due to initialization.
@@ -4148,23 +4238,26 @@
     const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
     CHECK(dex_class_def != nullptr);
     const DexFile& dex_file = klass->GetDexFile();
-    StackHandleScope<2> hs(self);
+    StackHandleScope<3> hs(self);
     Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
-    EncodedStaticFieldValueIterator it(dex_file, &dex_cache, &class_loader,
-                                       this, *dex_class_def);
-    if (it.HasNext()) {
+    EncodedStaticFieldValueIterator value_it(dex_file, &dex_cache, &class_loader,
+                                             this, *dex_class_def);
+    const byte* class_data = dex_file.GetClassData(*dex_class_def);
+    ClassDataItemIterator field_it(dex_file, class_data);
+    if (value_it.HasNext()) {
+      DCHECK(field_it.HasNextStaticField());
       CHECK(can_init_statics);
-      // We reordered the fields, so we need to be able to map the
-      // field indexes to the right fields.
-      SafeMap<uint32_t, mirror::ArtField*> field_map;
-      ConstructFieldMap(dex_file, *dex_class_def, klass.Get(), field_map);
-      for (size_t i = 0; it.HasNext(); i++, it.Next()) {
+      for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) {
+        StackHandleScope<1> hs(self);
+        Handle<mirror::ArtField> field(hs.NewHandle(
+            ResolveField(dex_file, field_it.GetMemberIndex(), dex_cache, class_loader, true)));
         if (Runtime::Current()->IsActiveTransaction()) {
-          it.ReadValueToField<true>(field_map.Get(i));
+          value_it.ReadValueToField<true>(field);
         } else {
-          it.ReadValueToField<false>(field_map.Get(i));
+          value_it.ReadValueToField<false>(field);
         }
+        DCHECK(!value_it.HasNext() || field_it.HasNextStaticField());
       }
     }
   }
@@ -4176,6 +4269,7 @@
     clinit->Invoke(self, nullptr, 0, &result, "V");
   }
 
+  self->AllowThreadSuspension();
   uint64_t t1 = NanoTime();
 
   bool success = true;
@@ -4244,7 +4338,7 @@
     LOG(FATAL) << "Unexpected class status. " << PrettyClass(klass.Get()) << " is "
         << klass->GetStatus();
   }
-  LOG(FATAL) << "Not Reached" << PrettyClass(klass.Get());
+  UNREACHABLE();
 }
 
 bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) {
@@ -4253,8 +4347,8 @@
   }
   // Begin with the methods local to the superclass.
   StackHandleScope<2> hs(Thread::Current());
-  MethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-  MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+  MutableMethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+  MutableMethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
   if (klass->HasSuperClass() &&
       klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) {
     for (int i = klass->GetSuperClass()->GetVTableLength() - 1; i >= 0; --i) {
@@ -4292,15 +4386,14 @@
   return true;
 }
 
-bool ClassLinker::EnsureInitialized(Handle<mirror::Class> c, bool can_init_fields,
+bool ClassLinker::EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields,
                                     bool can_init_parents) {
   DCHECK(c.Get() != nullptr);
   if (c->IsInitialized()) {
     EnsurePreverifiedMethods(c);
     return true;
   }
-  const bool success = InitializeClass(c, can_init_fields, can_init_parents);
-  Thread* self = Thread::Current();
+  const bool success = InitializeClass(self, c, can_init_fields, can_init_parents);
   if (!success) {
     if (can_init_fields && can_init_parents) {
       CHECK(self->IsExceptionPending()) << PrettyClass(c.Get());
@@ -4311,20 +4404,6 @@
   return success;
 }
 
-void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
-                                    mirror::Class* c,
-                                    SafeMap<uint32_t, mirror::ArtField*>& field_map) {
-  const byte* class_data = dex_file.GetClassData(dex_class_def);
-  ClassDataItemIterator it(dex_file, class_data);
-  StackHandleScope<2> hs(Thread::Current());
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(c->GetDexCache()));
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(c->GetClassLoader()));
-  CHECK(!kMovingFields);
-  for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
-    field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true));
-  }
-}
-
 void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) {
   mirror::ObjectArray<mirror::ArtField>* fields = new_class->GetIFields();
   if (fields != nullptr) {
@@ -4374,15 +4453,14 @@
   if (!LinkMethods(self, klass, interfaces)) {
     return false;
   }
-  if (!LinkInstanceFields(klass)) {
+  if (!LinkInstanceFields(self, klass)) {
     return false;
   }
   size_t class_size;
-  if (!LinkStaticFields(klass, &class_size)) {
+  if (!LinkStaticFields(self, klass, &class_size)) {
     return false;
   }
   CreateReferenceInstanceOffsets(klass);
-  CreateReferenceStaticOffsets(klass);
   CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
 
   if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) {
@@ -4536,6 +4614,7 @@
 // Populate the class vtable and itable. Compute return type indices.
 bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
                               Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
+  self->AllowThreadSuspension();
   if (klass->IsInterface()) {
     // No vtable.
     size_t count = klass->NumVirtualMethods();
@@ -4563,7 +4642,7 @@
     CHECK_LE(actual_count, max_count);
     StackHandleScope<4> hs(self);
     Handle<mirror::Class> super_class(hs.NewHandle(klass->GetSuperClass()));
-    Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable;
+    MutableHandle<mirror::ObjectArray<mirror::ArtMethod>> vtable;
     if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
       vtable = hs.NewHandle(AllocArtMethodArray(self, max_count));
       if (UNLIKELY(vtable.Get() == nullptr)) {
@@ -4584,8 +4663,8 @@
     }
 
     // See if any of our virtual methods override the superclass.
-    MethodHelper local_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-    MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+    MutableMethodHelper local_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+    MutableMethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
     for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
       mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i);
       local_mh.ChangeMethod(local_method);
@@ -4700,8 +4779,8 @@
       return true;
     }
   }
-  StackHandleScope<4> hs(self);
-  Handle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
+  StackHandleScope<5> hs(self);
+  MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
   if (UNLIKELY(iftable.Get() == nullptr)) {
     CHECK(self->IsExceptionPending());  // OOME.
     return false;
@@ -4713,6 +4792,7 @@
       iftable->SetInterface(i, super_interface);
     }
   }
+  self->AllowThreadSuspension();
   // Flatten the interface inheritance hierarchy.
   size_t idx = super_ifcount;
   for (size_t i = 0; i < num_interfaces; i++) {
@@ -4756,6 +4836,7 @@
       }
     }
   }
+  self->AllowThreadSuspension();
   // Shrink iftable in case duplicates were found
   if (idx < ifcount) {
     iftable.Assign(down_cast<mirror::IfTable*>(iftable->CopyOf(self, idx * mirror::IfTable::kMax)));
@@ -4773,6 +4854,7 @@
   if (klass->IsInterface()) {
     return true;
   }
+  self->AllowThreadSuspension();
   // Allocate imtable
   bool imtable_changed = false;
   Handle<mirror::ObjectArray<mirror::ArtMethod>> imtable(
@@ -4781,10 +4863,17 @@
     CHECK(self->IsExceptionPending());  // OOME.
     return false;
   }
-  MethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-  MethodHelper vtable_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-  std::vector<mirror::ArtMethod*> miranda_list;
+  MutableMethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+  MutableMethodHelper vtable_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+  size_t max_miranda_methods = 0;  // The max size of miranda_list.
   for (size_t i = 0; i < ifcount; ++i) {
+    max_miranda_methods += iftable->GetInterface(i)->NumVirtualMethods();
+  }
+  Handle<mirror::ObjectArray<mirror::ArtMethod>>
+      miranda_list(hs.NewHandle(AllocArtMethodArray(self, max_miranda_methods)));
+  size_t miranda_list_size = 0;  // The current size of miranda_list.
+  for (size_t i = 0; i < ifcount; ++i) {
+    self->AllowThreadSuspension();
     size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
     if (num_methods > 0) {
       StackHandleScope<2> hs(self);
@@ -4798,8 +4887,7 @@
       Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
           hs.NewHandle(klass->GetVTableDuringLinking()));
       for (size_t j = 0; j < num_methods; ++j) {
-        mirror::ArtMethod* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j);
-        interface_mh.ChangeMethod(interface_method);
+        interface_mh.ChangeMethod(iftable->GetInterface(i)->GetVirtualMethod(j));
         int32_t k;
         // For each method listed in the interface's method list, find the
         // matching method in our class's method list.  We want to favor the
@@ -4810,22 +4898,21 @@
         // those don't end up in the virtual method table, so it shouldn't
         // matter which direction we go.  We walk it backward anyway.)
         for (k = vtable->GetLength() - 1; k >= 0; --k) {
-          mirror::ArtMethod* vtable_method = vtable->Get(k);
-          vtable_mh.ChangeMethod(vtable_method);
+          vtable_mh.ChangeMethod(vtable->Get(k));
           if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
-            if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
+            if (!vtable_mh.Get()->IsAbstract() && !vtable_mh.Get()->IsPublic()) {
               ThrowIllegalAccessError(
                   klass.Get(),
                   "Method '%s' implementing interface method '%s' is not public",
-                  PrettyMethod(vtable_method).c_str(),
-                  PrettyMethod(interface_method).c_str());
+                  PrettyMethod(vtable_mh.Get()).c_str(),
+                  PrettyMethod(interface_mh.Get()).c_str());
               return false;
             }
-            method_array->Set<false>(j, vtable_method);
+            method_array->Set<false>(j, vtable_mh.Get());
             // Place method in imt if entry is empty, place conflict otherwise.
-            uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+            uint32_t imt_index = interface_mh.Get()->GetDexMethodIndex() % mirror::Class::kImtSize;
             if (imtable->Get(imt_index) == nullptr) {
-              imtable->Set<false>(imt_index, vtable_method);
+              imtable->Set<false>(imt_index, vtable_mh.Get());
               imtable_changed = true;
             } else {
               imtable->Set<false>(imt_index, runtime->GetImtConflictMethod());
@@ -4836,7 +4923,9 @@
         if (k < 0) {
           StackHandleScope<1> hs(self);
           auto miranda_method = hs.NewHandle<mirror::ArtMethod>(nullptr);
-          for (mirror::ArtMethod* mir_method : miranda_list) {
+          for (size_t l = 0; l < miranda_list_size; ++l) {
+            mirror::ArtMethod* mir_method = miranda_list->Get(l);
+            DCHECK(mir_method != nullptr);
             vtable_mh.ChangeMethod(mir_method);
             if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
               miranda_method.Assign(mir_method);
@@ -4845,13 +4934,13 @@
           }
           if (miranda_method.Get() == nullptr) {
             // Point the interface table at a phantom slot.
-            miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_method->Clone(self)));
+            miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_mh.Get()->Clone(self)));
             if (UNLIKELY(miranda_method.Get() == nullptr)) {
               CHECK(self->IsExceptionPending());  // OOME.
               return false;
             }
-            // TODO: If a methods move then the miranda_list may hold stale references.
-            miranda_list.push_back(miranda_method.Get());
+            DCHECK_LT(miranda_list_size, max_miranda_methods);
+            miranda_list->Set<false>(miranda_list_size++, miranda_method.Get());
           }
           method_array->Set<false>(j, miranda_method.Get());
         }
@@ -4868,9 +4957,9 @@
     }
     klass->SetImTable(imtable.Get());
   }
-  if (!miranda_list.empty()) {
+  if (miranda_list_size > 0) {
     int old_method_count = klass->NumVirtualMethods();
-    int new_method_count = old_method_count + miranda_list.size();
+    int new_method_count = old_method_count + miranda_list_size;
     mirror::ObjectArray<mirror::ArtMethod>* virtuals;
     if (old_method_count == 0) {
       virtuals = AllocArtMethodArray(self, new_method_count);
@@ -4884,18 +4973,18 @@
     klass->SetVirtualMethods(virtuals);
 
     StackHandleScope<1> hs(self);
-    Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
+    MutableHandle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
         hs.NewHandle(klass->GetVTableDuringLinking()));
     CHECK(vtable.Get() != nullptr);
     int old_vtable_count = vtable->GetLength();
-    int new_vtable_count = old_vtable_count + miranda_list.size();
+    int new_vtable_count = old_vtable_count + miranda_list_size;
     vtable.Assign(vtable->CopyOf(self, new_vtable_count));
     if (UNLIKELY(vtable.Get() == nullptr)) {
       CHECK(self->IsExceptionPending());  // OOME.
       return false;
     }
-    for (size_t i = 0; i < miranda_list.size(); ++i) {
-      mirror::ArtMethod* method = miranda_list[i];
+    for (size_t i = 0; i < miranda_list_size; ++i) {
+      mirror::ArtMethod* method = miranda_list->Get(i);
       // Leave the declaring class alone as type indices are relative to it
       method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
       method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
@@ -4911,19 +5000,19 @@
     CHECK(vtable->Get(i) != nullptr);
   }
 
-//  klass->DumpClass(std::cerr, Class::kDumpClassFullDetail);
+  self->AllowThreadSuspension();
 
   return true;
 }
 
-bool ClassLinker::LinkInstanceFields(Handle<mirror::Class> klass) {
+bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) {
   CHECK(klass.Get() != nullptr);
-  return LinkFields(klass, false, nullptr);
+  return LinkFields(self, klass, false, nullptr);
 }
 
-bool ClassLinker::LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size) {
+bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) {
   CHECK(klass.Get() != nullptr);
-  return LinkFields(klass, true, class_size);
+  return LinkFields(self, klass, true, class_size);
 }
 
 struct LinkFieldsComparator {
@@ -4932,20 +5021,20 @@
   // No thread safety analysis as will be called from STL. Checked lock held in constructor.
   bool operator()(mirror::ArtField* field1, mirror::ArtField* field2)
       NO_THREAD_SAFETY_ANALYSIS {
-    // First come reference fields, then 64-bit, and finally 32-bit
+    // First come reference fields, then 64-bit, then 32-bit, and then 16-bit, then finally 8-bit.
     Primitive::Type type1 = field1->GetTypeAsPrimitiveType();
     Primitive::Type type2 = field2->GetTypeAsPrimitiveType();
     if (type1 != type2) {
       bool is_primitive1 = type1 != Primitive::kPrimNot;
       bool is_primitive2 = type2 != Primitive::kPrimNot;
-      bool is64bit1 = is_primitive1 && (type1 == Primitive::kPrimLong ||
-                                        type1 == Primitive::kPrimDouble);
-      bool is64bit2 = is_primitive2 && (type2 == Primitive::kPrimLong ||
-                                        type2 == Primitive::kPrimDouble);
-      int order1 = !is_primitive1 ? 0 : (is64bit1 ? 1 : 2);
-      int order2 = !is_primitive2 ? 0 : (is64bit2 ? 1 : 2);
-      if (order1 != order2) {
-        return order1 < order2;
+      if (type1 != type2) {
+        if (is_primitive1 && is_primitive2) {
+          // Larger primitive types go first.
+          return Primitive::ComponentSize(type1) > Primitive::ComponentSize(type2);
+        } else {
+          // Reference always goes first.
+          return !is_primitive1;
+        }
       }
     }
     // same basic group? then sort by string.
@@ -4953,7 +5042,9 @@
   }
 };
 
-bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size) {
+bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static,
+                             size_t* class_size) {
+  self->AllowThreadSuspension();
   size_t num_fields =
       is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
 
@@ -4967,7 +5058,7 @@
     if (klass->ShouldHaveEmbeddedImtAndVTable()) {
       // Static fields come after the embedded tables.
       base = mirror::Class::ComputeClassSize(true, klass->GetVTableDuringLinking()->GetLength(),
-                                             0, 0, 0);
+                                             0, 0, 0, 0, 0);
     }
     field_offset = MemberOffset(base);
   } else {
@@ -4984,6 +5075,8 @@
   // we want a relatively stable order so that adding new fields
   // minimizes disruption of C++ version such as Class and Method.
   std::deque<mirror::ArtField*> grouped_and_sorted_fields;
+  const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension(
+      "Naked ArtField references in deque");
   for (size_t i = 0; i < num_fields; i++) {
     mirror::ArtField* f = fields->Get(i);
     CHECK(f != nullptr) << PrettyClass(klass.Get());
@@ -4995,6 +5088,8 @@
   // References should be at the front.
   size_t current_field = 0;
   size_t num_reference_fields = 0;
+  FieldGaps gaps;
+
   for (; current_field < num_fields; current_field++) {
     mirror::ArtField* field = grouped_and_sorted_fields.front();
     Primitive::Type type = field->GetTypeAsPrimitiveType();
@@ -5002,51 +5097,31 @@
     if (isPrimitive) {
       break;  // past last reference, move on to the next phase
     }
+    if (UNLIKELY(!IsAligned<4>(field_offset.Uint32Value()))) {
+      MemberOffset old_offset = field_offset;
+      field_offset = MemberOffset(RoundUp(field_offset.Uint32Value(), 4));
+      AddFieldGap(old_offset.Uint32Value(), field_offset.Uint32Value(), &gaps);
+    }
+    DCHECK(IsAligned<4>(field_offset.Uint32Value()));
     grouped_and_sorted_fields.pop_front();
     num_reference_fields++;
     fields->Set<false>(current_field, field);
     field->SetOffset(field_offset);
     field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
   }
-
-  // Now we want to pack all of the double-wide fields together.  If
-  // we're not aligned, though, we want to shuffle one 32-bit field
-  // into place.  If we can't find one, we'll have to pad it.
-  if (current_field != num_fields && !IsAligned<8>(field_offset.Uint32Value())) {
-    for (size_t i = 0; i < grouped_and_sorted_fields.size(); i++) {
-      mirror::ArtField* field = grouped_and_sorted_fields[i];
-      Primitive::Type type = field->GetTypeAsPrimitiveType();
-      CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
-      if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) {
-        continue;
-      }
-      fields->Set<false>(current_field++, field);
-      field->SetOffset(field_offset);
-      // drop the consumed field
-      grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i);
-      break;
-    }
-    // whether we found a 32-bit field for padding or not, we advance
-    field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
-  }
-
-  // Alignment is good, shuffle any double-wide fields forward, and
-  // finish assigning field offsets to all fields.
-  DCHECK(current_field == num_fields || IsAligned<8>(field_offset.Uint32Value()))
-      << PrettyClass(klass.Get());
-  while (!grouped_and_sorted_fields.empty()) {
-    mirror::ArtField* field = grouped_and_sorted_fields.front();
-    grouped_and_sorted_fields.pop_front();
-    Primitive::Type type = field->GetTypeAsPrimitiveType();
-    CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
-    fields->Set<false>(current_field, field);
-    field->SetOffset(field_offset);
-    field_offset = MemberOffset(field_offset.Uint32Value() +
-                                ((type == Primitive::kPrimLong || type == Primitive::kPrimDouble)
-                                 ? sizeof(uint64_t)
-                                 : sizeof(uint32_t)));
-    current_field++;
-  }
+  // Gaps are stored as a max heap which means that we must shuffle from largest to smallest
+  // otherwise we could end up with suboptimal gap fills.
+  ShuffleForward<8>(num_fields, &current_field, &field_offset,
+                    fields, &grouped_and_sorted_fields, &gaps);
+  ShuffleForward<4>(num_fields, &current_field, &field_offset,
+                    fields, &grouped_and_sorted_fields, &gaps);
+  ShuffleForward<2>(num_fields, &current_field, &field_offset,
+                    fields, &grouped_and_sorted_fields, &gaps);
+  ShuffleForward<1>(num_fields, &current_field, &field_offset,
+                    fields, &grouped_and_sorted_fields, &gaps);
+  CHECK(grouped_and_sorted_fields.empty()) << "Missed " << grouped_and_sorted_fields.size() <<
+      " fields.";
+  self->EndAssertNoThreadSuspension(old_no_suspend_cause);
 
   // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it.
   if (!is_static && klass->DescriptorEquals("Ljava/lang/ref/Reference;")) {
@@ -5124,20 +5199,13 @@
       return;
     }
   }
-  CreateReferenceOffsets(klass, false, reference_offsets);
+  CreateReferenceOffsets(klass, reference_offsets);
 }
 
-void ClassLinker::CreateReferenceStaticOffsets(Handle<mirror::Class> klass) {
-  CreateReferenceOffsets(klass, true, 0);
-}
-
-void ClassLinker::CreateReferenceOffsets(Handle<mirror::Class> klass, bool is_static,
+void ClassLinker::CreateReferenceOffsets(Handle<mirror::Class> klass,
                                          uint32_t reference_offsets) {
-  size_t num_reference_fields =
-      is_static ? klass->NumReferenceStaticFieldsDuringLinking()
-                : klass->NumReferenceInstanceFieldsDuringLinking();
-  mirror::ObjectArray<mirror::ArtField>* fields =
-      is_static ? klass->GetSFields() : klass->GetIFields();
+  size_t num_reference_fields = klass->NumReferenceInstanceFieldsDuringLinking();
+  mirror::ObjectArray<mirror::ArtField>* fields = klass->GetIFields();
   // All of the fields that contain object references are guaranteed
   // to be at the beginning of the fields list.
   for (size_t i = 0; i < num_reference_fields; ++i) {
@@ -5155,12 +5223,7 @@
       break;
     }
   }
-  // Update fields in klass
-  if (is_static) {
-    klass->SetReferenceStaticOffsets(reference_offsets);
-  } else {
-    klass->SetReferenceInstanceOffsets(reference_offsets);
-  }
+  klass->SetReferenceInstanceOffsets(reference_offsets);
 }
 
 mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx,
@@ -5474,10 +5537,12 @@
 }
 
 void ClassLinker::DumpForSigQuit(std::ostream& os) {
+  Thread* self = Thread::Current();
   if (dex_cache_image_class_lookup_required_) {
+    ScopedObjectAccess soa(self);
     MoveImageClassesToClassTable();
   }
-  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
   os << "Loaded classes: " << class_table_.size() << " allocated classes\n";
 }
 
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 107a4b2..111dd63 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_CLASS_LINKER_H_
 #define ART_RUNTIME_CLASS_LINKER_H_
 
+#include <deque>
 #include <string>
 #include <utility>
 #include <vector>
@@ -47,6 +48,7 @@
   class StackTraceElement;
 }  // namespace mirror
 
+template<class T> class Handle;
 class InternTable;
 template<class T> class ObjectLock;
 class ScopedObjectAccessAlreadyRunnable;
@@ -90,17 +92,20 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns true if the class linker is initialized.
-  bool IsInitialized() const;
+  bool IsInitialized() const {
+    return init_done_;
+  }
 
   // Define a new a class based on a ClassDef from a DexFile
-  mirror::Class* DefineClass(const char* descriptor,
+  mirror::Class* DefineClass(Thread* self, const char* descriptor,
                              Handle<mirror::ClassLoader> class_loader,
                              const DexFile& dex_file, const DexFile::ClassDef& dex_class_def)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
   // by the given 'class_loader'.
-  mirror::Class* LookupClass(const char* descriptor, const mirror::ClassLoader* class_loader)
+  mirror::Class* LookupClass(Thread* self, const char* descriptor,
+                             const mirror::ClassLoader* class_loader)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -122,8 +127,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void DumpForSigQuit(std::ostream& os)
-      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_);
 
   size_t NumLoadedClasses()
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
@@ -219,7 +223,8 @@
   // Returns true on success, false if there's an exception pending.
   // can_run_clinit=false allows the compiler to attempt to init a class,
   // given the restriction that no <clinit> execution is possible.
-  bool EnsureInitialized(Handle<mirror::Class> c, bool can_init_fields, bool can_init_parents)
+  bool EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields,
+                         bool can_init_parents)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Initializes classes that have instances in the image but that have
@@ -294,6 +299,9 @@
                                            InstructionSet instruction_set,
                                            std::string* error_msg);
 
+  // Allocate an instance of a java.lang.Object.
+  mirror::Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // TODO: replace this with multiple methods that allocate the correct managed type.
   template <class T>
   mirror::ObjectArray<T>* AllocObjectArray(Thread* self, size_t length)
@@ -318,7 +326,8 @@
                                                                               size_t length)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VerifyClass(Handle<mirror::Class> klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void VerifyClass(Thread* self, Handle<mirror::Class> klass)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass,
                                mirror::Class::Status& oat_file_class_status)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -342,18 +351,22 @@
   // Get the oat code for a method when its class isn't yet initialized
   const void* GetQuickOatCodeFor(mirror::ArtMethod* method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#if defined(ART_USE_PORTABLE_COMPILER)
   const void* GetPortableOatCodeFor(mirror::ArtMethod* method, bool* have_portable_code)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#endif
 
   // Get the oat code for a method from a method index.
   const void* GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#if defined(ART_USE_PORTABLE_COMPILER)
   const void* GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#endif
+
+  // Get compiled code for a method, return null if no code
+  // exists. This is unlike Get..OatCodeFor which will return a bridge
+  // or interpreter entrypoint.
+  const void* GetOatMethodQuickCodeFor(mirror::ArtMethod* method)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const void* GetOatMethodPortableCodeFor(mirror::ArtMethod* method)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   pid_t GetClassesLockOwner();  // For SignalCatcher.
   pid_t GetDexLockOwner();  // For SignalCatcher.
@@ -403,7 +416,7 @@
   }
 
  private:
-  bool FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method)
+  const OatFile::OatMethod FindOatMethodFor(mirror::ArtMethod* method, bool* found)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   OatFile& GetImageOatFile(gc::space::ImageSpace* space)
@@ -435,29 +448,21 @@
                                   Handle<mirror::ClassLoader> class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void AppendToBootClassPath(const DexFile& dex_file)
+  void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
-                         mirror::Class* c, SafeMap<uint32_t, mirror::ArtField*>& field_map)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // Precomputes size needed for Class, in the case of a non-temporary class this size must be
   // sufficient to hold all static fields.
   uint32_t SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file,
                                             const DexFile::ClassDef& dex_class_def);
 
-  void LoadClass(const DexFile& dex_file,
-                 const DexFile::ClassDef& dex_class_def,
-                 Handle<mirror::Class> klass,
-                 mirror::ClassLoader* class_loader)
+  void LoadClass(Thread* self, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+                 Handle<mirror::Class> klass, mirror::ClassLoader* class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void LoadClassMembers(const DexFile& dex_file,
-                        const byte* class_data,
-                        Handle<mirror::Class> klass,
-                        mirror::ClassLoader* class_loader,
+  void LoadClassMembers(Thread* self, const DexFile& dex_file, const byte* class_data,
+                        Handle<mirror::Class> klass, mirror::ClassLoader* class_loader,
                         const OatFile::OatClass* oat_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -472,9 +477,9 @@
 
   void FixupStaticTrampolines(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Finds the associated oat class for a dex_file and descriptor. Returns whether the class
-  // was found, and sets the data in oat_class.
-  bool FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, OatFile::OatClass* oat_class)
+  // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
+  // error and sets found to false.
+  OatFile::OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
@@ -483,7 +488,7 @@
   bool IsDexFileRegisteredLocked(const DexFile& dex_file)
       SHARED_LOCKS_REQUIRED(dex_lock_, Locks::mutator_lock_);
 
-  bool InitializeClass(Handle<mirror::Class> klass, bool can_run_clinit,
+  bool InitializeClass(Thread* self, Handle<mirror::Class> klass, bool can_run_clinit,
                        bool can_init_parents)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool WaitForInitializeClass(Handle<mirror::Class> klass, Thread* self,
@@ -523,22 +528,18 @@
                             Handle<mirror::ObjectArray<mirror::Class>> interfaces)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size)
+  bool LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool LinkInstanceFields(Handle<mirror::Class> klass)
+  bool LinkInstanceFields(Thread* self, Handle<mirror::Class> klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size)
+  bool LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static, size_t* class_size)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
                 const DexFile& dex_file, uint32_t dex_method_index, uint32_t method_index)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   void CreateReferenceInstanceOffsets(Handle<mirror::Class> klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void CreateReferenceStaticOffsets(Handle<mirror::Class> klass)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void CreateReferenceOffsets(Handle<mirror::Class> klass, bool is_static,
-                              uint32_t reference_offsets)
+  void CreateReferenceOffsets(Handle<mirror::Class> klass, uint32_t reference_offsets)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // For use by ImageWriter to find DexCaches for its roots
@@ -672,7 +673,7 @@
   std::vector<const DexFile*> boot_class_path_;
 
   mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);;
+  std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);
   std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_);
   std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
 
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 37564e8..613ac66 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -278,10 +278,11 @@
     // Confirm that all instances fields are packed together at the start
     EXPECT_GE(klass->NumInstanceFields(), klass->NumReferenceInstanceFields());
     StackHandleScope<1> hs(Thread::Current());
-    FieldHelper fh(hs.NewHandle<mirror::ArtField>(nullptr));
+    MutableHandle<mirror::ArtField> fhandle = hs.NewHandle<mirror::ArtField>(nullptr);
     for (size_t i = 0; i < klass->NumReferenceInstanceFields(); i++) {
       mirror::ArtField* field = klass->GetInstanceField(i);
-      fh.ChangeField(field);
+      fhandle.Assign(field);
+      FieldHelper fh(fhandle);
       ASSERT_TRUE(!field->IsPrimitiveType());
       mirror::Class* field_type = fh.GetType();
       ASSERT_TRUE(field_type != NULL);
@@ -289,7 +290,8 @@
     }
     for (size_t i = klass->NumReferenceInstanceFields(); i < klass->NumInstanceFields(); i++) {
       mirror::ArtField* field = klass->GetInstanceField(i);
-      fh.ChangeField(field);
+      fhandle.Assign(field);
+      FieldHelper fh(fhandle);
       mirror::Class* field_type = fh.GetType();
       ASSERT_TRUE(field_type != NULL);
       if (!fh.GetField()->IsPrimitiveType() || !field_type->IsPrimitive()) {
@@ -488,9 +490,7 @@
     // alphabetical 64-bit
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_interpreter_),            "entryPointFromInterpreter"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_jni_),                    "entryPointFromJni"));
-#if defined(ART_USE_PORTABLE_COMPILER)
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_portable_compiled_code_), "entryPointFromPortableCompiledCode"));
-#endif
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_quick_compiled_code_),    "entryPointFromQuickCompiledCode"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, gc_map_),                                  "gcMap"));
 
@@ -530,7 +530,6 @@
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, object_size_),                   "objectSize"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, primitive_type_),                "primitiveType"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, reference_instance_offsets_),    "referenceInstanceOffsets"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, reference_static_offsets_),      "referenceStaticOffsets"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, status_),                        "status"));
   };
 };
@@ -858,7 +857,7 @@
       hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
   Handle<mirror::Class> statics(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
-  class_linker_->EnsureInitialized(statics, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), statics, true, true);
 
   // Static final primitives that are initialized by a compile-time constant
   // expression resolve to a copy of a constant value from the constant pool.
@@ -1136,7 +1135,7 @@
 
   CheckPreverified(security_manager.Get(), false);
 
-  class_linker_->EnsureInitialized(security_manager, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), security_manager, true, true);
   CheckPreverified(security_manager.Get(), true);
 }
 
@@ -1151,7 +1150,7 @@
 
   CheckPreverified(statics.Get(), false);
 
-  class_linker_->EnsureInitialized(statics, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), statics, true, true);
   CheckPreverified(statics.Get(), true);
 }
 
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 989384e..eed6f71 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -341,23 +341,22 @@
   for (const DexFile* dex_file : dex_files) {
     class_linker_->RegisterDexFile(*dex_file);
   }
-  ScopedObjectAccessUnchecked soa(Thread::Current());
-  ScopedLocalRef<jobject> class_loader_local(soa.Env(),
-      soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
-  jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
-  soa.Self()->SetClassLoaderOverride(soa.Decode<mirror::ClassLoader*>(class_loader_local.get()));
+  Thread* self = Thread::Current();
+  JNIEnvExt* env = self->GetJniEnv();
+  ScopedLocalRef<jobject> class_loader_local(env,
+      env->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
+  jobject class_loader = env->NewGlobalRef(class_loader_local.get());
+  self->SetClassLoaderOverride(class_loader_local.get());
   Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files);
   return class_loader;
 }
 
 CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) {
-  vm_->check_jni_abort_hook = Hook;
-  vm_->check_jni_abort_hook_data = &actual_;
+  vm_->SetCheckJniAbortHook(Hook, &actual_);
 }
 
 CheckJniAbortCatcher::~CheckJniAbortCatcher() {
-  vm_->check_jni_abort_hook = nullptr;
-  vm_->check_jni_abort_hook_data = nullptr;
+  vm_->SetCheckJniAbortHook(nullptr, nullptr);
   EXPECT_TRUE(actual_.empty()) << actual_;
 }
 
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 5b014b3..1ca6eb3 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -132,7 +132,7 @@
  private:
   static void Hook(void* data, const std::string& reason);
 
-  JavaVMExt* vm_;
+  JavaVMExt* const vm_;
   std::string actual_;
 
   DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index bb48be3..846216c 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -449,6 +449,10 @@
       break;
     }
     case Instruction::IPUT_QUICK:
+    case Instruction::IPUT_BOOLEAN_QUICK:
+    case Instruction::IPUT_BYTE_QUICK:
+    case Instruction::IPUT_CHAR_QUICK:
+    case Instruction::IPUT_SHORT_QUICK:
     case Instruction::IPUT_WIDE_QUICK:
     case Instruction::IPUT_OBJECT_QUICK: {
       // Since we replaced the field index, we ask the verifier to tell us which
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7fb199c..96b44bf 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -147,7 +147,7 @@
 
   size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     size_t depth = 0;
-    while (depth < kMaxAllocRecordStackDepth && stack_[depth].Method() != NULL) {
+    while (depth < kMaxAllocRecordStackDepth && stack_[depth].Method() != nullptr) {
       ++depth;
     }
     return depth;
@@ -178,7 +178,7 @@
   jobject type_;  // This is a weak global.
   size_t byte_count_;
   uint16_t thin_lock_id_;
-  AllocRecordStackTraceElement stack_[kMaxAllocRecordStackDepth];  // Unused entries have NULL method.
+  AllocRecordStackTraceElement stack_[kMaxAllocRecordStackDepth];  // Unused entries have nullptr method.
 };
 
 class Breakpoint {
@@ -297,7 +297,7 @@
 static JDWP::JdwpOptions gJdwpOptions;
 
 // Runtime JDWP state.
-static JDWP::JdwpState* gJdwpState = NULL;
+static JDWP::JdwpState* gJdwpState = nullptr;
 static bool gDebuggerConnected;  // debugger or DDMS is connected.
 static bool gDebuggerActive;     // debugger is making requests.
 static bool gDisposed;           // debugger called VirtualMachine.Dispose, so we should drop the connection.
@@ -399,58 +399,60 @@
   return thread->IsSuspended() && thread->GetDebugSuspendCount() > 0;
 }
 
-static mirror::Array* DecodeArray(JDWP::RefTypeId id, JDWP::JdwpError& status)
+static mirror::Array* DecodeNonNullArray(JDWP::RefTypeId id, JDWP::JdwpError* error)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
-    status = JDWP::ERR_INVALID_OBJECT;
-    return NULL;
+  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id, error);
+  if (o == nullptr) {
+    *error = JDWP::ERR_INVALID_OBJECT;
+    return nullptr;
   }
   if (!o->IsArrayInstance()) {
-    status = JDWP::ERR_INVALID_ARRAY;
-    return NULL;
+    *error = JDWP::ERR_INVALID_ARRAY;
+    return nullptr;
   }
-  status = JDWP::ERR_NONE;
+  *error = JDWP::ERR_NONE;
   return o->AsArray();
 }
 
-static mirror::Class* DecodeClass(JDWP::RefTypeId id, JDWP::JdwpError& status)
+static mirror::Class* DecodeClass(JDWP::RefTypeId id, JDWP::JdwpError* error)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
-    status = JDWP::ERR_INVALID_OBJECT;
-    return NULL;
+  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id, error);
+  if (o == nullptr) {
+    *error = JDWP::ERR_INVALID_OBJECT;
+    return nullptr;
   }
   if (!o->IsClass()) {
-    status = JDWP::ERR_INVALID_CLASS;
-    return NULL;
+    *error = JDWP::ERR_INVALID_CLASS;
+    return nullptr;
   }
-  status = JDWP::ERR_NONE;
+  *error = JDWP::ERR_NONE;
   return o->AsClass();
 }
 
-static JDWP::JdwpError DecodeThread(ScopedObjectAccessUnchecked& soa, JDWP::ObjectId thread_id, Thread*& thread)
+static Thread* DecodeThread(ScopedObjectAccessUnchecked& soa, JDWP::ObjectId thread_id,
+                            JDWP::JdwpError* error)
     EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
     LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* thread_peer = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_id);
-  if (thread_peer == NULL || thread_peer == ObjectRegistry::kInvalidObject) {
+  mirror::Object* thread_peer = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_id, error);
+  if (thread_peer == nullptr) {
     // This isn't even an object.
-    return JDWP::ERR_INVALID_OBJECT;
+    *error = JDWP::ERR_INVALID_OBJECT;
+    return nullptr;
   }
 
   mirror::Class* java_lang_Thread = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
   if (!java_lang_Thread->IsAssignableFrom(thread_peer->GetClass())) {
     // This isn't a thread.
-    return JDWP::ERR_INVALID_THREAD;
+    *error = JDWP::ERR_INVALID_THREAD;
+    return nullptr;
   }
 
-  thread = Thread::FromManagedThread(soa, thread_peer);
-  if (thread == NULL) {
-    // This is a java.lang.Thread without a Thread*. Must be a zombie.
-    return JDWP::ERR_THREAD_NOT_ALIVE;
-  }
-  return JDWP::ERR_NONE;
+  Thread* thread = Thread::FromManagedThread(soa, thread_peer);
+  // If thread is null then this a java.lang.Thread without a Thread*. Must be a un-started or a
+  // zombie.
+  *error = (thread == nullptr) ? JDWP::ERR_THREAD_NOT_ALIVE : JDWP::ERR_NONE;
+  return thread;
 }
 
 static JDWP::JdwpTag BasicTagFromDescriptor(const char* descriptor) {
@@ -468,7 +470,7 @@
 
 static JDWP::JdwpTag TagFromClass(const ScopedObjectAccessUnchecked& soa, mirror::Class* c)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  CHECK(c != NULL);
+  CHECK(c != nullptr);
   if (c->IsArrayClass()) {
     return JDWP::JT_ARRAY;
   }
@@ -510,7 +512,7 @@
  * Null objects are tagged JT_OBJECT.
  */
 JDWP::JdwpTag Dbg::TagFromObject(const ScopedObjectAccessUnchecked& soa, mirror::Object* o) {
-  return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
+  return (o == nullptr) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
 }
 
 static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
@@ -651,7 +653,7 @@
   // debugger, passively listen for a debugger, or block waiting for a
   // debugger.
   gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
-  if (gJdwpState == NULL) {
+  if (gJdwpState == nullptr) {
     // We probably failed because some other process has the port already, which means that
     // if we don't abort the user is likely to think they're talking to us when they're actually
     // talking to that other process.
@@ -709,7 +711,7 @@
 }
 
 Thread* Dbg::GetDebugThread() {
-  return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
+  return (gJdwpState != nullptr) ? gJdwpState->GetDebugThread() : nullptr;
 }
 
 void Dbg::ClearWaitForEventThread() {
@@ -827,12 +829,14 @@
 }
 
 std::string Dbg::GetClassName(JDWP::RefTypeId class_id) {
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(class_id);
-  if (o == NULL) {
-    return "NULL";
-  }
-  if (o == ObjectRegistry::kInvalidObject) {
-    return StringPrintf("invalid object %p", reinterpret_cast<void*>(class_id));
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(class_id, &error);
+  if (o == nullptr) {
+    if (error == JDWP::ERR_NONE) {
+      return "NULL";
+    } else {
+      return StringPrintf("invalid object %p", reinterpret_cast<void*>(class_id));
+    }
   }
   if (!o->IsClass()) {
     return StringPrintf("non-class %p", o);  // This is only used for debugging output anyway.
@@ -848,34 +852,37 @@
   return DescriptorToName(klass->GetDescriptor(&temp));
 }
 
-JDWP::JdwpError Dbg::GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId& class_object_id) {
+JDWP::JdwpError Dbg::GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId* class_object_id) {
   JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(id, status);
-  if (c == NULL) {
+  mirror::Class* c = DecodeClass(id, &status);
+  if (c == nullptr) {
+    *class_object_id = 0;
     return status;
   }
-  class_object_id = gRegistry->Add(c);
+  *class_object_id = gRegistry->Add(c);
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId& superclass_id) {
+JDWP::JdwpError Dbg::GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId* superclass_id) {
   JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(id, status);
-  if (c == NULL) {
+  mirror::Class* c = DecodeClass(id, &status);
+  if (c == nullptr) {
+    *superclass_id = 0;
     return status;
   }
   if (c->IsInterface()) {
     // http://code.google.com/p/android/issues/detail?id=20856
-    superclass_id = 0;
+    *superclass_id = 0;
   } else {
-    superclass_id = gRegistry->Add(c->GetSuperClass());
+    *superclass_id = gRegistry->Add(c->GetSuperClass());
   }
   return JDWP::ERR_NONE;
 }
 
 JDWP::JdwpError Dbg::GetClassLoader(JDWP::RefTypeId id, JDWP::ExpandBuf* pReply) {
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(id, &error);
+  if (o == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   expandBufAddObjectId(pReply, gRegistry->Add(o->GetClass()->GetClassLoader()));
@@ -883,10 +890,10 @@
 }
 
 JDWP::JdwpError Dbg::GetModifiers(JDWP::RefTypeId id, JDWP::ExpandBuf* pReply) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(id, status);
-  if (c == NULL) {
-    return status;
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(id, &error);
+  if (c == nullptr) {
+    return error;
   }
 
   uint32_t access_flags = c->GetAccessFlags() & kAccJavaFlagsMask;
@@ -903,10 +910,10 @@
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* reply)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+JDWP::JdwpError Dbg::GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* reply) {
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+  if (o == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
 
@@ -921,10 +928,10 @@
   Runtime::Current()->GetThreadList()->ResumeAll();
   self->TransitionFromSuspendedToRunnable();
 
-  if (monitor_info.owner_ != NULL) {
+  if (monitor_info.owner_ != nullptr) {
     expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer()));
   } else {
-    expandBufAddObjectId(reply, gRegistry->Add(NULL));
+    expandBufAddObjectId(reply, gRegistry->Add(nullptr));
   }
   expandBufAdd4BE(reply, monitor_info.entry_count_);
   expandBufAdd4BE(reply, monitor_info.waiters_.size());
@@ -935,8 +942,8 @@
 }
 
 JDWP::JdwpError Dbg::GetOwnedMonitors(JDWP::ObjectId thread_id,
-                                      std::vector<JDWP::ObjectId>& monitors,
-                                      std::vector<uint32_t>& stack_depths) {
+                                      std::vector<JDWP::ObjectId>* monitors,
+                                      std::vector<uint32_t>* stack_depths) {
   struct OwnedMonitorVisitor : public StackVisitor {
     OwnedMonitorVisitor(Thread* thread, Context* context,
                         std::vector<JDWP::ObjectId>* monitor_vector,
@@ -963,16 +970,17 @@
     }
 
     size_t current_stack_depth;
-    std::vector<JDWP::ObjectId>* monitors;
-    std::vector<uint32_t>* stack_depths;
+    std::vector<JDWP::ObjectId>* const monitors;
+    std::vector<uint32_t>* const stack_depths;
   };
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
   Thread* thread;
   {
     MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-    JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
-    if (error != JDWP::ERR_NONE) {
+    JDWP::JdwpError error;
+    thread = DecodeThread(soa, thread_id, &error);
+    if (thread == nullptr) {
       return error;
     }
     if (!IsSuspendedForDebugger(soa, thread)) {
@@ -980,20 +988,21 @@
     }
   }
   std::unique_ptr<Context> context(Context::Create());
-  OwnedMonitorVisitor visitor(thread, context.get(), &monitors, &stack_depths);
+  OwnedMonitorVisitor visitor(thread, context.get(), monitors, stack_depths);
   visitor.WalkStack();
   return JDWP::ERR_NONE;
 }
 
 JDWP::JdwpError Dbg::GetContendedMonitor(JDWP::ObjectId thread_id,
-                                         JDWP::ObjectId& contended_monitor) {
+                                         JDWP::ObjectId* contended_monitor) {
   mirror::Object* contended_monitor_obj;
   ScopedObjectAccessUnchecked soa(Thread::Current());
+  *contended_monitor = 0;
   {
     MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-    Thread* thread;
-    JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
-    if (error != JDWP::ERR_NONE) {
+    JDWP::JdwpError error;
+    Thread* thread = DecodeThread(soa, thread_id, &error);
+    if (thread == nullptr) {
       return error;
     }
     if (!IsSuspendedForDebugger(soa, thread)) {
@@ -1003,108 +1012,105 @@
   }
   // Add() requires the thread_list_lock_ not held to avoid the lock
   // level violation.
-  contended_monitor = gRegistry->Add(contended_monitor_obj);
+  *contended_monitor = gRegistry->Add(contended_monitor_obj);
   return JDWP::ERR_NONE;
 }
 
 JDWP::JdwpError Dbg::GetInstanceCounts(const std::vector<JDWP::RefTypeId>& class_ids,
-                                       std::vector<uint64_t>& counts)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+                                       std::vector<uint64_t>* counts) {
   gc::Heap* heap = Runtime::Current()->GetHeap();
   heap->CollectGarbage(false);
   std::vector<mirror::Class*> classes;
-  counts.clear();
+  counts->clear();
   for (size_t i = 0; i < class_ids.size(); ++i) {
-    JDWP::JdwpError status;
-    mirror::Class* c = DecodeClass(class_ids[i], status);
-    if (c == NULL) {
-      return status;
+    JDWP::JdwpError error;
+    mirror::Class* c = DecodeClass(class_ids[i], &error);
+    if (c == nullptr) {
+      return error;
     }
     classes.push_back(c);
-    counts.push_back(0);
+    counts->push_back(0);
   }
-  heap->CountInstances(classes, false, &counts[0]);
+  heap->CountInstances(classes, false, &(*counts)[0]);
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, std::vector<JDWP::ObjectId>& instances)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count,
+                                  std::vector<JDWP::ObjectId>* instances) {
   gc::Heap* heap = Runtime::Current()->GetHeap();
   // We only want reachable instances, so do a GC.
   heap->CollectGarbage(false);
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
   if (c == nullptr) {
-    return status;
+    return error;
   }
   std::vector<mirror::Object*> raw_instances;
   Runtime::Current()->GetHeap()->GetInstances(c, max_count, raw_instances);
   for (size_t i = 0; i < raw_instances.size(); ++i) {
-    instances.push_back(gRegistry->Add(raw_instances[i]));
+    instances->push_back(gRegistry->Add(raw_instances[i]));
   }
   return JDWP::ERR_NONE;
 }
 
 JDWP::JdwpError Dbg::GetReferringObjects(JDWP::ObjectId object_id, int32_t max_count,
-                                         std::vector<JDWP::ObjectId>& referring_objects)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+                                         std::vector<JDWP::ObjectId>* referring_objects) {
   gc::Heap* heap = Runtime::Current()->GetHeap();
   heap->CollectGarbage(false);
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+  if (o == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   std::vector<mirror::Object*> raw_instances;
   heap->GetReferringObjects(o, max_count, raw_instances);
   for (size_t i = 0; i < raw_instances.size(); ++i) {
-    referring_objects.push_back(gRegistry->Add(raw_instances[i]));
+    referring_objects->push_back(gRegistry->Add(raw_instances[i]));
   }
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::DisableCollection(JDWP::ObjectId object_id)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+JDWP::JdwpError Dbg::DisableCollection(JDWP::ObjectId object_id) {
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+  if (o == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   gRegistry->DisableCollection(object_id);
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::EnableCollection(JDWP::ObjectId object_id)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
+JDWP::JdwpError Dbg::EnableCollection(JDWP::ObjectId object_id) {
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
   // Unlike DisableCollection, JDWP specs do not state an invalid object causes an error. The RI
   // also ignores these cases and never return an error. However it's not obvious why this command
   // should behave differently from DisableCollection and IsCollected commands. So let's be more
   // strict and return an error if this happens.
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+  if (o == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   gRegistry->EnableCollection(object_id);
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::IsCollected(JDWP::ObjectId object_id, bool& is_collected)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+JDWP::JdwpError Dbg::IsCollected(JDWP::ObjectId object_id, bool* is_collected) {
+  *is_collected = true;
   if (object_id == 0) {
     // Null object id is invalid.
     return JDWP::ERR_INVALID_OBJECT;
   }
   // JDWP specs state an INVALID_OBJECT error is returned if the object ID is not valid. However
   // the RI seems to ignore this and assume object has been collected.
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
-    is_collected = true;
-  } else {
-    is_collected = gRegistry->IsCollected(object_id);
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+  if (o != nullptr) {
+    *is_collected = gRegistry->IsCollected(object_id);
   }
   return JDWP::ERR_NONE;
 }
 
-void Dbg::DisposeObject(JDWP::ObjectId object_id, uint32_t reference_count)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void Dbg::DisposeObject(JDWP::ObjectId object_id, uint32_t reference_count) {
   gRegistry->DisposeObject(object_id, reference_count);
 }
 
@@ -1120,10 +1126,10 @@
 }
 
 JDWP::JdwpError Dbg::GetReflectedType(JDWP::RefTypeId class_id, JDWP::ExpandBuf* pReply) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
-  if (c == NULL) {
-    return status;
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
+  if (c == nullptr) {
+    return error;
   }
 
   JDWP::JdwpTypeTag type_tag = GetTypeTag(c);
@@ -1132,12 +1138,12 @@
   return JDWP::ERR_NONE;
 }
 
-void Dbg::GetClassList(std::vector<JDWP::RefTypeId>& classes) {
+void Dbg::GetClassList(std::vector<JDWP::RefTypeId>* classes) {
   // Get the complete list of reference classes (i.e. all classes except
   // the primitive types).
   // Returns a newly-allocated buffer full of RefTypeId values.
   struct ClassListCreator {
-    explicit ClassListCreator(std::vector<JDWP::RefTypeId>& classes) : classes(classes) {
+    explicit ClassListCreator(std::vector<JDWP::RefTypeId>* classes) : classes(classes) {
     }
 
     static bool Visit(mirror::Class* c, void* arg) {
@@ -1148,12 +1154,12 @@
     // annotalysis.
     bool Visit(mirror::Class* c) NO_THREAD_SAFETY_ANALYSIS {
       if (!c->IsPrimitive()) {
-        classes.push_back(gRegistry->AddRefType(c));
+        classes->push_back(gRegistry->AddRefType(c));
       }
       return true;
     }
 
-    std::vector<JDWP::RefTypeId>& classes;
+    std::vector<JDWP::RefTypeId>* const classes;
   };
 
   ClassListCreator clc(classes);
@@ -1163,10 +1169,10 @@
 
 JDWP::JdwpError Dbg::GetClassInfo(JDWP::RefTypeId class_id, JDWP::JdwpTypeTag* pTypeTag,
                                   uint32_t* pStatus, std::string* pDescriptor) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
-  if (c == NULL) {
-    return status;
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
+  if (c == nullptr) {
+    return error;
   }
 
   if (c->IsArrayClass()) {
@@ -1181,26 +1187,26 @@
     *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
   }
 
-  if (pDescriptor != NULL) {
+  if (pDescriptor != nullptr) {
     std::string temp;
     *pDescriptor = c->GetDescriptor(&temp);
   }
   return JDWP::ERR_NONE;
 }
 
-void Dbg::FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>& ids) {
+void Dbg::FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>* ids) {
   std::vector<mirror::Class*> classes;
   Runtime::Current()->GetClassLinker()->LookupClasses(descriptor, classes);
-  ids.clear();
+  ids->clear();
   for (size_t i = 0; i < classes.size(); ++i) {
-    ids.push_back(gRegistry->Add(classes[i]));
+    ids->push_back(gRegistry->Add(classes[i]));
   }
 }
 
-JDWP::JdwpError Dbg::GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
-  if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+JDWP::JdwpError Dbg::GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply) {
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+  if (o == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
 
@@ -1214,37 +1220,39 @@
 }
 
 JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string* signature) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
-  if (c == NULL) {
-    return status;
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
+  if (c == nullptr) {
+    return error;
   }
   std::string temp;
   *signature = c->GetDescriptor(&temp);
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::GetSourceFile(JDWP::RefTypeId class_id, std::string& result) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
+JDWP::JdwpError Dbg::GetSourceFile(JDWP::RefTypeId class_id, std::string* result) {
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
   if (c == nullptr) {
-    return status;
+    return error;
   }
   const char* source_file = c->GetSourceFile();
   if (source_file == nullptr) {
     return JDWP::ERR_ABSENT_INFORMATION;
   }
-  result = source_file;
+  *result = source_file;
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::GetObjectTag(JDWP::ObjectId object_id, uint8_t& tag) {
+JDWP::JdwpError Dbg::GetObjectTag(JDWP::ObjectId object_id, uint8_t* tag) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
-  if (o == ObjectRegistry::kInvalidObject) {
-    return JDWP::ERR_INVALID_OBJECT;
+  JDWP::JdwpError error;
+  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+  if (error != JDWP::ERR_NONE) {
+    *tag = JDWP::JT_VOID;
+    return error;
   }
-  tag = TagFromObject(soa, o);
+  *tag = TagFromObject(soa, o);
   return JDWP::ERR_NONE;
 }
 
@@ -1278,21 +1286,21 @@
   }
 }
 
-JDWP::JdwpError Dbg::GetArrayLength(JDWP::ObjectId array_id, int& length) {
-  JDWP::JdwpError status;
-  mirror::Array* a = DecodeArray(array_id, status);
-  if (a == NULL) {
-    return status;
+JDWP::JdwpError Dbg::GetArrayLength(JDWP::ObjectId array_id, int32_t* length) {
+  JDWP::JdwpError error;
+  mirror::Array* a = DecodeNonNullArray(array_id, &error);
+  if (a == nullptr) {
+    return error;
   }
-  length = a->GetLength();
+  *length = a->GetLength();
   return JDWP::ERR_NONE;
 }
 
 JDWP::JdwpError Dbg::OutputArray(JDWP::ObjectId array_id, int offset, int count, JDWP::ExpandBuf* pReply) {
-  JDWP::JdwpError status;
-  mirror::Array* a = DecodeArray(array_id, status);
+  JDWP::JdwpError error;
+  mirror::Array* a = DecodeNonNullArray(array_id, &error);
   if (a == nullptr) {
-    return status;
+    return error;
   }
 
   if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
@@ -1335,24 +1343,23 @@
 }
 
 template <typename T>
-static void CopyArrayData(mirror::Array* a, JDWP::Request& src, int offset, int count)
+static void CopyArrayData(mirror::Array* a, JDWP::Request* src, int offset, int count)
     NO_THREAD_SAFETY_ANALYSIS {
   // TODO: fix when annotalysis correctly handles non-member functions.
   DCHECK(a->GetClass()->IsPrimitiveArray());
 
   T* dst = reinterpret_cast<T*>(a->GetRawData(sizeof(T), offset));
   for (int i = 0; i < count; ++i) {
-    *dst++ = src.ReadValue(sizeof(T));
+    *dst++ = src->ReadValue(sizeof(T));
   }
 }
 
 JDWP::JdwpError Dbg::SetArrayElements(JDWP::ObjectId array_id, int offset, int count,
-                                      JDWP::Request& request)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  JDWP::JdwpError status;
-  mirror::Array* dst = DecodeArray(array_id, status);
-  if (dst == NULL) {
-    return status;
+                                      JDWP::Request* request) {
+  JDWP::JdwpError error;
+  mirror::Array* dst = DecodeNonNullArray(array_id, &error);
+  if (dst == nullptr) {
+    return error;
   }
 
   if (offset < 0 || count < 0 || offset > dst->GetLength() || dst->GetLength() - offset < count) {
@@ -1375,10 +1382,11 @@
   } else {
     mirror::ObjectArray<mirror::Object>* oa = dst->AsObjectArray<mirror::Object>();
     for (int i = 0; i < count; ++i) {
-      JDWP::ObjectId id = request.ReadObjectId();
-      mirror::Object* o = gRegistry->Get<mirror::Object*>(id);
-      if (o == ObjectRegistry::kInvalidObject) {
-        return JDWP::ERR_INVALID_OBJECT;
+      JDWP::ObjectId id = request->ReadObjectId();
+      JDWP::JdwpError error;
+      mirror::Object* o = gRegistry->Get<mirror::Object*>(id, &error);
+      if (error != JDWP::ERR_NONE) {
+        return error;
       }
       oa->Set<false>(offset + i, o);
     }
@@ -1391,13 +1399,14 @@
   return gRegistry->Add(mirror::String::AllocFromModifiedUtf8(Thread::Current(), str.c_str()));
 }
 
-JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId& new_object) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
-  if (c == NULL) {
-    return status;
+JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object) {
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
+  if (c == nullptr) {
+    *new_object = 0;
+    return error;
   }
-  new_object = gRegistry->Add(c->AllocObject(Thread::Current()));
+  *new_object = gRegistry->Add(c->AllocObject(Thread::Current()));
   return JDWP::ERR_NONE;
 }
 
@@ -1405,15 +1414,16 @@
  * Used by Eclipse's "Display" view to evaluate "new byte[5]" to get "(byte[]) [0, 0, 0, 0, 0]".
  */
 JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t length,
-                                       JDWP::ObjectId& new_array) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(array_class_id, status);
-  if (c == NULL) {
-    return status;
+                                       JDWP::ObjectId* new_array) {
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(array_class_id, &error);
+  if (c == nullptr) {
+    *new_array = 0;
+    return error;
   }
-  new_array = gRegistry->Add(mirror::Array::Alloc<true>(Thread::Current(), c, length,
-                                                        c->GetComponentSize(),
-                                                        Runtime::Current()->GetHeap()->GetCurrentAllocator()));
+  *new_array = gRegistry->Add(mirror::Array::Alloc<true>(Thread::Current(), c, length,
+                                                         c->GetComponentSizeShift(),
+                                                         Runtime::Current()->GetHeap()->GetCurrentAllocator()));
   return JDWP::ERR_NONE;
 }
 
@@ -1442,7 +1452,9 @@
 
 bool Dbg::MatchThread(JDWP::ObjectId expected_thread_id, Thread* event_thread) {
   CHECK(event_thread != nullptr);
-  mirror::Object* expected_thread_peer = gRegistry->Get<mirror::Object*>(expected_thread_id);
+  JDWP::JdwpError error;
+  mirror::Object* expected_thread_peer = gRegistry->Get<mirror::Object*>(expected_thread_id,
+                                                                         &error);
   return expected_thread_peer == event_thread->GetPeer();
 }
 
@@ -1459,8 +1471,8 @@
   if (event_class == nullptr) {
     return false;
   }
-  JDWP::JdwpError status;
-  mirror::Class* expected_class = DecodeClass(class_id, status);
+  JDWP::JdwpError error;
+  mirror::Class* expected_class = DecodeClass(class_id, &error);
   CHECK(expected_class != nullptr);
   return expected_class->IsAssignableFrom(event_class);
 }
@@ -1475,7 +1487,8 @@
 }
 
 bool Dbg::MatchInstance(JDWP::ObjectId expected_instance_id, mirror::Object* event_instance) {
-  mirror::Object* modifier_instance = gRegistry->Get<mirror::Object*>(expected_instance_id);
+  JDWP::JdwpError error;
+  mirror::Object* modifier_instance = gRegistry->Get<mirror::Object*>(expected_instance_id, &error);
   return modifier_instance == event_instance;
 }
 
@@ -1492,8 +1505,7 @@
   }
 }
 
-std::string Dbg::GetMethodName(JDWP::MethodId method_id)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+std::string Dbg::GetMethodName(JDWP::MethodId method_id) {
   mirror::ArtMethod* m = FromMethodId(method_id);
   if (m == nullptr) {
     return "NULL";
@@ -1501,8 +1513,7 @@
   return m->GetName();
 }
 
-std::string Dbg::GetFieldName(JDWP::FieldId field_id)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+std::string Dbg::GetFieldName(JDWP::FieldId field_id) {
   mirror::ArtField* f = FromFieldId(field_id);
   if (f == nullptr) {
     return "NULL";
@@ -1569,10 +1580,10 @@
 }
 
 JDWP::JdwpError Dbg::OutputDeclaredFields(JDWP::RefTypeId class_id, bool with_generic, JDWP::ExpandBuf* pReply) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
-  if (c == NULL) {
-    return status;
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
+  if (c == nullptr) {
+    return error;
   }
 
   size_t instance_field_count = c->NumInstanceFields();
@@ -1596,10 +1607,10 @@
 
 JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_generic,
                                            JDWP::ExpandBuf* pReply) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(class_id, status);
-  if (c == NULL) {
-    return status;
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(class_id, &error);
+  if (c == nullptr) {
+    return error;
   }
 
   size_t direct_method_count = c->NumDirectMethods();
@@ -1622,12 +1633,12 @@
 }
 
 JDWP::JdwpError Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId class_id, JDWP::ExpandBuf* pReply) {
-  JDWP::JdwpError status;
+  JDWP::JdwpError error;
   Thread* self = Thread::Current();
   StackHandleScope<1> hs(self);
-  Handle<mirror::Class> c(hs.NewHandle(DecodeClass(class_id, status)));
+  Handle<mirror::Class> c(hs.NewHandle(DecodeClass(class_id, &error)));
   if (c.Get() == nullptr) {
-    return status;
+    return error;
   }
   size_t interface_count = c->NumDirectInterfaces();
   expandBufAdd4BE(pReply, interface_count);
@@ -1638,8 +1649,7 @@
   return JDWP::ERR_NONE;
 }
 
-void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::ExpandBuf* pReply)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::ExpandBuf* pReply) {
   struct DebugCallbackContext {
     int numItems;
     JDWP::ExpandBuf* pReply;
@@ -1678,7 +1688,7 @@
 
   if (code_item != nullptr) {
     m->GetDexFile()->DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(),
-                                     DebugCallbackContext::Callback, NULL, &context);
+                                     DebugCallbackContext::Callback, nullptr, &context);
   }
 
   JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
@@ -1736,7 +1746,7 @@
   const DexFile::CodeItem* code_item = m->GetCodeItem();
   if (code_item != nullptr) {
     m->GetDexFile()->DecodeDebugInfo(
-        code_item, m->IsStatic(), m->GetDexMethodIndex(), NULL, DebugCallbackContext::Callback,
+        code_item, m->IsStatic(), m->GetDexMethodIndex(), nullptr, DebugCallbackContext::Callback,
         &context);
   }
 
@@ -1758,10 +1768,9 @@
 }
 
 JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id,
-                                  std::vector<uint8_t>& bytecodes)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+                                  std::vector<uint8_t>* bytecodes) {
   mirror::ArtMethod* m = FromMethodId(method_id);
-  if (m == NULL) {
+  if (m == nullptr) {
     return JDWP::ERR_INVALID_METHODID;
   }
   const DexFile::CodeItem* code_item = m->GetCodeItem();
@@ -1769,7 +1778,7 @@
   const uint8_t* begin = reinterpret_cast<const uint8_t*>(code_item->insns_);
   const uint8_t* end = begin + byte_count;
   for (const uint8_t* p = begin; p != end; ++p) {
-    bytecodes.push_back(*p);
+    bytecodes->push_back(*p);
   }
   return JDWP::ERR_NONE;
 }
@@ -1786,24 +1795,24 @@
                                          JDWP::FieldId field_id, JDWP::ExpandBuf* pReply,
                                          bool is_static)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  JDWP::JdwpError status;
-  mirror::Class* c = DecodeClass(ref_type_id, status);
-  if (ref_type_id != 0 && c == NULL) {
-    return status;
+  JDWP::JdwpError error;
+  mirror::Class* c = DecodeClass(ref_type_id, &error);
+  if (ref_type_id != 0 && c == nullptr) {
+    return error;
   }
 
-  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id);
-  if ((!is_static && o == NULL) || o == ObjectRegistry::kInvalidObject) {
+  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error);
+  if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   mirror::ArtField* f = FromFieldId(field_id);
 
   mirror::Class* receiver_class = c;
-  if (receiver_class == NULL && o != NULL) {
+  if (receiver_class == nullptr && o != nullptr) {
     receiver_class = o->GetClass();
   }
-  // TODO: should we give up now if receiver_class is NULL?
-  if (receiver_class != NULL && !f->GetDeclaringClass()->IsAssignableFrom(receiver_class)) {
+  // TODO: should we give up now if receiver_class is nullptr?
+  if (receiver_class != nullptr && !f->GetDeclaringClass()->IsAssignableFrom(receiver_class)) {
     LOG(INFO) << "ERR_INVALID_FIELDID: " << PrettyField(f) << " " << PrettyClass(receiver_class);
     return JDWP::ERR_INVALID_FIELDID;
   }
@@ -1816,7 +1825,8 @@
     }
   } else {
     if (f->IsStatic()) {
-      LOG(WARNING) << "Ignoring non-NULL receiver for ObjectReference.SetValues on static field " << PrettyField(f);
+      LOG(WARNING) << "Ignoring non-nullptr receiver for ObjectReference.SetValues on static field "
+          << PrettyField(f);
     }
   }
   if (f->IsStatic()) {
@@ -1844,15 +1854,17 @@
   return GetFieldValueImpl(0, object_id, field_id, pReply, false);
 }
 
-JDWP::JdwpError Dbg::GetStaticFieldValue(JDWP::RefTypeId ref_type_id, JDWP::FieldId field_id, JDWP::ExpandBuf* pReply) {
+JDWP::JdwpError Dbg::GetStaticFieldValue(JDWP::RefTypeId ref_type_id, JDWP::FieldId field_id,
+                                         JDWP::ExpandBuf* pReply) {
   return GetFieldValueImpl(ref_type_id, 0, field_id, pReply, true);
 }
 
 static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId field_id,
                                          uint64_t value, int width, bool is_static)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id);
-  if ((!is_static && o == NULL) || o == ObjectRegistry::kInvalidObject) {
+  JDWP::JdwpError error;
+  mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error);
+  if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   mirror::ArtField* f = FromFieldId(field_id);
@@ -1865,7 +1877,7 @@
     }
   } else {
     if (f->IsStatic()) {
-      LOG(WARNING) << "Ignoring non-NULL receiver for ObjectReference.SetValues on static field " << PrettyField(f);
+      LOG(WARNING) << "Ignoring non-nullptr receiver for ObjectReference.SetValues on static field " << PrettyField(f);
     }
   }
   if (f->IsStatic()) {
@@ -1885,11 +1897,11 @@
       f->Set32<false>(o, value);
     }
   } else {
-    mirror::Object* v = Dbg::GetObjectRegistry()->Get<mirror::Object*>(value);
-    if (v == ObjectRegistry::kInvalidObject) {
+    mirror::Object* v = Dbg::GetObjectRegistry()->Get<mirror::Object*>(value, &error);
+    if (error != JDWP::ERR_NONE) {
       return JDWP::ERR_INVALID_OBJECT;
     }
-    if (v != NULL) {
+    if (v != nullptr) {
       mirror::Class* field_type;
       {
         StackHandleScope<3> hs(Thread::Current());
@@ -1919,8 +1931,12 @@
 }
 
 JDWP::JdwpError Dbg::StringToUtf8(JDWP::ObjectId string_id, std::string* str) {
-  mirror::Object* obj = gRegistry->Get<mirror::Object*>(string_id);
-  if (obj == nullptr || obj == ObjectRegistry::kInvalidObject) {
+  JDWP::JdwpError error;
+  mirror::Object* obj = gRegistry->Get<mirror::Object*>(string_id, &error);
+  if (error != JDWP::ERR_NONE) {
+    return error;
+  }
+  if (obj == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   {
@@ -1957,40 +1973,42 @@
   }
 }
 
-JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string& name) {
+JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string* name) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+  JDWP::JdwpError error;
+  Thread* thread = DecodeThread(soa, thread_id, &error);
+  UNUSED(thread);
   if (error != JDWP::ERR_NONE && error != JDWP::ERR_THREAD_NOT_ALIVE) {
     return error;
   }
 
   // We still need to report the zombie threads' names, so we can't just call Thread::GetThreadName.
-  mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id);
+  mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id, &error);
+  CHECK(thread_object != nullptr) << error;
   mirror::ArtField* java_lang_Thread_name_field =
       soa.DecodeField(WellKnownClasses::java_lang_Thread_name);
   mirror::String* s =
       reinterpret_cast<mirror::String*>(java_lang_Thread_name_field->GetObject(thread_object));
-  if (s != NULL) {
-    name = s->ToModifiedUtf8();
+  if (s != nullptr) {
+    *name = s->ToModifiedUtf8();
   }
   return JDWP::ERR_NONE;
 }
 
 JDWP::JdwpError Dbg::GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply) {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id);
-  if (thread_object == ObjectRegistry::kInvalidObject) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
+  JDWP::JdwpError error;
+  mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id, &error);
+  if (error != JDWP::ERR_NONE) {
     return JDWP::ERR_INVALID_OBJECT;
   }
-  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroup");
+  ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroup");
   // Okay, so it's an object, but is it actually a thread?
-  JDWP::JdwpError error;
   {
     MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-    Thread* thread;
-    error = DecodeThread(soa, thread_id, thread);
+    Thread* thread = DecodeThread(soa, thread_id, &error);
+    UNUSED(thread);
   }
   if (error == JDWP::ERR_THREAD_NOT_ALIVE) {
     // Zombie threads are in the null group.
@@ -2006,36 +2024,124 @@
     JDWP::ObjectId thread_group_id = gRegistry->Add(group);
     expandBufAddObjectId(pReply, thread_group_id);
   }
-  soa.Self()->EndAssertNoThreadSuspension(old_cause);
   return error;
 }
 
-std::string Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id) {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-  CHECK(thread_group != nullptr);
-  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName");
+static mirror::Object* DecodeThreadGroup(ScopedObjectAccessUnchecked& soa,
+                                         JDWP::ObjectId thread_group_id, JDWP::JdwpError* error)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::Object* thread_group = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_group_id,
+                                                                                error);
+  if (*error != JDWP::ERR_NONE) {
+    return nullptr;
+  }
+  if (thread_group == nullptr) {
+    *error = JDWP::ERR_INVALID_OBJECT;
+    return nullptr;
+  }
   mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
   CHECK(c != nullptr);
-  mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
-  CHECK(f != NULL);
-  mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
-  soa.Self()->EndAssertNoThreadSuspension(old_cause);
-  return s->ToModifiedUtf8();
+  if (!c->IsAssignableFrom(thread_group->GetClass())) {
+    // This is not a java.lang.ThreadGroup.
+    *error = JDWP::ERR_INVALID_THREAD_GROUP;
+    return nullptr;
+  }
+  *error = JDWP::ERR_NONE;
+  return thread_group;
 }
 
-JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id) {
+JDWP::JdwpError Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id, JDWP::ExpandBuf* pReply) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-  CHECK(thread_group != nullptr);
-  const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupParent");
+  JDWP::JdwpError error;
+  mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error);
+  if (error != JDWP::ERR_NONE) {
+    return error;
+  }
+  ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupName");
   mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
-  CHECK(c != nullptr);
-  mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
-  CHECK(f != NULL);
-  mirror::Object* parent = f->GetObject(thread_group);
-  soa.Self()->EndAssertNoThreadSuspension(old_cause);
-  return gRegistry->Add(parent);
+  mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
+  CHECK(f != nullptr);
+  mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
+
+  std::string thread_group_name(s->ToModifiedUtf8());
+  expandBufAddUtf8String(pReply, thread_group_name);
+  return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id, JDWP::ExpandBuf* pReply) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
+  JDWP::JdwpError error;
+  mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error);
+  if (error != JDWP::ERR_NONE) {
+    return error;
+  }
+  mirror::Object* parent;
+  {
+    ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupParent");
+    mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+    CHECK(c != nullptr);
+    mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
+    CHECK(f != nullptr);
+    parent = f->GetObject(thread_group);
+  }
+  JDWP::ObjectId parent_group_id = gRegistry->Add(parent);
+  expandBufAddObjectId(pReply, parent_group_id);
+  return JDWP::ERR_NONE;
+}
+
+static void GetChildThreadGroups(ScopedObjectAccessUnchecked& soa, mirror::Object* thread_group,
+                                 std::vector<JDWP::ObjectId>* child_thread_group_ids)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  CHECK(thread_group != nullptr);
+
+  // Get the ArrayList<ThreadGroup> "groups" out of this thread group...
+  mirror::ArtField* groups_field = thread_group->GetClass()->FindInstanceField("groups", "Ljava/util/List;");
+  mirror::Object* groups_array_list = groups_field->GetObject(thread_group);
+
+  // Get the array and size out of the ArrayList<ThreadGroup>...
+  mirror::ArtField* array_field = groups_array_list->GetClass()->FindInstanceField("array", "[Ljava/lang/Object;");
+  mirror::ArtField* size_field = groups_array_list->GetClass()->FindInstanceField("size", "I");
+  mirror::ObjectArray<mirror::Object>* groups_array =
+      array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>();
+  const int32_t size = size_field->GetInt(groups_array_list);
+
+  // Copy the first 'size' elements out of the array into the result.
+  ObjectRegistry* registry = Dbg::GetObjectRegistry();
+  for (int32_t i = 0; i < size; ++i) {
+    child_thread_group_ids->push_back(registry->Add(groups_array->Get(i)));
+  }
+}
+
+JDWP::JdwpError Dbg::GetThreadGroupChildren(JDWP::ObjectId thread_group_id,
+                                            JDWP::ExpandBuf* pReply) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
+  JDWP::JdwpError error;
+  mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error);
+  if (error != JDWP::ERR_NONE) {
+    return error;
+  }
+
+  // Add child threads.
+  {
+    std::vector<JDWP::ObjectId> child_thread_ids;
+    GetThreads(thread_group, &child_thread_ids);
+    expandBufAdd4BE(pReply, child_thread_ids.size());
+    for (JDWP::ObjectId child_thread_id : child_thread_ids) {
+      expandBufAddObjectId(pReply, child_thread_id);
+    }
+  }
+
+  // Add child thread groups.
+  {
+    std::vector<JDWP::ObjectId> child_thread_groups_ids;
+    GetChildThreadGroups(soa, thread_group, &child_thread_groups_ids);
+    expandBufAdd4BE(pReply, child_thread_groups_ids.size());
+    for (JDWP::ObjectId child_thread_group_id : child_thread_groups_ids) {
+      expandBufAddObjectId(pReply, child_thread_group_id);
+    }
+  }
+
+  return JDWP::ERR_NONE;
 }
 
 JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
@@ -2045,13 +2151,6 @@
   return gRegistry->Add(group);
 }
 
-JDWP::ObjectId Dbg::GetMainThreadGroupId() {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup);
-  mirror::Object* group = f->GetObject(f->GetDeclaringClass());
-  return gRegistry->Add(group);
-}
-
 JDWP::JdwpThreadStatus Dbg::ToJdwpThreadStatus(ThreadState state) {
   switch (state) {
     case kBlocked:
@@ -2093,8 +2192,8 @@
   *pSuspendStatus = JDWP::SUSPEND_STATUS_NOT_SUSPENDED;
 
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+  JDWP::JdwpError error;
+  Thread* thread = DecodeThread(soa, thread_id, &error);
   if (error != JDWP::ERR_NONE) {
     if (error == JDWP::ERR_THREAD_NOT_ALIVE) {
       *pThreadStatus = JDWP::TS_ZOMBIE;
@@ -2114,8 +2213,8 @@
 JDWP::JdwpError Dbg::GetThreadDebugSuspendCount(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply) {
   ScopedObjectAccess soa(Thread::Current());
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+  JDWP::JdwpError error;
+  Thread* thread = DecodeThread(soa, thread_id, &error);
   if (error != JDWP::ERR_NONE) {
     return error;
   }
@@ -2127,8 +2226,8 @@
 JDWP::JdwpError Dbg::Interrupt(JDWP::ObjectId thread_id) {
   ScopedObjectAccess soa(Thread::Current());
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+  JDWP::JdwpError error;
+  Thread* thread = DecodeThread(soa, thread_id, &error);
   if (error != JDWP::ERR_NONE) {
     return error;
   }
@@ -2149,9 +2248,8 @@
   return (group == desired_thread_group);
 }
 
-void Dbg::GetThreads(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& thread_ids) {
+void Dbg::GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* thread_ids) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
   std::list<Thread*> all_threads_list;
   {
     MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
@@ -2178,37 +2276,15 @@
       continue;
     }
     if (IsInDesiredThreadGroup(soa, thread_group, peer)) {
-      thread_ids.push_back(gRegistry->Add(peer));
+      thread_ids->push_back(gRegistry->Add(peer));
     }
   }
 }
 
-void Dbg::GetChildThreadGroups(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& child_thread_group_ids) {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-
-  // Get the ArrayList<ThreadGroup> "groups" out of this thread group...
-  mirror::ArtField* groups_field = thread_group->GetClass()->FindInstanceField("groups", "Ljava/util/List;");
-  mirror::Object* groups_array_list = groups_field->GetObject(thread_group);
-
-  // Get the array and size out of the ArrayList<ThreadGroup>...
-  mirror::ArtField* array_field = groups_array_list->GetClass()->FindInstanceField("array", "[Ljava/lang/Object;");
-  mirror::ArtField* size_field = groups_array_list->GetClass()->FindInstanceField("size", "I");
-  mirror::ObjectArray<mirror::Object>* groups_array =
-      array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>();
-  const int32_t size = size_field->GetInt(groups_array_list);
-
-  // Copy the first 'size' elements out of the array into the result.
-  for (int32_t i = 0; i < size; ++i) {
-    child_thread_group_ids.push_back(gRegistry->Add(groups_array->Get(i)));
-  }
-}
-
-static int GetStackDepth(Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static int GetStackDepth(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   struct CountStackDepthVisitor : public StackVisitor {
     explicit CountStackDepthVisitor(Thread* thread)
-        : StackVisitor(thread, NULL), depth(0) {}
+        : StackVisitor(thread, nullptr), depth(0) {}
 
     // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
     // annotalysis.
@@ -2226,18 +2302,19 @@
   return visitor.depth;
 }
 
-JDWP::JdwpError Dbg::GetThreadFrameCount(JDWP::ObjectId thread_id, size_t& result) {
+JDWP::JdwpError Dbg::GetThreadFrameCount(JDWP::ObjectId thread_id, size_t* result) {
   ScopedObjectAccess soa(Thread::Current());
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+  JDWP::JdwpError error;
+  *result = 0;
+  Thread* thread = DecodeThread(soa, thread_id, &error);
   if (error != JDWP::ERR_NONE) {
     return error;
   }
   if (!IsSuspendedForDebugger(soa, thread)) {
     return JDWP::ERR_THREAD_NOT_SUSPENDED;
   }
-  result = GetStackDepth(thread);
+  *result = GetStackDepth(thread);
   return JDWP::ERR_NONE;
 }
 
@@ -2247,7 +2324,7 @@
    public:
     GetFrameVisitor(Thread* thread, size_t start_frame, size_t frame_count, JDWP::ExpandBuf* buf)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, NULL), depth_(0),
+        : StackVisitor(thread, nullptr), depth_(0),
           start_frame_(start_frame), frame_count_(frame_count), buf_(buf) {
       expandBufAdd4BE(buf_, frame_count_);
     }
@@ -2282,8 +2359,8 @@
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+  JDWP::JdwpError error;
+  Thread* thread = DecodeThread(soa, thread_id, &error);
   if (error != JDWP::ERR_NONE) {
     return error;
   }
@@ -2314,12 +2391,13 @@
 
 JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspension) {
   Thread* self = Thread::Current();
-  ScopedLocalRef<jobject> peer(self->GetJniEnv(), NULL);
+  ScopedLocalRef<jobject> peer(self->GetJniEnv(), nullptr);
   {
     ScopedObjectAccess soa(self);
-    peer.reset(soa.AddLocalReference<jobject>(gRegistry->Get<mirror::Object*>(thread_id)));
+    JDWP::JdwpError error;
+    peer.reset(soa.AddLocalReference<jobject>(gRegistry->Get<mirror::Object*>(thread_id, &error)));
   }
-  if (peer.get() == NULL) {
+  if (peer.get() == nullptr) {
     return JDWP::ERR_THREAD_NOT_ALIVE;
   }
   // Suspend thread to build stack trace. Take suspend thread lock to avoid races with threads
@@ -2329,7 +2407,7 @@
   ThreadList* thread_list = Runtime::Current()->GetThreadList();
   Thread* thread = thread_list->SuspendThreadByPeer(peer.get(), request_suspension, true,
                                                     &timed_out);
-  if (thread != NULL) {
+  if (thread != nullptr) {
     return JDWP::ERR_NONE;
   } else if (timed_out) {
     return JDWP::ERR_INTERNAL;
@@ -2340,13 +2418,15 @@
 
 void Dbg::ResumeThread(JDWP::ObjectId thread_id) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  mirror::Object* peer = gRegistry->Get<mirror::Object*>(thread_id);
+  JDWP::JdwpError error;
+  mirror::Object* peer = gRegistry->Get<mirror::Object*>(thread_id, &error);
+  CHECK(peer != nullptr) << error;
   Thread* thread;
   {
     MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
     thread = Thread::FromManagedThread(soa, peer);
   }
-  if (thread == NULL) {
+  if (thread == nullptr) {
     LOG(WARNING) << "No such thread for resume: " << peer;
     return;
   }
@@ -2367,7 +2447,7 @@
 struct GetThisVisitor : public StackVisitor {
   GetThisVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(thread, context), this_object(NULL), frame_id(frame_id) {}
+      : StackVisitor(thread, context), this_object(nullptr), frame_id(frame_id) {}
 
   // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
   // annotalysis.
@@ -2390,7 +2470,8 @@
   Thread* thread;
   {
     MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-    JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+    JDWP::JdwpError error;
+    thread = DecodeThread(soa, thread_id, &error);
     if (error != JDWP::ERR_NONE) {
       return error;
     }
@@ -2405,298 +2486,329 @@
   return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                   JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
-  struct GetLocalVisitor : public StackVisitor {
-    GetLocalVisitor(const ScopedObjectAccessUnchecked& soa, Thread* thread, Context* context,
-                    JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, context), soa_(soa), frame_id_(frame_id), slot_(slot), tag_(tag),
-          buf_(buf), width_(width), error_(JDWP::ERR_NONE) {}
+// Walks the stack until we find the frame with the given FrameId.
+class FindFrameVisitor FINAL : public StackVisitor {
+ public:
+  FindFrameVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, context), frame_id_(frame_id), error_(JDWP::ERR_INVALID_FRAMEID) {}
 
-    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
-    // annotalysis.
-    bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
-      if (GetFrameId() != frame_id_) {
-        return true;  // Not our frame, carry on.
-      }
-      // TODO: check that the tag is compatible with the actual type of the slot!
-      // TODO: check slot is valid for this method or return INVALID_SLOT error.
-      mirror::ArtMethod* m = GetMethod();
-      if (m->IsNative()) {
-        // We can't read local value from native method.
-        error_ = JDWP::ERR_OPAQUE_FRAME;
-        return false;
-      }
-      uint16_t reg = DemangleSlot(slot_, m);
-      constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
-      switch (tag_) {
-        case JDWP::JT_BOOLEAN: {
-          CHECK_EQ(width_, 1U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
-            JDWP::Set1(buf_+1, intVal != 0);
-          } else {
-            VLOG(jdwp) << "failed to get boolean local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_BYTE: {
-          CHECK_EQ(width_, 1U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
-            JDWP::Set1(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get byte local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_SHORT:
-        case JDWP::JT_CHAR: {
-          CHECK_EQ(width_, 2U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
-            JDWP::Set2BE(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get short/char local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_INT: {
-          CHECK_EQ(width_, 4U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kIntVReg, &intVal)) {
-            VLOG(jdwp) << "get int local " << reg << " = " << intVal;
-            JDWP::Set4BE(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get int local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_FLOAT: {
-          CHECK_EQ(width_, 4U);
-          uint32_t intVal;
-          if (GetVReg(m, reg, kFloatVReg, &intVal)) {
-            VLOG(jdwp) << "get float local " << reg << " = " << intVal;
-            JDWP::Set4BE(buf_+1, intVal);
-          } else {
-            VLOG(jdwp) << "failed to get float local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_ARRAY:
-        case JDWP::JT_CLASS_LOADER:
-        case JDWP::JT_CLASS_OBJECT:
-        case JDWP::JT_OBJECT:
-        case JDWP::JT_STRING:
-        case JDWP::JT_THREAD:
-        case JDWP::JT_THREAD_GROUP: {
-          CHECK_EQ(width_, sizeof(JDWP::ObjectId));
-          uint32_t intVal;
-          if (GetVReg(m, reg, kReferenceVReg, &intVal)) {
-            mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
-            VLOG(jdwp) << "get " << tag_ << " object local " << reg << " = " << o;
-            if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
-              LOG(FATAL) << "Register " << reg << " expected to hold " << tag_ << " object: " << o;
-            }
-            tag_ = TagFromObject(soa_, o);
-            JDWP::SetObjectId(buf_+1, gRegistry->Add(o));
-          } else {
-            VLOG(jdwp) << "failed to get " << tag_ << " object local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_DOUBLE: {
-          CHECK_EQ(width_, 8U);
-          uint64_t longVal;
-          if (GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
-            VLOG(jdwp) << "get double local " << reg << " = " << longVal;
-            JDWP::Set8BE(buf_+1, longVal);
-          } else {
-            VLOG(jdwp) << "failed to get double local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_LONG: {
-          CHECK_EQ(width_, 8U);
-          uint64_t longVal;
-          if (GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) {
-            VLOG(jdwp) << "get long local " << reg << " = " << longVal;
-            JDWP::Set8BE(buf_+1, longVal);
-          } else {
-            VLOG(jdwp) << "failed to get long local " << reg;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        default:
-          LOG(FATAL) << "Unknown tag " << tag_;
-          break;
-      }
-
-      // Prepend tag, which may have been updated.
-      JDWP::Set1(buf_, tag_);
-      return false;
+  // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
+  // annotalysis.
+  bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+    if (GetFrameId() != frame_id_) {
+      return true;  // Not our frame, carry on.
     }
-    const ScopedObjectAccessUnchecked& soa_;
-    const JDWP::FrameId frame_id_;
-    const int slot_;
-    JDWP::JdwpTag tag_;
-    uint8_t* const buf_;
-    const size_t width_;
-    JDWP::JdwpError error_;
-  };
+    mirror::ArtMethod* m = GetMethod();
+    if (m->IsNative()) {
+      // We can't read/write local value from/into native method.
+      error_ = JDWP::ERR_OPAQUE_FRAME;
+    } else {
+      // We found our frame.
+      error_ = JDWP::ERR_NONE;
+    }
+    return false;
+  }
+
+  JDWP::JdwpError GetError() const {
+    return error_;
+  }
+
+ private:
+  const JDWP::FrameId frame_id_;
+  JDWP::JdwpError error_;
+};
+
+JDWP::JdwpError Dbg::GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pReply) {
+  JDWP::ObjectId thread_id = request->ReadThreadId();
+  JDWP::FrameId frame_id = request->ReadFrameId();
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
-  if (error != JDWP::ERR_NONE) {
-    return error;
+  {
+    MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+    JDWP::JdwpError error;
+    thread = DecodeThread(soa, thread_id, &error);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
   }
-  // TODO check thread is suspended by the debugger ?
+  // Find the frame with the given frame_id.
   std::unique_ptr<Context> context(Context::Create());
-  GetLocalVisitor visitor(soa, thread, context.get(), frame_id, slot, tag, buf, width);
+  FindFrameVisitor visitor(thread, context.get(), frame_id);
   visitor.WalkStack();
-  return visitor.error_;
+  if (visitor.GetError() != JDWP::ERR_NONE) {
+    return visitor.GetError();
+  }
+
+  // Read the values from visitor's context.
+  int32_t slot_count = request->ReadSigned32("slot count");
+  expandBufAdd4BE(pReply, slot_count);     /* "int values" */
+  for (int32_t i = 0; i < slot_count; ++i) {
+    uint32_t slot = request->ReadUnsigned32("slot");
+    JDWP::JdwpTag reqSigByte = request->ReadTag();
+
+    VLOG(jdwp) << "    --> slot " << slot << " " << reqSigByte;
+
+    size_t width = Dbg::GetTagWidth(reqSigByte);
+    uint8_t* ptr = expandBufAddSpace(pReply, width+1);
+    JDWP::JdwpError error = Dbg::GetLocalValue(visitor, soa, slot, reqSigByte, ptr, width);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
+  }
+  return JDWP::ERR_NONE;
 }
 
-JDWP::JdwpError Dbg::SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                   JDWP::JdwpTag tag, uint64_t value, size_t width) {
-  struct SetLocalVisitor : public StackVisitor {
-    SetLocalVisitor(Thread* thread, Context* context,
-                    JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint64_t value,
-                    size_t width)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, context),
-          frame_id_(frame_id), slot_(slot), tag_(tag), value_(value), width_(width),
-          error_(JDWP::ERR_NONE) {}
-
-    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
-    // annotalysis.
-    bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
-      if (GetFrameId() != frame_id_) {
-        return true;  // Not our frame, carry on.
+JDWP::JdwpError Dbg::GetLocalValue(const StackVisitor& visitor, ScopedObjectAccessUnchecked& soa,
+                                   int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
+  mirror::ArtMethod* m = visitor.GetMethod();
+  uint16_t reg = DemangleSlot(slot, m);
+  // TODO: check that the tag is compatible with the actual type of the slot!
+  // TODO: check slot is valid for this method or return INVALID_SLOT error.
+  constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
+  switch (tag) {
+    case JDWP::JT_BOOLEAN: {
+      CHECK_EQ(width, 1U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
+        JDWP::Set1(buf + 1, intVal != 0);
+      } else {
+        VLOG(jdwp) << "failed to get boolean local " << reg;
+        return kFailureErrorCode;
       }
-      // TODO: check that the tag is compatible with the actual type of the slot!
-      // TODO: check slot is valid for this method or return INVALID_SLOT error.
-      mirror::ArtMethod* m = GetMethod();
-      if (m->IsNative()) {
-        // We can't read local value from native method.
-        error_ = JDWP::ERR_OPAQUE_FRAME;
-        return false;
-      }
-      uint16_t reg = DemangleSlot(slot_, m);
-      constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
-      switch (tag_) {
-        case JDWP::JT_BOOLEAN:
-        case JDWP::JT_BYTE:
-          CHECK_EQ(width_, 1U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
-            VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_SHORT:
-        case JDWP::JT_CHAR:
-          CHECK_EQ(width_, 2U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
-            VLOG(jdwp) << "failed to set short/char local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_INT:
-          CHECK_EQ(width_, 4U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
-            VLOG(jdwp) << "failed to set int local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_FLOAT:
-          CHECK_EQ(width_, 4U);
-          if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kFloatVReg)) {
-            VLOG(jdwp) << "failed to set float local " << reg << " = "
-                       << static_cast<uint32_t>(value_);
-            error_ = kFailureErrorCode;
-          }
-          break;
-        case JDWP::JT_ARRAY:
-        case JDWP::JT_CLASS_LOADER:
-        case JDWP::JT_CLASS_OBJECT:
-        case JDWP::JT_OBJECT:
-        case JDWP::JT_STRING:
-        case JDWP::JT_THREAD:
-        case JDWP::JT_THREAD_GROUP: {
-          CHECK_EQ(width_, sizeof(JDWP::ObjectId));
-          mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value_));
-          if (o == ObjectRegistry::kInvalidObject) {
-            VLOG(jdwp) << tag_ << " object " << o << " is an invalid object";
-            error_ = JDWP::ERR_INVALID_OBJECT;
-          } else if (!SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
-                              kReferenceVReg)) {
-            VLOG(jdwp) << "failed to set " << tag_ << " object local " << reg << " = " << o;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_DOUBLE: {
-          CHECK_EQ(width_, 8U);
-          bool success = SetVRegPair(m, reg, value_, kDoubleLoVReg, kDoubleHiVReg);
-          if (!success) {
-            VLOG(jdwp) << "failed to set double local " << reg << " = " << value_;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        case JDWP::JT_LONG: {
-          CHECK_EQ(width_, 8U);
-          bool success = SetVRegPair(m, reg, value_, kLongLoVReg, kLongHiVReg);
-          if (!success) {
-            VLOG(jdwp) << "failed to set double local " << reg << " = " << value_;
-            error_ = kFailureErrorCode;
-          }
-          break;
-        }
-        default:
-          LOG(FATAL) << "Unknown tag " << tag_;
-          break;
-      }
-      return false;
+      break;
     }
+    case JDWP::JT_BYTE: {
+      CHECK_EQ(width, 1U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
+        JDWP::Set1(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get byte local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_SHORT:
+    case JDWP::JT_CHAR: {
+      CHECK_EQ(width, 2U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
+        JDWP::Set2BE(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get short/char local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_INT: {
+      CHECK_EQ(width, 4U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+        VLOG(jdwp) << "get int local " << reg << " = " << intVal;
+        JDWP::Set4BE(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get int local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_FLOAT: {
+      CHECK_EQ(width, 4U);
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kFloatVReg, &intVal)) {
+        VLOG(jdwp) << "get float local " << reg << " = " << intVal;
+        JDWP::Set4BE(buf + 1, intVal);
+      } else {
+        VLOG(jdwp) << "failed to get float local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_ARRAY:
+    case JDWP::JT_CLASS_LOADER:
+    case JDWP::JT_CLASS_OBJECT:
+    case JDWP::JT_OBJECT:
+    case JDWP::JT_STRING:
+    case JDWP::JT_THREAD:
+    case JDWP::JT_THREAD_GROUP: {
+      CHECK_EQ(width, sizeof(JDWP::ObjectId));
+      uint32_t intVal;
+      if (visitor.GetVReg(m, reg, kReferenceVReg, &intVal)) {
+        mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
+        VLOG(jdwp) << "get " << tag << " object local " << reg << " = " << o;
+        if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+          LOG(FATAL) << "Register " << reg << " expected to hold " << tag << " object: " << o;
+        }
+        tag = TagFromObject(soa, o);
+        JDWP::SetObjectId(buf + 1, gRegistry->Add(o));
+      } else {
+        VLOG(jdwp) << "failed to get " << tag << " object local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_DOUBLE: {
+      CHECK_EQ(width, 8U);
+      uint64_t longVal;
+      if (visitor.GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
+        VLOG(jdwp) << "get double local " << reg << " = " << longVal;
+        JDWP::Set8BE(buf + 1, longVal);
+      } else {
+        VLOG(jdwp) << "failed to get double local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_LONG: {
+      CHECK_EQ(width, 8U);
+      uint64_t longVal;
+      if (visitor.GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) {
+        VLOG(jdwp) << "get long local " << reg << " = " << longVal;
+        JDWP::Set8BE(buf + 1, longVal);
+      } else {
+        VLOG(jdwp) << "failed to get long local " << reg;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unknown tag " << tag;
+      break;
+  }
 
-    const JDWP::FrameId frame_id_;
-    const int slot_;
-    const JDWP::JdwpTag tag_;
-    const uint64_t value_;
-    const size_t width_;
-    JDWP::JdwpError error_;
-  };
+  // Prepend tag, which may have been updated.
+  JDWP::Set1(buf, tag);
+  return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::SetLocalValues(JDWP::Request* request) {
+  JDWP::ObjectId thread_id = request->ReadThreadId();
+  JDWP::FrameId frame_id = request->ReadFrameId();
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
-  if (error != JDWP::ERR_NONE) {
-    return error;
+  {
+    MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+    JDWP::JdwpError error;
+    thread = DecodeThread(soa, thread_id, &error);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
   }
-  // TODO check thread is suspended by the debugger ?
+  // Find the frame with the given frame_id.
   std::unique_ptr<Context> context(Context::Create());
-  SetLocalVisitor visitor(thread, context.get(), frame_id, slot, tag, value, width);
+  FindFrameVisitor visitor(thread, context.get(), frame_id);
   visitor.WalkStack();
-  return visitor.error_;
+  if (visitor.GetError() != JDWP::ERR_NONE) {
+    return visitor.GetError();
+  }
+
+  // Writes the values into visitor's context.
+  int32_t slot_count = request->ReadSigned32("slot count");
+  for (int32_t i = 0; i < slot_count; ++i) {
+    uint32_t slot = request->ReadUnsigned32("slot");
+    JDWP::JdwpTag sigByte = request->ReadTag();
+    size_t width = Dbg::GetTagWidth(sigByte);
+    uint64_t value = request->ReadValue(width);
+
+    VLOG(jdwp) << "    --> slot " << slot << " " << sigByte << " " << value;
+    JDWP::JdwpError error = Dbg::SetLocalValue(visitor, slot, sigByte, value, width);
+    if (error != JDWP::ERR_NONE) {
+      return error;
+    }
+  }
+  return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag,
+                                   uint64_t value, size_t width) {
+  mirror::ArtMethod* m = visitor.GetMethod();
+  uint16_t reg = DemangleSlot(slot, m);
+  // TODO: check that the tag is compatible with the actual type of the slot!
+  // TODO: check slot is valid for this method or return INVALID_SLOT error.
+  constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
+  switch (tag) {
+    case JDWP::JT_BOOLEAN:
+    case JDWP::JT_BYTE:
+      CHECK_EQ(width, 1U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+        VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_SHORT:
+    case JDWP::JT_CHAR:
+      CHECK_EQ(width, 2U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+        VLOG(jdwp) << "failed to set short/char local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_INT:
+      CHECK_EQ(width, 4U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+        VLOG(jdwp) << "failed to set int local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_FLOAT:
+      CHECK_EQ(width, 4U);
+      if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kFloatVReg)) {
+        VLOG(jdwp) << "failed to set float local " << reg << " = "
+                   << static_cast<uint32_t>(value);
+        return kFailureErrorCode;
+      }
+      break;
+    case JDWP::JT_ARRAY:
+    case JDWP::JT_CLASS_LOADER:
+    case JDWP::JT_CLASS_OBJECT:
+    case JDWP::JT_OBJECT:
+    case JDWP::JT_STRING:
+    case JDWP::JT_THREAD:
+    case JDWP::JT_THREAD_GROUP: {
+      CHECK_EQ(width, sizeof(JDWP::ObjectId));
+      JDWP::JdwpError error;
+      mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value),
+                                                          &error);
+      if (error != JDWP::ERR_NONE) {
+        VLOG(jdwp) << tag << " object " << o << " is an invalid object";
+        return JDWP::ERR_INVALID_OBJECT;
+      } else if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
+                          kReferenceVReg)) {
+        VLOG(jdwp) << "failed to set " << tag << " object local " << reg << " = " << o;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_DOUBLE: {
+      CHECK_EQ(width, 8U);
+      if (!visitor.SetVRegPair(m, reg, value, kDoubleLoVReg, kDoubleHiVReg)) {
+        VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    case JDWP::JT_LONG: {
+      CHECK_EQ(width, 8U);
+      if (!visitor.SetVRegPair(m, reg, value, kLongLoVReg, kLongHiVReg)) {
+        VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
+        return kFailureErrorCode;
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unknown tag " << tag;
+      break;
+  }
+  return JDWP::ERR_NONE;
 }
 
 static void SetEventLocation(JDWP::EventLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
@@ -3055,12 +3167,13 @@
   }
   // Note: method verifier may cause thread suspension.
   self->AssertThreadSuspensionIsAllowable();
-  StackHandleScope<2> hs(self);
+  StackHandleScope<3> hs(self);
   mirror::Class* declaring_class = m->GetDeclaringClass();
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
-  verifier::MethodVerifier verifier(dex_cache->GetDexFile(), &dex_cache, &class_loader,
-                                    &m->GetClassDef(), code_item, m->GetDexMethodIndex(), m,
+  Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+  verifier::MethodVerifier verifier(self, dex_cache->GetDexFile(), dex_cache, class_loader,
+                                    &m->GetClassDef(), code_item, m->GetDexMethodIndex(), method,
                                     m->GetAccessFlags(), false, true, false);
   // Note: we don't need to verify the method.
   return InlineMethodAnalyser::AnalyseMethodCode(&verifier, nullptr);
@@ -3192,7 +3305,7 @@
     ScopedObjectAccessUnchecked soa(self);
     {
       MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-      error_ = DecodeThread(soa, thread_id, thread_);
+      thread_ = DecodeThread(soa, thread_id, &error_);
     }
     if (error_ == JDWP::ERR_NONE) {
       if (thread_ == soa.Self()) {
@@ -3258,10 +3371,10 @@
     explicit SingleStepStackVisitor(Thread* thread, SingleStepControl* single_step_control,
                                     int32_t* line_number)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, NULL), single_step_control_(single_step_control),
+        : StackVisitor(thread, nullptr), single_step_control_(single_step_control),
           line_number_(line_number) {
       DCHECK_EQ(single_step_control_, thread->GetSingleStepControl());
-      single_step_control_->method = NULL;
+      single_step_control_->method = nullptr;
       single_step_control_->stack_depth = 0;
     }
 
@@ -3271,11 +3384,11 @@
       mirror::ArtMethod* m = GetMethod();
       if (!m->IsRuntimeMethod()) {
         ++single_step_control_->stack_depth;
-        if (single_step_control_->method == NULL) {
+        if (single_step_control_->method == nullptr) {
           mirror::DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache();
           single_step_control_->method = m;
           *line_number_ = -1;
-          if (dex_cache != NULL) {
+          if (dex_cache != nullptr) {
             const DexFile& dex_file = *dex_cache->GetDexFile();
             *line_number_ = dex_file.GetLineNumFromPC(m, GetDexPc());
           }
@@ -3348,7 +3461,7 @@
     const DexFile::CodeItem* const code_item = m->GetCodeItem();
     DebugCallbackContext context(single_step_control, line_number, code_item);
     m->GetDexFile()->DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(),
-                                     DebugCallbackContext::Callback, NULL, &context);
+                                     DebugCallbackContext::Callback, nullptr, &context);
   }
 
   //
@@ -3378,8 +3491,8 @@
 void Dbg::UnconfigureStep(JDWP::ObjectId thread_id) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread;
-  JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+  JDWP::JdwpError error;
+  Thread* thread = DecodeThread(soa, thread_id, &error);
   if (error == JDWP::ERR_NONE) {
     SingleStepControl* single_step_control = thread->GetSingleStepControl();
     DCHECK(single_step_control != nullptr);
@@ -3423,13 +3536,14 @@
                                   JDWP::ObjectId* pExceptionId) {
   ThreadList* thread_list = Runtime::Current()->GetThreadList();
 
-  Thread* targetThread = NULL;
-  DebugInvokeReq* req = NULL;
+  Thread* targetThread = nullptr;
+  DebugInvokeReq* req = nullptr;
   Thread* self = Thread::Current();
   {
     ScopedObjectAccessUnchecked soa(self);
     MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-    JDWP::JdwpError error = DecodeThread(soa, thread_id, targetThread);
+    JDWP::JdwpError error;
+    targetThread = DecodeThread(soa, thread_id, &error);
     if (error != JDWP::ERR_NONE) {
       LOG(ERROR) << "InvokeMethod request for invalid thread id " << thread_id;
       return error;
@@ -3464,25 +3578,24 @@
       return JDWP::ERR_THREAD_SUSPENDED;  // Probably not expected here.
     }
 
-    JDWP::JdwpError status;
-    mirror::Object* receiver = gRegistry->Get<mirror::Object*>(object_id);
-    if (receiver == ObjectRegistry::kInvalidObject) {
+    mirror::Object* receiver = gRegistry->Get<mirror::Object*>(object_id, &error);
+    if (error != JDWP::ERR_NONE) {
       return JDWP::ERR_INVALID_OBJECT;
     }
 
-    mirror::Object* thread = gRegistry->Get<mirror::Object*>(thread_id);
-    if (thread == ObjectRegistry::kInvalidObject) {
+    mirror::Object* thread = gRegistry->Get<mirror::Object*>(thread_id, &error);
+    if (error != JDWP::ERR_NONE) {
       return JDWP::ERR_INVALID_OBJECT;
     }
     // TODO: check that 'thread' is actually a java.lang.Thread!
 
-    mirror::Class* c = DecodeClass(class_id, status);
-    if (c == NULL) {
-      return status;
+    mirror::Class* c = DecodeClass(class_id, &error);
+    if (c == nullptr) {
+      return error;
     }
 
     mirror::ArtMethod* m = FromMethodId(method_id);
-    if (m->IsStatic() != (receiver == NULL)) {
+    if (m->IsStatic() != (receiver == nullptr)) {
       return JDWP::ERR_INVALID_METHODID;
     }
     if (m->IsStatic()) {
@@ -3516,11 +3629,11 @@
         if (shorty[i + 1] == 'L') {
           // Did we really get an argument of an appropriate reference type?
           mirror::Class* parameter_type = mh.GetClassFromTypeIdx(types->GetTypeItem(i).type_idx_);
-          mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i]);
-          if (argument == ObjectRegistry::kInvalidObject) {
+          mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
+          if (error != JDWP::ERR_NONE) {
             return JDWP::ERR_INVALID_OBJECT;
           }
-          if (argument != NULL && !argument->InstanceOf(parameter_type)) {
+          if (argument != nullptr && !argument->InstanceOf(parameter_type)) {
             return JDWP::ERR_ILLEGAL_ARGUMENT;
           }
 
@@ -3630,8 +3743,8 @@
   }
 
   // Translate the method through the vtable, unless the debugger wants to suppress it.
-  Handle<mirror::ArtMethod> m(hs.NewHandle(pReq->method));
-  if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != NULL) {
+  MutableHandle<mirror::ArtMethod> m(hs.NewHandle(pReq->method));
+  if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != nullptr) {
     mirror::ArtMethod* actual_method = pReq->klass->FindVirtualMethodForVirtualOrInterface(m.Get());
     if (actual_method != m.Get()) {
       VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m.Get()) << " to " << PrettyMethod(actual_method);
@@ -3648,7 +3761,7 @@
   pReq->result_value = InvokeWithJValues(soa, pReq->receiver, soa.EncodeMethod(m.Get()),
                                          reinterpret_cast<jvalue*>(pReq->arg_values));
 
-  mirror::Throwable* exception = soa.Self()->GetException(NULL);
+  mirror::Throwable* exception = soa.Self()->GetException(nullptr);
   soa.Self()->ClearException();
   pReq->exception = gRegistry->Add(exception);
   pReq->result_tag = BasicTagFromDescriptor(m.Get()->GetShorty());
@@ -3676,7 +3789,7 @@
     gRegistry->Add(pReq->result_value.GetL());
   }
 
-  if (old_exception.Get() != NULL) {
+  if (old_exception.Get() != nullptr) {
     ThrowLocation gc_safe_throw_location(old_throw_this_object.Get(), old_throw_method.Get(),
                                          old_throw_dex_pc);
     soa.Self()->SetException(gc_safe_throw_location, old_exception.Get());
@@ -3695,23 +3808,24 @@
  * OLD-TODO: we currently assume that the request and reply include a single
  * chunk.  If this becomes inconvenient we will need to adapt.
  */
-bool Dbg::DdmHandlePacket(JDWP::Request& request, uint8_t** pReplyBuf, int* pReplyLen) {
+bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) {
   Thread* self = Thread::Current();
   JNIEnv* env = self->GetJniEnv();
 
-  uint32_t type = request.ReadUnsigned32("type");
-  uint32_t length = request.ReadUnsigned32("length");
+  uint32_t type = request->ReadUnsigned32("type");
+  uint32_t length = request->ReadUnsigned32("length");
 
   // Create a byte[] corresponding to 'request'.
-  size_t request_length = request.size();
+  size_t request_length = request->size();
   ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(request_length));
-  if (dataArray.get() == NULL) {
+  if (dataArray.get() == nullptr) {
     LOG(WARNING) << "byte[] allocation failed: " << request_length;
     env->ExceptionClear();
     return false;
   }
-  env->SetByteArrayRegion(dataArray.get(), 0, request_length, reinterpret_cast<const jbyte*>(request.data()));
-  request.Skip(request_length);
+  env->SetByteArrayRegion(dataArray.get(), 0, request_length,
+                          reinterpret_cast<const jbyte*>(request->data()));
+  request->Skip(request_length);
 
   // Run through and find all chunks.  [Currently just find the first.]
   ScopedByteArrayRO contents(env, dataArray.get());
@@ -3731,7 +3845,7 @@
     return false;
   }
 
-  if (chunk.get() == NULL) {
+  if (chunk.get() == nullptr) {
     return false;
   }
 
@@ -3753,13 +3867,13 @@
   type = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type);
 
   VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length);
-  if (length == 0 || replyData.get() == NULL) {
+  if (length == 0 || replyData.get() == nullptr) {
     return false;
   }
 
   const int kChunkHdrLen = 8;
   uint8_t* reply = new uint8_t[length + kChunkHdrLen];
-  if (reply == NULL) {
+  if (reply == nullptr) {
     LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
     return false;
   }
@@ -3824,8 +3938,8 @@
     ScopedObjectAccessUnchecked soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::String> name(hs.NewHandle(t->GetThreadName(soa)));
-    size_t char_count = (name.Get() != NULL) ? name->GetLength() : 0;
-    const jchar* chars = (name.Get() != NULL) ? name->GetCharArray()->GetData() : NULL;
+    size_t char_count = (name.Get() != nullptr) ? name->GetLength() : 0;
+    const jchar* chars = (name.Get() != nullptr) ? name->GetCharArray()->GetData() : nullptr;
 
     std::vector<uint8_t> bytes;
     JDWP::Append4BE(bytes, t->GetThreadId());
@@ -3875,7 +3989,7 @@
 }
 
 void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
-  CHECK(buf != NULL);
+  CHECK(buf != nullptr);
   iovec vec[1];
   vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
   vec[0].iov_len = byte_count;
@@ -3887,7 +4001,7 @@
 }
 
 void Dbg::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) {
-  if (gJdwpState == NULL) {
+  if (gJdwpState == nullptr) {
     VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type;
   } else {
     gJdwpState->DdmSendChunkV(type, iov, iov_count);
@@ -4044,7 +4158,7 @@
   }
 
   void Flush() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (pieceLenField_ == NULL) {
+    if (pieceLenField_ == nullptr) {
       // Flush immediately post Reset (maybe back-to-back Flush). Ignore.
       CHECK(needHeader_);
       return;
@@ -4072,7 +4186,7 @@
     ResetStartOfNextChunk();
     totalAllocationUnits_ = 0;
     needHeader_ = true;
-    pieceLenField_ = NULL;
+    pieceLenField_ = nullptr;
   }
 
   void HeapChunkCallback(void* start, void* /*end*/, size_t used_bytes)
@@ -4081,9 +4195,9 @@
     // Note: heap call backs cannot manipulate the heap upon which they are crawling, care is taken
     // in the following code not to allocate memory, by ensuring buf_ is of the correct size
     if (used_bytes == 0) {
-        if (start == NULL) {
+        if (start == nullptr) {
             // Reset for start of new heap.
-            startOfNextMemoryChunk_ = NULL;
+            startOfNextMemoryChunk_ = nullptr;
             Flush();
         }
         // Only process in use memory so that free region information
@@ -4098,7 +4212,7 @@
 
     // TODO: I'm not sure using start of next chunk works well with multiple spaces. We shouldn't
     // count gaps inbetween spaces as free memory.
-    if (startOfNextMemoryChunk_ != NULL) {
+    if (startOfNextMemoryChunk_ != nullptr) {
         // Transmit any pending free memory. Native free memory of
         // over kMaxFreeLen could be because of the use of mmaps, so
         // don't report. If not free memory then start a new segment.
@@ -4114,7 +4228,7 @@
             }
         }
         if (flush) {
-            startOfNextMemoryChunk_ = NULL;
+            startOfNextMemoryChunk_ = nullptr;
             Flush();
         }
     }
@@ -4160,7 +4274,7 @@
 
   uint8_t ExamineObject(mirror::Object* o, bool is_native_heap)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
-    if (o == NULL) {
+    if (o == nullptr) {
       return HPSG_STATE(SOLIDITY_FREE, 0);
     }
 
@@ -4177,7 +4291,7 @@
     }
 
     mirror::Class* c = o->GetClass();
-    if (c == NULL) {
+    if (c == nullptr) {
       // The object was probably just created but hasn't been initialized yet.
       return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
     }
@@ -4327,7 +4441,7 @@
   if (enable) {
     {
       MutexLock mu(self, *Locks::alloc_tracker_lock_);
-      if (recent_allocation_records_ != NULL) {
+      if (recent_allocation_records_ != nullptr) {
         return;  // Already enabled, bail.
       }
       alloc_record_max_ = GetAllocTrackerMax();
@@ -4337,19 +4451,19 @@
       DCHECK_EQ(alloc_record_head_, 0U);
       DCHECK_EQ(alloc_record_count_, 0U);
       recent_allocation_records_ = new AllocRecord[alloc_record_max_];
-      CHECK(recent_allocation_records_ != NULL);
+      CHECK(recent_allocation_records_ != nullptr);
     }
     Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
   } else {
     {
       ScopedObjectAccess soa(self);  // For type_cache_.Clear();
       MutexLock mu(self, *Locks::alloc_tracker_lock_);
-      if (recent_allocation_records_ == NULL) {
+      if (recent_allocation_records_ == nullptr) {
         return;  // Already disabled, bail.
       }
       LOG(INFO) << "Disabling alloc tracker";
       delete[] recent_allocation_records_;
-      recent_allocation_records_ = NULL;
+      recent_allocation_records_ = nullptr;
       alloc_record_head_ = 0;
       alloc_record_count_ = 0;
       type_cache_.Clear();
@@ -4362,7 +4476,7 @@
 struct AllocRecordStackVisitor : public StackVisitor {
   AllocRecordStackVisitor(Thread* thread, AllocRecord* record)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(thread, NULL), record(record), depth(0) {}
+      : StackVisitor(thread, nullptr), record(record), depth(0) {}
 
   // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
   // annotalysis.
@@ -4391,12 +4505,9 @@
   size_t depth;
 };
 
-void Dbg::RecordAllocation(mirror::Class* type, size_t byte_count) {
-  Thread* self = Thread::Current();
-  CHECK(self != NULL);
-
+void Dbg::RecordAllocation(Thread* self, mirror::Class* type, size_t byte_count) {
   MutexLock mu(self, *Locks::alloc_tracker_lock_);
-  if (recent_allocation_records_ == NULL) {
+  if (recent_allocation_records_ == nullptr) {
     // In the process of shutting down recording, bail.
     return;
   }
@@ -4437,7 +4548,7 @@
 void Dbg::DumpRecentAllocations() {
   ScopedObjectAccess soa(Thread::Current());
   MutexLock mu(soa.Self(), *Locks::alloc_tracker_lock_);
-  if (recent_allocation_records_ == NULL) {
+  if (recent_allocation_records_ == nullptr) {
     LOG(INFO) << "Not recording tracked allocations";
     return;
   }
@@ -4458,7 +4569,7 @@
     for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
       AllocRecordStackTraceElement* stack_element = record->StackElement(stack_frame);
       mirror::ArtMethod* m = stack_element->Method();
-      if (m == NULL) {
+      if (m == nullptr) {
         break;
       }
       LOG(INFO) << "    " << PrettyMethod(m) << " line " << stack_element->LineNumber();
@@ -4587,7 +4698,7 @@
       class_names.Add(record->Type()->GetDescriptor(&temp));
       for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
         mirror::ArtMethod* m = record->StackElement(i)->Method();
-        if (m != NULL) {
+        if (m != nullptr) {
           class_names.Add(m->GetDeclaringClassDescriptor());
           method_names.Add(m->GetName());
           filenames.Add(GetMethodSourceFile(m));
@@ -4670,7 +4781,7 @@
   }
   JNIEnv* env = self->GetJniEnv();
   jbyteArray result = env->NewByteArray(bytes.size());
-  if (result != NULL) {
+  if (result != nullptr) {
     env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
   }
   return result;
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 5dc39d8..cb7adae 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -45,6 +45,7 @@
 class AllocRecord;
 class ObjectRegistry;
 class ScopedObjectAccessUnchecked;
+class StackVisitor;
 class Thread;
 class ThrowLocation;
 
@@ -254,9 +255,9 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static std::string GetClassName(mirror::Class* klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId& class_object_id)
+  static JDWP::JdwpError GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId* class_object_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId& superclass_id)
+  static JDWP::JdwpError GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId* superclass_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetClassLoader(JDWP::RefTypeId id, JDWP::ExpandBuf* pReply)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -264,38 +265,38 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetReflectedType(JDWP::RefTypeId class_id, JDWP::ExpandBuf* pReply)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static void GetClassList(std::vector<JDWP::RefTypeId>& classes)
+  static void GetClassList(std::vector<JDWP::RefTypeId>* classes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetClassInfo(JDWP::RefTypeId class_id, JDWP::JdwpTypeTag* pTypeTag,
                                       uint32_t* pStatus, std::string* pDescriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static void FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>& ids)
+  static void FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>* ids)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetSignature(JDWP::RefTypeId ref_type_id, std::string* signature)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError GetSourceFile(JDWP::RefTypeId ref_type_id, std::string& source_file)
+  static JDWP::JdwpError GetSourceFile(JDWP::RefTypeId ref_type_id, std::string* source_file)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError GetObjectTag(JDWP::ObjectId object_id, uint8_t& tag)
+  static JDWP::JdwpError GetObjectTag(JDWP::ObjectId object_id, uint8_t* tag)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static size_t GetTagWidth(JDWP::JdwpTag tag);
 
-  static JDWP::JdwpError GetArrayLength(JDWP::ObjectId array_id, int& length)
+  static JDWP::JdwpError GetArrayLength(JDWP::ObjectId array_id, int32_t* length)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError OutputArray(JDWP::ObjectId array_id, int offset, int count,
                                      JDWP::ExpandBuf* pReply)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError SetArrayElements(JDWP::ObjectId array_id, int offset, int count,
-                                          JDWP::Request& request)
+                                          JDWP::Request* request)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static JDWP::ObjectId CreateString(const std::string& str)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId& new_object)
+  static JDWP::JdwpError CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t length,
-                                           JDWP::ObjectId& new_array)
+                                           JDWP::ObjectId* new_array)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   //
@@ -324,12 +325,12 @@
   static JDWP::JdwpError GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* reply)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetOwnedMonitors(JDWP::ObjectId thread_id,
-                                          std::vector<JDWP::ObjectId>& monitors,
-                                          std::vector<uint32_t>& stack_depths)
+                                          std::vector<JDWP::ObjectId>* monitors,
+                                          std::vector<uint32_t>* stack_depths)
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetContendedMonitor(JDWP::ObjectId thread_id,
-                                             JDWP::ObjectId& contended_monitor)
+                                             JDWP::ObjectId* contended_monitor)
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -337,19 +338,19 @@
   // Heap.
   //
   static JDWP::JdwpError GetInstanceCounts(const std::vector<JDWP::RefTypeId>& class_ids,
-                                           std::vector<uint64_t>& counts)
+                                           std::vector<uint64_t>* counts)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetInstances(JDWP::RefTypeId class_id, int32_t max_count,
-                                      std::vector<JDWP::ObjectId>& instances)
+                                      std::vector<JDWP::ObjectId>* instances)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetReferringObjects(JDWP::ObjectId object_id, int32_t max_count,
-                                             std::vector<JDWP::ObjectId>& referring_objects)
+                                             std::vector<JDWP::ObjectId>* referring_objects)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError DisableCollection(JDWP::ObjectId object_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError EnableCollection(JDWP::ObjectId object_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError IsCollected(JDWP::ObjectId object_id, bool& is_collected)
+  static JDWP::JdwpError IsCollected(JDWP::ObjectId object_id, bool* is_collected)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void DisposeObject(JDWP::ObjectId object_id, uint32_t reference_count)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -381,7 +382,7 @@
                                JDWP::ExpandBuf* pReply)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::JdwpError GetBytecodes(JDWP::RefTypeId class_id, JDWP::MethodId method_id,
-                                      std::vector<uint8_t>& bytecodes)
+                                      std::vector<uint8_t>* bytecodes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static std::string GetFieldName(JDWP::FieldId field_id)
@@ -410,17 +411,23 @@
   /*
    * Thread, ThreadGroup, Frame
    */
-  static JDWP::JdwpError GetThreadName(JDWP::ObjectId thread_id, std::string& name)
+  static JDWP::JdwpError GetThreadName(JDWP::ObjectId thread_id, std::string* name)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_);
   static JDWP::JdwpError GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_);
-  static std::string GetThreadGroupName(JDWP::ObjectId thread_group_id);
-  static JDWP::ObjectId GetThreadGroupParent(JDWP::ObjectId thread_group_id)
+  static JDWP::JdwpError GetThreadGroupName(JDWP::ObjectId thread_group_id,
+                                            JDWP::ExpandBuf* pReply)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static JDWP::JdwpError GetThreadGroupParent(JDWP::ObjectId thread_group_id,
+                                              JDWP::ExpandBuf* pReply)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static JDWP::JdwpError GetThreadGroupChildren(JDWP::ObjectId thread_group_id,
+                                                JDWP::ExpandBuf* pReply)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static JDWP::ObjectId GetSystemThreadGroupId()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::ObjectId GetMainThreadGroupId();
 
   static JDWP::JdwpThreadStatus ToJdwpThreadStatus(ThreadState state);
   static JDWP::JdwpError GetThreadStatus(JDWP::ObjectId thread_id,
@@ -435,12 +442,11 @@
 
   // Fills 'thread_ids' with the threads in the given thread group. If thread_group_id == 0,
   // returns all threads.
-  static void GetThreads(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& thread_ids)
+  static void GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* thread_ids)
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static void GetChildThreadGroups(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& child_thread_group_ids);
 
-  static JDWP::JdwpError GetThreadFrameCount(JDWP::ObjectId thread_id, size_t& result)
+  static JDWP::JdwpError GetThreadFrameCount(JDWP::ObjectId thread_id, size_t* result)
       LOCKS_EXCLUDED(Locks::thread_list_lock_);
   static JDWP::JdwpError GetThreadFrames(JDWP::ObjectId thread_id, size_t start_frame,
                                          size_t frame_count, JDWP::ExpandBuf* buf)
@@ -470,12 +476,10 @@
       LOCKS_EXCLUDED(Locks::thread_list_lock_,
                      Locks::thread_suspend_count_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                       JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen)
+  static JDWP::JdwpError GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pReply)
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static JDWP::JdwpError SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
-                                       JDWP::JdwpTag tag, uint64_t value, size_t width)
+  static JDWP::JdwpError SetLocalValues(JDWP::Request* request)
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -566,7 +570,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void DdmSetThreadNotification(bool enable)
       LOCKS_EXCLUDED(Locks::thread_list_lock_);
-  static bool DdmHandlePacket(JDWP::Request& request, uint8_t** pReplyBuf, int* pReplyLen);
+  static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen);
   static void DdmConnected() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void DdmDisconnected() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes)
@@ -582,7 +586,7 @@
   /*
    * Recent allocation tracking support.
    */
-  static void RecordAllocation(mirror::Class* type, size_t byte_count)
+  static void RecordAllocation(Thread* self, mirror::Class* type, size_t byte_count)
       LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
@@ -636,6 +640,16 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
+  static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor,
+                                       ScopedObjectAccessUnchecked& soa, int slot,
+                                       JDWP::JdwpTag tag, uint8_t* buf, size_t width)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static JDWP::JdwpError SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag,
+                                       uint64_t value, size_t width)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void PostThreadStartOrStop(Thread*, uint32_t)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index c783ee4..6ef62c5 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -205,19 +205,20 @@
 
   const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
 
-  const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release(), error_msg);
-  if (dex_file == nullptr) {
+  std::unique_ptr<const DexFile> dex_file(OpenMemory(location, dex_header->checksum_, map.release(),
+                                                     error_msg));
+  if (dex_file.get() == nullptr) {
     *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location,
                               error_msg->c_str());
     return nullptr;
   }
 
-  if (verify && !DexFileVerifier::Verify(dex_file, dex_file->Begin(), dex_file->Size(), location,
-                                         error_msg)) {
+  if (verify && !DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
+                                         location, error_msg)) {
     return nullptr;
   }
 
-  return dex_file;
+  return dex_file.release();
 }
 
 const char* DexFile::kClassesDex = "classes.dex";
@@ -353,8 +354,7 @@
       proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
       class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
       find_class_def_misses_(0),
-      class_def_index_(nullptr),
-      build_class_def_index_mutex_("DexFile index creation mutex") {
+      class_def_index_(nullptr) {
   CHECK(begin_ != NULL) << GetLocation();
   CHECK_GT(size_, 0U) << GetLocation();
 }
@@ -443,20 +443,21 @@
   // up. This isn't done eagerly at construction as construction is not performed in multi-threaded
   // sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing
   // out which thread builds the index.
-  find_class_def_misses_++;
   const uint32_t kMaxFailedDexClassDefLookups = 100;
-  if (find_class_def_misses_ > kMaxFailedDexClassDefLookups) {
-    MutexLock mu(Thread::Current(), build_class_def_index_mutex_);
-    // Are we the first ones building the index?
-    if (class_def_index_.LoadSequentiallyConsistent() == nullptr) {
-      index = new Index(num_class_defs);
-      for (uint32_t i = 0; i < num_class_defs;  ++i) {
-        const ClassDef& class_def = GetClassDef(i);
-        const char* descriptor = GetClassDescriptor(class_def);
-        index->insert(std::make_pair(descriptor, &class_def));
-      }
-      class_def_index_.StoreSequentiallyConsistent(index);
+  uint32_t old_misses = find_class_def_misses_.FetchAndAddSequentiallyConsistent(1);
+  if (old_misses == kMaxFailedDexClassDefLookups) {
+    // Are we the ones moving the miss count past the max? Sanity check the index doesn't exist.
+    CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
+    // Build the index.
+    index = new Index(num_class_defs);
+    for (uint32_t i = 0; i < num_class_defs;  ++i) {
+      const ClassDef& class_def = GetClassDef(i);
+      const char* descriptor = GetClassDescriptor(class_def);
+      index->insert(std::make_pair(descriptor, &class_def));
     }
+    // Sanity check the index still doesn't exist, only 1 thread should build it.
+    CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
+    class_def_index_.StoreSequentiallyConsistent(index);
   }
   return nullptr;
 }
@@ -1191,7 +1192,7 @@
 }
 
 template<bool kTransactionActive>
-void EncodedStaticFieldValueIterator::ReadValueToField(mirror::ArtField* field) const {
+void EncodedStaticFieldValueIterator::ReadValueToField(Handle<mirror::ArtField> field) const {
   switch (type_) {
     case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z); break;
     case kByte:    field->SetByte<kTransactionActive>(field->GetDeclaringClass(), jval_.b); break;
@@ -1218,8 +1219,8 @@
     default: UNIMPLEMENTED(FATAL) << ": type " << type_;
   }
 }
-template void EncodedStaticFieldValueIterator::ReadValueToField<true>(mirror::ArtField* field) const;
-template void EncodedStaticFieldValueIterator::ReadValueToField<false>(mirror::ArtField* field) const;
+template void EncodedStaticFieldValueIterator::ReadValueToField<true>(Handle<mirror::ArtField> field) const;
+template void EncodedStaticFieldValueIterator::ReadValueToField<false>(Handle<mirror::ArtField> field) const;
 
 CatchHandlerIterator::CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address) {
   handler_.address_ = -1;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 1306f11..c160253 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -997,7 +997,6 @@
   };
   typedef std::unordered_map<const char*, const ClassDef*, UTF16HashCmp, UTF16HashCmp> Index;
   mutable Atomic<Index*> class_def_index_;
-  mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 };
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
 
@@ -1250,7 +1249,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template<bool kTransactionActive>
-  void ReadValueToField(mirror::ArtField* field) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void ReadValueToField(Handle<mirror::ArtField> field) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool HasNext() { return pos_ < array_size_; }
 
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index b6810b0..b913220 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -190,7 +190,7 @@
   }
 
   // Reads an instruction out of the stream from the current address plus an offset.
-  const Instruction* RelativeAt(int32_t offset) const {
+  const Instruction* RelativeAt(int32_t offset) const WARN_UNUSED {
     return At(reinterpret_cast<const uint16_t*>(this) + offset);
   }
 
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index 80638ed..05214a4 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -253,10 +253,10 @@
   V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
   V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \
   V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \
-  V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xEE, UNUSED_EE, "unused-ee", k10x, false, kUnknown, 0, kVerifyError) \
+  V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+  V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+  V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+  V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
   V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0, kVerifyError) \
   V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0, kVerifyError) \
   V(0xF1, UNUSED_F1, "unused-f1", k10x, false, kUnknown, 0, kVerifyError) \
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0ab6394..65a557b 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1261,7 +1261,7 @@
   return nullptr;
 }
 
-struct PACKED(1) FDE {
+struct PACKED(1) FDE32 {
   uint32_t raw_length_;
   uint32_t GetLength() {
     return raw_length_ + sizeof(raw_length_);
@@ -1272,25 +1272,186 @@
   uint8_t instructions[0];
 };
 
-static FDE* NextFDE(FDE* frame) {
+static FDE32* NextFDE(FDE32* frame) {
   byte* fde_bytes = reinterpret_cast<byte*>(frame);
   fde_bytes += frame->GetLength();
-  return reinterpret_cast<FDE*>(fde_bytes);
+  return reinterpret_cast<FDE32*>(fde_bytes);
 }
 
-static bool IsFDE(FDE* frame) {
+static bool IsFDE(FDE32* frame) {
   return frame->CIE_pointer != 0;
 }
 
-// TODO This only works for 32-bit Elf Files.
-static bool FixupEHFrame(uintptr_t text_start, byte* eh_frame, size_t eh_frame_size) {
-  FDE* last_frame = reinterpret_cast<FDE*>(eh_frame + eh_frame_size);
-  FDE* frame = NextFDE(reinterpret_cast<FDE*>(eh_frame));
-  for (; frame < last_frame; frame = NextFDE(frame)) {
-    if (!IsFDE(frame)) {
+struct PACKED(1) FDE64 {
+  uint32_t raw_length_;
+  uint64_t extended_length_;
+  uint64_t GetLength() {
+    return extended_length_ + sizeof(raw_length_) + sizeof(extended_length_);
+  }
+  uint64_t CIE_pointer;
+  uint64_t initial_location;
+  uint64_t address_range;
+  uint8_t instructions[0];
+};
+
+static FDE64* NextFDE(FDE64* frame) {
+  byte* fde_bytes = reinterpret_cast<byte*>(frame);
+  fde_bytes += frame->GetLength();
+  return reinterpret_cast<FDE64*>(fde_bytes);
+}
+
+static bool IsFDE(FDE64* frame) {
+  return frame->CIE_pointer != 0;
+}
+
+static bool FixupEHFrame(off_t base_address_delta,
+                           byte* eh_frame, size_t eh_frame_size) {
+  if (*(reinterpret_cast<uint32_t*>(eh_frame)) == 0xffffffff) {
+    FDE64* last_frame = reinterpret_cast<FDE64*>(eh_frame + eh_frame_size);
+    FDE64* frame = NextFDE(reinterpret_cast<FDE64*>(eh_frame));
+    for (; frame < last_frame; frame = NextFDE(frame)) {
+      if (!IsFDE(frame)) {
+        return false;
+      }
+      frame->initial_location += base_address_delta;
+    }
+    return true;
+  } else {
+    FDE32* last_frame = reinterpret_cast<FDE32*>(eh_frame + eh_frame_size);
+    FDE32* frame = NextFDE(reinterpret_cast<FDE32*>(eh_frame));
+    for (; frame < last_frame; frame = NextFDE(frame)) {
+      if (!IsFDE(frame)) {
+        return false;
+      }
+      frame->initial_location += base_address_delta;
+    }
+    return true;
+  }
+}
+
+static uint8_t* NextLeb128(uint8_t* current) {
+  DecodeUnsignedLeb128(const_cast<const uint8_t**>(&current));
+  return current;
+}
+
+struct PACKED(1) DebugLineHeader {
+  uint32_t unit_length_;  // TODO 32-bit specific size
+  uint16_t version_;
+  uint32_t header_length_;  // TODO 32-bit specific size
+  uint8_t minimum_instruction_lenght_;
+  uint8_t maximum_operations_per_instruction_;
+  uint8_t default_is_stmt_;
+  int8_t line_base_;
+  uint8_t line_range_;
+  uint8_t opcode_base_;
+  uint8_t remaining_[0];
+
+  bool IsStandardOpcode(const uint8_t* op) const {
+    return *op != 0 && *op < opcode_base_;
+  }
+
+  bool IsExtendedOpcode(const uint8_t* op) const {
+    return *op == 0;
+  }
+
+  const uint8_t* GetStandardOpcodeLengths() const {
+    return remaining_;
+  }
+
+  uint8_t* GetNextOpcode(uint8_t* op) const {
+    if (IsExtendedOpcode(op)) {
+      uint8_t* length_field = op + 1;
+      uint32_t length = DecodeUnsignedLeb128(const_cast<const uint8_t**>(&length_field));
+      return length_field + length;
+    } else if (!IsStandardOpcode(op)) {
+      return op + 1;
+    } else if (*op == DW_LNS_fixed_advance_pc) {
+      return op + 1 + sizeof(uint16_t);
+    } else {
+      uint8_t num_args = GetStandardOpcodeLengths()[*op - 1];
+      op += 1;
+      for (int i = 0; i < num_args; i++) {
+        op = NextLeb128(op);
+      }
+      return op;
+    }
+  }
+
+  uint8_t* GetDebugLineData() const {
+    const uint8_t* hdr_start =
+        reinterpret_cast<const uint8_t*>(&header_length_) + sizeof(header_length_);
+    return const_cast<uint8_t*>(hdr_start + header_length_);
+  }
+};
+
+class DebugLineInstructionIterator {
+ public:
+  static DebugLineInstructionIterator* Create(DebugLineHeader* header, size_t section_size) {
+    std::unique_ptr<DebugLineInstructionIterator> line_iter(
+        new DebugLineInstructionIterator(header, section_size));
+    if (line_iter.get() == nullptr) {
+      return nullptr;
+    } else {
+      return line_iter.release();
+    }
+  }
+
+  ~DebugLineInstructionIterator() {}
+
+  bool Next() {
+    if (current_instruction_ == nullptr) {
       return false;
     }
-    frame->initial_location += text_start;
+    current_instruction_ = header_->GetNextOpcode(current_instruction_);
+    if (current_instruction_ >= last_instruction_) {
+      current_instruction_ = nullptr;
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  uint8_t* GetInstruction() {
+    return current_instruction_;
+  }
+
+  bool IsExtendedOpcode() {
+    return header_->IsExtendedOpcode(current_instruction_);
+  }
+
+  uint8_t GetOpcode() {
+    if (!IsExtendedOpcode()) {
+      return *current_instruction_;
+    } else {
+      uint8_t* len_ptr = current_instruction_ + 1;
+      return *NextLeb128(len_ptr);
+    }
+  }
+
+  uint8_t* GetArguments() {
+    if (!IsExtendedOpcode()) {
+      return current_instruction_ + 1;
+    } else {
+      uint8_t* len_ptr = current_instruction_ + 1;
+      return NextLeb128(len_ptr) + 1;
+    }
+  }
+
+ private:
+  DebugLineInstructionIterator(DebugLineHeader* header, size_t size)
+    : header_(header), last_instruction_(reinterpret_cast<uint8_t*>(header) + size),
+      current_instruction_(header->GetDebugLineData()) {}
+
+  DebugLineHeader* header_;
+  uint8_t* last_instruction_;
+  uint8_t* current_instruction_;
+};
+
+static bool FixupDebugLine(off_t base_offset_delta, DebugLineInstructionIterator* iter) {
+  while (iter->Next()) {
+    if (iter->IsExtendedOpcode() && iter->GetOpcode() == DW_LNE_set_address) {
+      *reinterpret_cast<uint32_t*>(iter->GetArguments()) += base_offset_delta;
+    }
   }
   return true;
 }
@@ -1430,20 +1591,29 @@
  public:
   ~DebugAbbrev() {}
   static DebugAbbrev* Create(const byte* dbg_abbrev, size_t dbg_abbrev_size) {
-    std::unique_ptr<DebugAbbrev> abbrev(new DebugAbbrev);
-    const byte* last = dbg_abbrev + dbg_abbrev_size;
-    while (dbg_abbrev < last) {
-      std::unique_ptr<DebugTag> tag(DebugTag::Create(&dbg_abbrev));
-      if (tag.get() == nullptr) {
-        return nullptr;
-      } else {
-        abbrev->tags_.insert(std::pair<uint32_t, uint32_t>(tag->index_, abbrev->tag_list_.size()));
-        abbrev->tag_list_.push_back(std::move(tag));
-      }
+    std::unique_ptr<DebugAbbrev> abbrev(new DebugAbbrev(dbg_abbrev, dbg_abbrev + dbg_abbrev_size));
+    if (!abbrev->ReadAtOffset(0)) {
+      return nullptr;
     }
     return abbrev.release();
   }
 
+  bool ReadAtOffset(uint32_t abbrev_offset) {
+    tags_.clear();
+    tag_list_.clear();
+    const byte* dbg_abbrev = begin_ + abbrev_offset;
+    while (dbg_abbrev < end_ && *dbg_abbrev != 0) {
+      std::unique_ptr<DebugTag> tag(DebugTag::Create(&dbg_abbrev));
+      if (tag.get() == nullptr) {
+        return false;
+      } else {
+        tags_.insert(std::pair<uint32_t, uint32_t>(tag->index_, tag_list_.size()));
+        tag_list_.push_back(std::move(tag));
+      }
+    }
+    return true;
+  }
+
   DebugTag* ReadTag(const byte* entry) {
     uint32_t tag_num = DecodeUnsignedLeb128(&entry);
     auto it = tags_.find(tag_num);
@@ -1456,7 +1626,9 @@
   }
 
  private:
-  DebugAbbrev() {}
+  DebugAbbrev(const byte* begin, const byte* end) : begin_(begin), end_(end) {}
+  const byte* begin_;
+  const byte* end_;
   std::map<uint32_t, uint32_t> tags_;
   std::vector<std::unique_ptr<DebugTag>> tag_list_;
 };
@@ -1480,11 +1652,21 @@
     if (current_entry_ == nullptr || current_tag_ == nullptr) {
       return false;
     }
+    bool reread_abbrev = false;
     current_entry_ += current_tag_->GetSize();
+    if (reinterpret_cast<DebugInfoHeader*>(current_entry_) >= next_cu_) {
+      current_cu_ = next_cu_;
+      next_cu_ = GetNextCu(current_cu_);
+      current_entry_ = reinterpret_cast<byte*>(current_cu_) + sizeof(DebugInfoHeader);
+      reread_abbrev = true;
+    }
     if (current_entry_ >= last_entry_) {
       current_entry_ = nullptr;
       return false;
     }
+    if (reread_abbrev) {
+      abbrev_->ReadAtOffset(current_cu_->debug_abbrev_offset);
+    }
     current_tag_ = abbrev_->ReadTag(current_entry_);
     if (current_tag_ == nullptr) {
       current_entry_ = nullptr;
@@ -1512,49 +1694,91 @@
   }
 
  private:
+  static DebugInfoHeader* GetNextCu(DebugInfoHeader* hdr) {
+    byte* hdr_byte = reinterpret_cast<byte*>(hdr);
+    return reinterpret_cast<DebugInfoHeader*>(hdr_byte + sizeof(uint32_t) + hdr->unit_length);
+  }
+
   DebugInfoIterator(DebugInfoHeader* header, size_t frame_size, DebugAbbrev* abbrev)
       : abbrev_(abbrev),
+        current_cu_(header),
+        next_cu_(GetNextCu(header)),
         last_entry_(reinterpret_cast<byte*>(header) + frame_size),
         current_entry_(reinterpret_cast<byte*>(header) + sizeof(DebugInfoHeader)),
         current_tag_(abbrev_->ReadTag(current_entry_)) {}
   DebugAbbrev* abbrev_;
+  DebugInfoHeader* current_cu_;
+  DebugInfoHeader* next_cu_;
   byte* last_entry_;
   byte* current_entry_;
   DebugTag* current_tag_;
 };
 
-static bool FixupDebugInfo(uint32_t text_start, DebugInfoIterator* iter) {
+static bool FixupDebugInfo(off_t base_address_delta, DebugInfoIterator* iter) {
   do {
     if (iter->GetCurrentTag()->GetAttrSize(DW_AT_low_pc) != sizeof(int32_t) ||
         iter->GetCurrentTag()->GetAttrSize(DW_AT_high_pc) != sizeof(int32_t)) {
+      LOG(ERROR) << "DWARF information with 64 bit pointers is not supported yet.";
       return false;
     }
     uint32_t* PC_low = reinterpret_cast<uint32_t*>(iter->GetPointerToField(DW_AT_low_pc));
     uint32_t* PC_high = reinterpret_cast<uint32_t*>(iter->GetPointerToField(DW_AT_high_pc));
     if (PC_low != nullptr && PC_high != nullptr) {
-      *PC_low  += text_start;
-      *PC_high += text_start;
+      *PC_low  += base_address_delta;
+      *PC_high += base_address_delta;
     }
   } while (iter->next());
   return true;
 }
 
-static bool FixupDebugSections(const byte* dbg_abbrev, size_t dbg_abbrev_size,
-                               uintptr_t text_start,
-                               byte* dbg_info, size_t dbg_info_size,
-                               byte* eh_frame, size_t eh_frame_size) {
-  std::unique_ptr<DebugAbbrev> abbrev(DebugAbbrev::Create(dbg_abbrev, dbg_abbrev_size));
+bool ElfFile::FixupDebugSections(off_t base_address_delta) {
+  const Elf32_Shdr* debug_info = FindSectionByName(".debug_info");
+  const Elf32_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
+  const Elf32_Shdr* eh_frame = FindSectionByName(".eh_frame");
+  const Elf32_Shdr* debug_str = FindSectionByName(".debug_str");
+  const Elf32_Shdr* debug_line = FindSectionByName(".debug_line");
+  const Elf32_Shdr* strtab_sec = FindSectionByName(".strtab");
+  const Elf32_Shdr* symtab_sec = FindSectionByName(".symtab");
+
+  if (debug_info == nullptr || debug_abbrev == nullptr ||
+      debug_str == nullptr || strtab_sec == nullptr || symtab_sec == nullptr) {
+    // Release version of ART does not generate debug info.
+    return true;
+  }
+  if (base_address_delta == 0) {
+    return true;
+  }
+  if (eh_frame != nullptr &&
+      !FixupEHFrame(base_address_delta, Begin() + eh_frame->sh_offset, eh_frame->sh_size)) {
+    return false;
+  }
+
+  std::unique_ptr<DebugAbbrev> abbrev(DebugAbbrev::Create(Begin() + debug_abbrev->sh_offset,
+                                                          debug_abbrev->sh_size));
   if (abbrev.get() == nullptr) {
     return false;
   }
-  std::unique_ptr<DebugInfoIterator> iter(
-      DebugInfoIterator::Create(reinterpret_cast<DebugInfoHeader*>(dbg_info),
-                                dbg_info_size, abbrev.get()));
-  if (iter.get() == nullptr) {
+  DebugInfoHeader* info_header =
+      reinterpret_cast<DebugInfoHeader*>(Begin() + debug_info->sh_offset);
+  std::unique_ptr<DebugInfoIterator> info_iter(DebugInfoIterator::Create(info_header,
+                                                                         debug_info->sh_size,
+                                                                         abbrev.get()));
+  if (info_iter.get() == nullptr) {
     return false;
   }
-  return FixupDebugInfo(text_start, iter.get())
-      && FixupEHFrame(text_start, eh_frame, eh_frame_size);
+  if (debug_line != nullptr) {
+    DebugLineHeader* line_header =
+        reinterpret_cast<DebugLineHeader*>(Begin() + debug_line->sh_offset);
+    std::unique_ptr<DebugLineInstructionIterator> line_iter(
+        DebugLineInstructionIterator::Create(line_header, debug_line->sh_size));
+    if (line_iter.get() == nullptr) {
+      return false;
+    }
+    if (!FixupDebugLine(base_address_delta, line_iter.get())) {
+      return false;
+    }
+  }
+  return FixupDebugInfo(base_address_delta, info_iter.get());
 }
 
 void ElfFile::GdbJITSupport() {
@@ -1572,19 +1796,13 @@
   }
   ElfFile& all = *all_ptr;
 
-  // Do we have interesting sections?
-  const Elf32_Shdr* debug_info = all.FindSectionByName(".debug_info");
-  const Elf32_Shdr* debug_abbrev = all.FindSectionByName(".debug_abbrev");
+  // We need the eh_frame for gdb but debug info might be present without it.
   const Elf32_Shdr* eh_frame = all.FindSectionByName(".eh_frame");
-  const Elf32_Shdr* debug_str = all.FindSectionByName(".debug_str");
-  const Elf32_Shdr* strtab_sec = all.FindSectionByName(".strtab");
-  const Elf32_Shdr* symtab_sec = all.FindSectionByName(".symtab");
-  Elf32_Shdr* text_sec = all.FindSectionByName(".text");
-  if (debug_info == nullptr || debug_abbrev == nullptr || eh_frame == nullptr ||
-      debug_str == nullptr || text_sec == nullptr || strtab_sec == nullptr ||
-      symtab_sec == nullptr) {
+  if (eh_frame == nullptr) {
     return;
   }
+
+  // Do we have interesting sections?
   // We need to add in a strtab and symtab to the image.
   // all is MAP_PRIVATE so it can be written to freely.
   // We also already have strtab and symtab so we are fine there.
@@ -1595,13 +1813,9 @@
   elf_hdr.e_phentsize = 0;
   elf_hdr.e_type = ET_EXEC;
 
-  text_sec->sh_type = SHT_NOBITS;
-  text_sec->sh_offset = 0;
-
-  if (!FixupDebugSections(
-        all.Begin() + debug_abbrev->sh_offset, debug_abbrev->sh_size, text_sec->sh_addr,
-        all.Begin() + debug_info->sh_offset, debug_info->sh_size,
-        all.Begin() + eh_frame->sh_offset, eh_frame->sh_size)) {
+  // Since base_address_ is 0 if we are actually loaded at a known address (i.e. this is boot.oat)
+  // and the actual address stuff starts at in regular files this is good.
+  if (!all.FixupDebugSections(reinterpret_cast<intptr_t>(base_address_))) {
     LOG(ERROR) << "Failed to load GDB data";
     return;
   }
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 985be76..916d693 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -108,6 +108,8 @@
   // executable is true at run time, false at compile time.
   bool Load(bool executable, std::string* error_msg);
 
+  bool FixupDebugSections(off_t base_address_delta);
+
  private:
   ElfFile(File* file, bool writable, bool program_header_only);
 
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index ccbedc0..9fb9a3b 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -37,6 +37,7 @@
 
 // TODO: Fix no thread safety analysis when GCC can handle template specialization.
 template <const bool kAccessCheck>
+ALWAYS_INLINE
 static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
                                               mirror::ArtMethod* method,
                                               Thread* self, bool* slow_path) {
@@ -78,7 +79,7 @@
     // has changed and to null-check the return value in case the
     // initialization fails.
     *slow_path = true;
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) {
       DCHECK(self->IsExceptionPending());
       return nullptr;  // Failure
     } else {
@@ -90,6 +91,7 @@
 }
 
 // TODO: Fix no thread safety analysis when annotalysis is smarter.
+ALWAYS_INLINE
 static inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass,
                                                                  Thread* self,
                                                                  bool* slow_path) {
@@ -105,7 +107,7 @@
     // has changed and to null-check the return value in case the
     // initialization fails.
     *slow_path = true;
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
       DCHECK(self->IsExceptionPending());
       return nullptr;  // Failure
     }
@@ -120,6 +122,7 @@
 // check.
 // TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
 template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE
 static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
                                                   mirror::ArtMethod* method,
                                                   Thread* self,
@@ -139,6 +142,7 @@
 // Given the context of a calling Method and a resolved class, create an instance.
 // TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
 template <bool kInstrumented>
+ALWAYS_INLINE
 static inline mirror::Object* AllocObjectFromCodeResolved(mirror::Class* klass,
                                                           mirror::ArtMethod* method,
                                                           Thread* self,
@@ -161,6 +165,7 @@
 // Given the context of a calling Method and an initialized class, create an instance.
 // TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
 template <bool kInstrumented>
+ALWAYS_INLINE
 static inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass,
                                                              mirror::ArtMethod* method,
                                                              Thread* self,
@@ -173,6 +178,7 @@
 
 // TODO: Fix no thread safety analysis when GCC can handle template specialization.
 template <bool kAccessCheck>
+ALWAYS_INLINE
 static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
                                              mirror::ArtMethod* method,
                                              int32_t component_count,
@@ -209,6 +215,7 @@
 // check.
 // TODO: Fix no thread safety analysis when GCC can handle template specialization.
 template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE
 static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
                                                 mirror::ArtMethod* method,
                                                 int32_t component_count,
@@ -223,14 +230,15 @@
     }
     gc::Heap* heap = Runtime::Current()->GetHeap();
     return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
-                                               klass->GetComponentSize(),
+                                               klass->GetComponentSizeShift(),
                                                heap->GetCurrentAllocator());
   }
   return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
-                                             klass->GetComponentSize(), allocator_type);
+                                             klass->GetComponentSizeShift(), allocator_type);
 }
 
 template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE
 static inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
                                                         mirror::ArtMethod* method,
                                                         int32_t component_count,
@@ -251,7 +259,7 @@
   // No need to retry a slow-path allocation as the above code won't cause a GC or thread
   // suspension.
   return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
-                                             klass->GetComponentSize(), allocator_type);
+                                             klass->GetComponentSizeShift(), allocator_type);
 }
 
 template<FindFieldType type, bool access_check>
@@ -316,7 +324,7 @@
     } else {
       StackHandleScope<1> hs(self);
       Handle<mirror::Class> h_class(hs.NewHandle(fields_class));
-      if (LIKELY(class_linker->EnsureInitialized(h_class, true, true))) {
+      if (LIKELY(class_linker->EnsureInitialized(self, h_class, true, true))) {
         // Otherwise let's ensure the class is initialized before resolving the field.
         return resolved_field;
       }
@@ -595,7 +603,7 @@
   }
   StackHandleScope<1> hs(self);
   Handle<mirror::Class> h_class(hs.NewHandle(klass));
-  if (!class_linker->EnsureInitialized(h_class, true, true)) {
+  if (!class_linker->EnsureInitialized(self, h_class, true, true)) {
     CHECK(self->IsExceptionPending());
     return nullptr;  // Failure - Indicate to caller to deliver exception
   }
@@ -632,18 +640,6 @@
   }
 }
 
-static inline void CheckSuspend(Thread* thread) {
-  for (;;) {
-    if (thread->ReadFlag(kCheckpointRequest)) {
-      thread->RunCheckpointFunction();
-    } else if (thread->ReadFlag(kSuspendRequest)) {
-      thread->FullSuspendCheck();
-    } else {
-      break;
-    }
-  }
-}
-
 template <typename INT_TYPE, typename FLOAT_TYPE>
 static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) {
   const INT_TYPE kMaxInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::max());
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 6ede6de..835d6e2 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -90,7 +90,8 @@
   gc::Heap* heap = Runtime::Current()->GetHeap();
   // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
   // the heap switched the allocator type while we were suspended.
-  return mirror::Array::Alloc<false>(self, klass, component_count, klass->GetComponentSize(),
+  return mirror::Array::Alloc<false>(self, klass, component_count,
+                                     klass->GetComponentSizeShift(),
                                      heap->GetCurrentAllocator());
 }
 
@@ -109,7 +110,8 @@
   gc::Heap* heap = Runtime::Current()->GetHeap();
   // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
   // the heap switched the allocator type while we were suspended.
-  return mirror::Array::Alloc<true>(self, klass, component_count, klass->GetComponentSize(),
+  return mirror::Array::Alloc<true>(self, klass, component_count,
+                                    klass->GetComponentSizeShift(),
                                     heap->GetCurrentAllocator());
 }
 
@@ -213,21 +215,20 @@
 }
 
 void CheckReferenceResult(mirror::Object* o, Thread* self) {
-  if (o == NULL) {
+  if (o == nullptr) {
     return;
   }
-  mirror::ArtMethod* m = self->GetCurrentMethod(NULL);
-  if (o == kInvalidIndirectRefObject) {
-    JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str());
-  }
+  mirror::ArtMethod* m = self->GetCurrentMethod(nullptr);
   // Make sure that the result is an instance of the type this method was expected to return.
   StackHandleScope<1> hs(self);
   Handle<mirror::ArtMethod> h_m(hs.NewHandle(m));
   mirror::Class* return_type = MethodHelper(h_m).GetReturnType();
 
   if (!o->InstanceOf(return_type)) {
-    JniAbortF(NULL, "attempt to return an instance of %s from %s", PrettyTypeOf(o).c_str(),
-              PrettyMethod(h_m.Get()).c_str());
+    Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
+                                               "attempt to return an instance of %s from %s",
+                                               PrettyTypeOf(o).c_str(),
+                                               PrettyMethod(h_m.Get()).c_str());
   }
 }
 
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index bc95c23..08edecf 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -174,8 +174,6 @@
 void CheckReferenceResult(mirror::Object* o, Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
 JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty,
                                     jobject rcvr_jobj, jobject interface_art_method_jobj,
                                     std::vector<jvalue>& args)
@@ -199,7 +197,6 @@
   return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit);
 }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
 extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
 static inline const void* GetPortableToInterpreterBridge() {
   return reinterpret_cast<void*>(art_portable_to_interpreter_bridge);
@@ -209,14 +206,12 @@
   // TODO: portable to quick bridge. Bug: 8196384
   return GetPortableToInterpreterBridge();
 }
-#endif
 
 extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
 static inline const void* GetQuickToInterpreterBridge() {
   return reinterpret_cast<void*>(art_quick_to_interpreter_bridge);
 }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
 static inline const void* GetQuickToPortableBridge() {
   // TODO: quick to portable bridge. Bug: 8196384
   return GetQuickToInterpreterBridge();
@@ -226,7 +221,6 @@
 static inline const void* GetPortableProxyInvokeHandler() {
   return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
 }
-#endif
 
 extern "C" void art_quick_proxy_invoke_handler();
 static inline const void* GetQuickProxyInvokeHandler() {
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index 64faf76..b617636 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -36,7 +36,8 @@
       self->PushShadowFrame(shadow_frame);
       StackHandleScope<1> hs(self);
       Handle<mirror::Class> h_class(hs.NewHandle(declaringClass));
-      if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true))) {
+      if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true,
+                                                                            true))) {
         self->PopShadowFrame();
         DCHECK(self->IsExceptionPending());
         return;
diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
index 16ce471..6f9c083 100644
--- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
@@ -37,11 +37,7 @@
     }
   }
   DCHECK(!self->IsExceptionPending());
-#if defined(ART_USE_PORTABLE_COMPILER)
   const void* code = method->GetEntryPointFromPortableCompiledCode();
-#else
-  const void* code = nullptr;
-#endif
 
   // When we return, the caller will branch to this address, so it had better not be 0!
   if (UNLIKELY(code == NULL)) {
diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
index 23e1c36..ecbc65e 100644
--- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method.h"
-#include "mirror/object-inl.h"
+#include "mirror/art_method-inl.h"
 #include "verifier/dex_gc_map.h"
 #include "stack.h"
+#include "thread-inl.h"
 
 namespace art {
 
@@ -71,7 +70,7 @@
 
 extern "C" void art_portable_test_suspend_from_code(Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  CheckSuspend(self);
+  self->CheckSuspend();
   if (Runtime::Current()->GetInstrumentation()->ShouldPortableCodeDeoptimize()) {
     // Save out the shadow frame to the heap
     ShadowFrameCopyVisitor visitor(self);
diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
index f90288b..7f6144b 100644
--- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
@@ -215,7 +215,7 @@
     if (method->IsStatic() && !method->GetDeclaringClass()->IsInitialized()) {
       // Ensure static method's class is initialized.
       Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
-      if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+      if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
         DCHECK(Thread::Current()->IsExceptionPending());
         self->PopManagedStackFragment(fragment);
         return 0;
@@ -399,41 +399,27 @@
     // Ensure that the called method's class is initialized.
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass()));
-    linker->EnsureInitialized(called_class, true, true);
+    linker->EnsureInitialized(self, called_class, true, true);
     if (LIKELY(called_class->IsInitialized())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
       code = called->GetEntryPointFromPortableCompiledCode();
-#else
-      code = nullptr;
-#endif
       // TODO: remove this after we solve the link issue.
       if (code == nullptr) {
-#if defined(ART_USE_PORTABLE_COMPILER)
         bool have_portable_code;
         code = linker->GetPortableOatCodeFor(called, &have_portable_code);
-#endif
       }
     } else if (called_class->IsInitializing()) {
       if (invoke_type == kStatic) {
         // Class is still initializing, go to oat and grab code (trampoline must be left in place
         // until class is initialized to stop races between threads).
-#if defined(ART_USE_PORTABLE_COMPILER)
         bool have_portable_code;
         code = linker->GetPortableOatCodeFor(called, &have_portable_code);
-#endif
       } else {
         // No trampoline for non-static methods.
-#if defined(ART_USE_PORTABLE_COMPILER)
         code = called->GetEntryPointFromPortableCompiledCode();
-#else
-        code = nullptr;
-#endif
         // TODO: remove this after we solve the link issue.
         if (code == nullptr) {
-#if defined(ART_USE_PORTABLE_COMPILER)
           bool have_portable_code;
           code = linker->GetPortableOatCodeFor(called, &have_portable_code);
-#endif
         }
       }
     } else {
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index e573d6d..e728f7d 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -18,7 +18,9 @@
 #define ART_RUNTIME_ENTRYPOINTS_QUICK_CALLEE_SAVE_FRAME_H_
 
 #include "base/mutex.h"
+#include "gc_root-inl.h"
 #include "instruction_set.h"
+#include "runtime-inl.h"
 #include "thread-inl.h"
 
 // Specific frame size code is in architecture-specific files. We include this to compile-time
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 1f2713a..d8da463 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -25,11 +25,34 @@
 
 namespace art {
 
+static constexpr bool kUseTlabFastPath = true;
+
 #define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR_INST(suffix, suffix2, instrumented_bool, allocator_type) \
 extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \
     uint32_t type_idx, mirror::ArtMethod* method, Thread* self, \
     StackReference<mirror::ArtMethod>* sp) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+  if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
+    mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx); \
+    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 (kUseBakerOrBrooksReadBarrier) { \
+          if (kUseBrooksReadBarrier) { \
+            obj->SetReadBarrierPointer(obj); \
+          } \
+          obj->AssertReadBarrierPointer(); \
+        } \
+        QuasiAtomic::ThreadFenceForConstructor(); \
+        return obj; \
+      } \
+    } \
+  } \
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
   return AllocObjectFromCode<false, instrumented_bool>(type_idx, method, self, allocator_type); \
 } \
@@ -37,6 +60,26 @@
     mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \
     StackReference<mirror::ArtMethod>* sp) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+  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 (kUseBakerOrBrooksReadBarrier) { \
+          if (kUseBrooksReadBarrier) { \
+            obj->SetReadBarrierPointer(obj); \
+          } \
+          obj->AssertReadBarrierPointer(); \
+        } \
+        QuasiAtomic::ThreadFenceForConstructor(); \
+        return obj; \
+      } \
+    } \
+  } \
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
   return AllocObjectFromCodeResolved<instrumented_bool>(klass, method, self, allocator_type); \
 } \
@@ -44,6 +87,24 @@
     mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \
     StackReference<mirror::ArtMethod>* sp) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+  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 (kUseBakerOrBrooksReadBarrier) { \
+        if (kUseBrooksReadBarrier) { \
+          obj->SetReadBarrierPointer(obj); \
+        } \
+        obj->AssertReadBarrierPointer(); \
+      } \
+      QuasiAtomic::ThreadFenceForConstructor(); \
+      return obj; \
+    } \
+  } \
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
   return AllocObjectFromCodeInitialized<instrumented_bool>(klass, method, self, allocator_type); \
 } \
@@ -155,10 +216,10 @@
 
 // Generate the entrypoint functions.
 #if !defined(__APPLE__) || !defined(__LP64__)
-GENERATE_ENTRYPOINTS(_dlmalloc);
-GENERATE_ENTRYPOINTS(_rosalloc);
-GENERATE_ENTRYPOINTS(_bump_pointer);
-GENERATE_ENTRYPOINTS(_tlab);
+GENERATE_ENTRYPOINTS(_dlmalloc)
+GENERATE_ENTRYPOINTS(_rosalloc)
+GENERATE_ENTRYPOINTS(_bump_pointer)
+GENERATE_ENTRYPOINTS(_tlab)
 #endif
 
 static bool entry_points_instrumented = false;
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.h b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
index 7fd3fe9..ec0aef5 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
@@ -17,15 +17,12 @@
 #ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ALLOC_ENTRYPOINTS_H_
 #define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ALLOC_ENTRYPOINTS_H_
 
-#include "gc/heap.h"
+#include "base/mutex.h"
+#include "gc/allocator_type.h"
 #include "quick_entrypoints.h"
 
 namespace art {
 
-namespace gc {
-enum AllocatorType;
-}  // namespace gc
-
 void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
 
 // Runtime shutdown lock is necessary to prevent races in thread initialization. When the thread is
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index f858743..fbc7913 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -38,12 +38,24 @@
   V(InitializeType, void*, uint32_t, void*) \
   V(ResolveString, void*, void*, uint32_t) \
 \
+  V(Set8Instance, int, uint32_t, void*, int8_t) \
+  V(Set8Static, int, uint32_t, int8_t) \
+  V(Set16Instance, int, uint32_t, void*, int16_t) \
+  V(Set16Static, int, uint32_t, int16_t) \
   V(Set32Instance, int, uint32_t, void*, int32_t) \
   V(Set32Static, int, uint32_t, int32_t) \
   V(Set64Instance, int, uint32_t, void*, int64_t) \
   V(Set64Static, int, uint32_t, int64_t) \
   V(SetObjInstance, int, uint32_t, void*, void*) \
   V(SetObjStatic, int, uint32_t, void*) \
+  V(GetByteInstance, int8_t, uint32_t, void*) \
+  V(GetBooleanInstance, uint8_t, uint32_t, void*) \
+  V(GetByteStatic, int8_t, uint32_t) \
+  V(GetBooleanStatic, uint8_t, uint32_t) \
+  V(GetShortInstance, int16_t, uint32_t, void*) \
+  V(GetCharInstance, uint16_t, uint32_t, void*) \
+  V(GetShortStatic, int16_t, uint32_t) \
+  V(GetCharStatic, uint16_t, uint32_t) \
   V(Get32Instance, int32_t, uint32_t, void*) \
   V(Get32Static, int32_t, uint32_t) \
   V(Get64Instance, int64_t, uint32_t, void*) \
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index cd1e247..b89c015 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -25,6 +25,74 @@
 
 namespace art {
 
+extern "C" int8_t artGetByteStaticFromCode(uint32_t field_idx,
+                                           mirror::ArtMethod* referrer,
+                                           Thread* self, StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetByte(field->GetDeclaringClass());
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetByte(field->GetDeclaringClass());
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint8_t artGetBooleanStaticFromCode(uint32_t field_idx,
+                                               mirror::ArtMethod* referrer,
+                                               Thread* self, StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetBoolean(field->GetDeclaringClass());
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetBoolean(field->GetDeclaringClass());
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" int16_t artGetShortStaticFromCode(uint32_t field_idx,
+                                             mirror::ArtMethod* referrer,
+                                             Thread* self, StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetShort(field->GetDeclaringClass());
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetShort(field->GetDeclaringClass());
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint16_t artGetCharStaticFromCode(uint32_t field_idx,
+                                             mirror::ArtMethod* referrer,
+                                             Thread* self, StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetChar(field->GetDeclaringClass());
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    return field->GetChar(field->GetDeclaringClass());
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
 extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx,
                                            mirror::ArtMethod* referrer,
                                            Thread* self, StackReference<mirror::ArtMethod>* sp)
@@ -78,6 +146,97 @@
   return NULL;  // Will throw exception by checking with Thread::Current
 }
 
+extern "C" int8_t artGetByteInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+                                             mirror::ArtMethod* referrer, Thread* self,
+                                             StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    return field->GetByte(obj);
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+    } else {
+      return field->GetByte(obj);
+    }
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint8_t artGetBooleanInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+                                                 mirror::ArtMethod* referrer, Thread* self,
+                                                 StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int8_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    return field->GetBoolean(obj);
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+    } else {
+      return field->GetBoolean(obj);
+    }
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+extern "C" int16_t artGetShortInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+                                               mirror::ArtMethod* referrer, Thread* self,
+                                               StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    return field->GetShort(obj);
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+    } else {
+      return field->GetShort(obj);
+    }
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint16_t artGetCharInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+                                               mirror::ArtMethod* referrer, Thread* self,
+                                               StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+                                          sizeof(int16_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    return field->GetChar(obj);
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+                                                         sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+    } else {
+      return field->GetChar(obj);
+    }
+  }
+  return 0;  // Will throw exception by checking with Thread::Current
+}
+
 extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
                                              mirror::ArtMethod* referrer, Thread* self,
                                              StackReference<mirror::ArtMethod>* sp)
@@ -148,6 +307,72 @@
   return NULL;  // Will throw exception by checking with Thread::Current
 }
 
+extern "C" int artSet8StaticFromCode(uint32_t field_idx, uint32_t new_value,
+                                     mirror::ArtMethod* referrer, Thread* self,
+                                     StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
+                                          sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    // Compiled code can't use transactional mode.
+    if (type == Primitive::kPrimBoolean) {
+      field->SetBoolean<false>(field->GetDeclaringClass(), new_value);
+    } else {
+      DCHECK_EQ(Primitive::kPrimByte, type);
+      field->SetByte<false>(field->GetDeclaringClass(), new_value);
+    }
+    return 0;  // success
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int8_t));
+  if (LIKELY(field != NULL)) {
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    // Compiled code can't use transactional mode.
+    if (type == Primitive::kPrimBoolean) {
+      field->SetBoolean<false>(field->GetDeclaringClass(), new_value);
+    } else {
+      DCHECK_EQ(Primitive::kPrimByte, type);
+      field->SetByte<false>(field->GetDeclaringClass(), new_value);
+    }
+    return 0;  // success
+  }
+  return -1;  // failure
+}
+
+extern "C" int artSet16StaticFromCode(uint32_t field_idx, uint16_t new_value,
+                                      mirror::ArtMethod* referrer, Thread* self,
+                                      StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
+                                          sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    // Compiled code can't use transactional mode.
+    if (type == Primitive::kPrimChar) {
+      field->SetChar<false>(field->GetDeclaringClass(), new_value);
+    } else {
+      DCHECK_EQ(Primitive::kPrimShort, type);
+      field->SetShort<false>(field->GetDeclaringClass(), new_value);
+    }
+    return 0;  // success
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int16_t));
+  if (LIKELY(field != NULL)) {
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    // Compiled code can't use transactional mode.
+    if (type == Primitive::kPrimChar) {
+      field->SetChar<false>(field->GetDeclaringClass(), new_value);
+    } else {
+      DCHECK_EQ(Primitive::kPrimShort, type);
+      field->SetShort<false>(field->GetDeclaringClass(), new_value);
+    }
+    return 0;  // success
+  }
+  return -1;  // failure
+}
+
 extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value,
                                       mirror::ArtMethod* referrer, Thread* self,
                                       StackReference<mirror::ArtMethod>* sp)
@@ -214,6 +439,91 @@
   return -1;  // failure
 }
 
+extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint8_t new_value,
+                                       mirror::ArtMethod* referrer, Thread* self,
+                                       StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
+                                          sizeof(int8_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    // Compiled code can't use transactional mode.
+    if (type == Primitive::kPrimBoolean) {
+      field->SetBoolean<false>(obj, new_value);
+    } else {
+      DCHECK_EQ(Primitive::kPrimByte, type);
+      field->SetByte<false>(obj, new_value);
+    }
+    return 0;  // success
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  {
+    StackHandleScope<1> hs(self);
+    HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
+    field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+                                                            sizeof(int8_t));
+  }
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+    } else {
+      Primitive::Type type = field->GetTypeAsPrimitiveType();
+      // Compiled code can't use transactional mode.
+      if (type == Primitive::kPrimBoolean) {
+        field->SetBoolean<false>(obj, new_value);
+      } else {
+        field->SetByte<false>(obj, new_value);
+      }
+      return 0;  // success
+    }
+  }
+  return -1;  // failure
+}
+
+extern "C" int artSet16InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint16_t new_value,
+                                        mirror::ArtMethod* referrer, Thread* self,
+                                        StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
+                                          sizeof(int16_t));
+  if (LIKELY(field != NULL && obj != NULL)) {
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    // Compiled code can't use transactional mode.
+    if (type == Primitive::kPrimChar) {
+      field->SetChar<false>(obj, new_value);
+    } else {
+      DCHECK_EQ(Primitive::kPrimShort, type);
+      field->SetShort<false>(obj, new_value);
+    }
+    return 0;  // success
+  }
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  {
+    StackHandleScope<1> hs(self);
+    HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
+    field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+                                                            sizeof(int16_t));
+  }
+  if (LIKELY(field != NULL)) {
+    if (UNLIKELY(obj == NULL)) {
+      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+    } else {
+      Primitive::Type type = field->GetTypeAsPrimitiveType();
+      // Compiled code can't use transactional mode.
+      if (type == Primitive::kPrimChar) {
+        field->SetChar<false>(obj, new_value);
+      } else {
+        DCHECK_EQ(Primitive::kPrimShort, type);
+        field->SetShort<false>(obj, new_value);
+      }
+      return 0;  // success
+    }
+  }
+  return -1;  // failure
+}
+
 extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint32_t new_value,
                                         mirror::ArtMethod* referrer, Thread* self,
                                         StackReference<mirror::ArtMethod>* sp)
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 6537249..c239535 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -14,15 +14,9 @@
  * limitations under the License.
  */
 
-#include "dex_file-inl.h"
 #include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object.h"
 #include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "scoped_thread_state_change.h"
-#include "thread.h"
+#include "thread-inl.h"
 #include "verify_object-inl.h"
 
 namespace art {
@@ -56,7 +50,7 @@
     // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there
     // is a flag raised.
     DCHECK(Locks::mutator_lock_->IsSharedHeld(self));
-    CheckSuspend(self);
+    self->CheckSuspend();
   }
 }
 
diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
index 118cd7f..ea75fb6 100644
--- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
@@ -15,17 +15,15 @@
  */
 
 #include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "thread.h"
-#include "thread_list.h"
+#include "thread-inl.h"
 
 namespace art {
 
-extern "C" void artTestSuspendFromCode(Thread* thread, StackReference<mirror::ArtMethod>* sp)
+extern "C" void artTestSuspendFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Called when suspend count check value is 0 and thread->suspend_count_ != 0
-  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsOnly);
-  CheckSuspend(thread);
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  self->CheckSuspend();
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index dfd2e11..1dbbb70 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -496,7 +496,7 @@
       // Ensure static method's class is initialized.
       StackHandleScope<1> hs(self);
       Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
-      if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+      if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
         DCHECK(Thread::Current()->IsExceptionPending()) << PrettyMethod(method);
         self->PopManagedStackFragment(fragment);
         return 0;
@@ -808,7 +808,7 @@
     // Ensure that the called method's class is initialized.
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass()));
-    linker->EnsureInitialized(called_class, true, true);
+    linker->EnsureInitialized(soa.Self(), called_class, true, true);
     if (LIKELY(called_class->IsInitialized())) {
       code = called->GetEntryPointFromQuickCompiledCode();
     } else if (called_class->IsInitializing()) {
@@ -1488,7 +1488,7 @@
       // Initialize padding entries.
       size_t expected_slots = handle_scope_->NumberOfReferences();
       while (cur_entry_ < expected_slots) {
-        handle_scope_->GetHandle(cur_entry_++).Assign(nullptr);
+        handle_scope_->GetMutableHandle(cur_entry_++).Assign(nullptr);
       }
       DCHECK_NE(cur_entry_, 0U);
     }
@@ -1509,7 +1509,7 @@
 
 uintptr_t BuildGenericJniFrameVisitor::FillJniCall::PushHandle(mirror::Object* ref) {
   uintptr_t tmp;
-  Handle<mirror::Object> h = handle_scope_->GetHandle(cur_entry_);
+  MutableHandle<mirror::Object> h = handle_scope_->GetMutableHandle(cur_entry_);
   h.Assign(ref);
   tmp = reinterpret_cast<uintptr_t>(h.ToJObject());
   cur_entry_++;
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index d205e2a..305e5a2 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -186,13 +186,25 @@
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType,
                          kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet32Instance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet16Instance, pSet16Static, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet16Static, pSet32Instance, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet32Instance, pSet32Static, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet32Static, pSet64Instance, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet64Instance, pSet64Static, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet64Static, pSetObjInstance, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjInstance, pSetObjStatic, kPointerSize);
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjStatic, pGet32Instance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjStatic, pGetByteInstance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetByteInstance, pGetBooleanInstance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetBooleanInstance, pGetByteStatic, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetByteStatic, pGetBooleanStatic, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetBooleanStatic, pGetShortInstance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetShortInstance, pGetCharInstance, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetCharInstance, pGetShortStatic, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetShortStatic, pGetCharStatic, kPointerSize);
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetCharStatic, pGet32Instance, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet32Instance, pGet32Static, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet32Static, pGet64Instance, kPointerSize);
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, kPointerSize);
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 99633a3..6033a5f 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -45,7 +45,7 @@
     my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader);
     ASSERT_TRUE(my_klass_ != NULL);
     Handle<mirror::Class> klass(hs.NewHandle(my_klass_));
-    class_linker_->EnsureInitialized(klass, true, true);
+    class_linker_->EnsureInitialized(soa.Self(), klass, true, true);
     my_klass_ = klass.Get();
 
     dex_ = my_klass_->GetDexCache()->GetDexFile();
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 25f87c5..fede2f8 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -19,6 +19,7 @@
 #include <setjmp.h>
 #include <sys/mman.h>
 #include <sys/ucontext.h>
+#include "base/stl_util.h"
 #include "mirror/art_method.h"
 #include "mirror/class.h"
 #include "sigchain.h"
@@ -115,13 +116,23 @@
   initialized_ = true;
 }
 
-void FaultManager::Shutdown() {
+void FaultManager::Release() {
   if (initialized_) {
     UnclaimSignalChain(SIGSEGV);
     initialized_ = false;
   }
 }
 
+void FaultManager::Shutdown() {
+  if (initialized_) {
+    Release();
+
+    // Free all handlers.
+    STLDeleteElements(&generated_code_handlers_);
+    STLDeleteElements(&other_handlers_);
+  }
+}
+
 void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
   // BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...)
   //
@@ -151,12 +162,73 @@
   // We hit a signal we didn't handle.  This might be something for which
   // we can give more information about so call all registered handlers to see
   // if it is.
-  for (const auto& handler : other_handlers_) {
-    if (handler->Action(sig, info, context)) {
-      return;
+
+  Thread* self = Thread::Current();
+
+  // Now set up the nested signal handler.
+
+  // Release the fault manager so that it will remove the signal chain for
+  // SIGSEGV and we call the real sigaction.
+  fault_manager.Release();
+
+  // The action for SIGSEGV should be the default handler now.
+
+  // Unblock the signals we allow so that they can be delivered in the signal handler.
+  sigset_t sigset;
+  sigemptyset(&sigset);
+  sigaddset(&sigset, SIGSEGV);
+  sigaddset(&sigset, SIGABRT);
+  pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
+
+  // If we get a signal in this code we want to invoke our nested signal
+  // handler.
+  struct sigaction action, oldsegvaction, oldabortaction;
+  action.sa_sigaction = art_nested_signal_handler;
+
+  // Explicitly mask out SIGSEGV and SIGABRT from the nested signal handler.  This
+  // should be the default but we definitely don't want these happening in our
+  // nested signal handler.
+  sigemptyset(&action.sa_mask);
+  sigaddset(&action.sa_mask, SIGSEGV);
+  sigaddset(&action.sa_mask, SIGABRT);
+
+  action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__APPLE__) && !defined(__mips__)
+  action.sa_restorer = nullptr;
+#endif
+
+  // Catch SIGSEGV and SIGABRT to invoke our nested handler
+  int e1 = sigaction(SIGSEGV, &action, &oldsegvaction);
+  int e2 = sigaction(SIGABRT, &action, &oldabortaction);
+  if (e1 != 0 || e2 != 0) {
+    LOG(ERROR) << "Unable to set up nested signal handler";
+  } else {
+    // Save the current state and call the handlers.  If anything causes a signal
+    // our nested signal handler will be invoked and this will longjmp to the saved
+    // state.
+    if (setjmp(*self->GetNestedSignalState()) == 0) {
+      for (const auto& handler : other_handlers_) {
+        if (handler->Action(sig, info, context)) {
+          // Restore the signal handlers, reinit the fault manager and return.  Signal was
+          // handled.
+          sigaction(SIGSEGV, &oldsegvaction, nullptr);
+          sigaction(SIGABRT, &oldabortaction, nullptr);
+          fault_manager.Init();
+          return;
+        }
+      }
+    } else {
+      LOG(ERROR) << "Nested signal detected - original signal being reported";
     }
+
+    // Restore the signal handlers.
+    sigaction(SIGSEGV, &oldsegvaction, nullptr);
+    sigaction(SIGABRT, &oldabortaction, nullptr);
   }
 
+  // Now put the fault manager back in place.
+  fault_manager.Init();
+
   // Set a breakpoint in this function to catch unhandled signals.
   art_sigsegv_fault();
 
@@ -165,6 +237,7 @@
 }
 
 void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) {
+  DCHECK(initialized_);
   if (generated_code) {
     generated_code_handlers_.push_back(handler);
   } else {
@@ -311,75 +384,18 @@
     uintptr_t sp = 0;
     Thread* self = Thread::Current();
 
-    // Shutdown the fault manager so that it will remove the signal chain for
-    // SIGSEGV and we call the real sigaction.
-    fault_manager.Shutdown();
-
-    // The action for SIGSEGV should be the default handler now.
-
-    // Unblock the signals we allow so that they can be delivered in the signal handler.
-    sigset_t sigset;
-    sigemptyset(&sigset);
-    sigaddset(&sigset, SIGSEGV);
-    sigaddset(&sigset, SIGABRT);
-    pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
-
-    // If we get a signal in this code we want to invoke our nested signal
-    // handler.
-    struct sigaction action, oldsegvaction, oldabortaction;
-    action.sa_sigaction = art_nested_signal_handler;
-
-    // Explictly mask out SIGSEGV and SIGABRT from the nested signal handler.  This
-    // should be the default but we definitely don't want these happening in our
-    // nested signal handler.
-    sigemptyset(&action.sa_mask);
-    sigaddset(&action.sa_mask, SIGSEGV);
-    sigaddset(&action.sa_mask, SIGABRT);
-
-    action.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__APPLE__) && !defined(__mips__)
-    action.sa_restorer = nullptr;
-#endif
-
-    // Catch SIGSEGV and SIGABRT to invoke our nested handler
-    int e1 = sigaction(SIGSEGV, &action, &oldsegvaction);
-    int e2 = sigaction(SIGABRT, &action, &oldabortaction);
-    if (e1 != 0 || e2 != 0) {
-      LOG(ERROR) << "Unable to register nested signal handler - no stack trace possible";
-      // If sigaction failed we have a serious problem.  We cannot catch
-      // any failures in the stack tracer and it's likely to occur since
-      // the program state is bad.  Therefore we don't even try to give
-      // a stack trace.
-    } else {
-      // Save the current state and try to dump the stack.  If this causes a signal
-      // our nested signal handler will be invoked and this will longjmp to the saved
-      // state.
-      if (setjmp(*self->GetNestedSignalState()) == 0) {
-        manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
-        // Inside of generated code, sp[0] is the method, so sp is the frame.
-        StackReference<mirror::ArtMethod>* frame =
-            reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
-        self->SetTopOfStack(frame, 0);  // Since we don't necessarily have a dex pc, pass in 0.
+    manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
+    // Inside of generated code, sp[0] is the method, so sp is the frame.
+    StackReference<mirror::ArtMethod>* frame =
+        reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
+    self->SetTopOfStack(frame, 0);  // Since we don't necessarily have a dex pc, pass in 0.
 #ifdef TEST_NESTED_SIGNAL
-        // To test the nested signal handler we raise a signal here.  This will cause the
-        // nested signal handler to be called and perform a longjmp back to the setjmp
-        // above.
-        abort();
+    // To test the nested signal handler we raise a signal here.  This will cause the
+    // nested signal handler to be called and perform a longjmp back to the setjmp
+    // above.
+    abort();
 #endif
-        self->DumpJavaStack(LOG(ERROR));
-      } else {
-        LOG(ERROR) << "Stack trace aborted due to nested signal - original signal being reported";
-      }
-
-      // Restore the signal handlers.
-      sigaction(SIGSEGV, &oldsegvaction, nullptr);
-      sigaction(SIGABRT, &oldabortaction, nullptr);
-    }
-
-    // Now put the fault manager back in place.
-    fault_manager.Init();
-
-    // And we're done.
+    self->DumpJavaStack(LOG(ERROR));
   }
 
   return false;  // Return false since we want to propagate the fault to the main signal handler.
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index bb26780..8b66a6f 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -39,10 +39,17 @@
   ~FaultManager();
 
   void Init();
+
+  // Unclaim signals.
+  void Release();
+
+  // Unclaim signals and delete registered handlers.
   void Shutdown();
 
   void HandleFault(int sig, siginfo_t* info, void* context);
   void HandleNestedSignal(int sig, siginfo_t* info, void* context);
+
+  // Added handlers are owned by the fault handler and will be freed on Shutdown().
   void AddHandler(FaultHandler* handler, bool generated_code);
   void RemoveHandler(FaultHandler* handler);
 
diff --git a/runtime/field_helper.h b/runtime/field_helper.h
index 5eae55e..8097025 100644
--- a/runtime/field_helper.h
+++ b/runtime/field_helper.h
@@ -27,11 +27,6 @@
  public:
   explicit FieldHelper(Handle<mirror::ArtField> f) : field_(f) {}
 
-  void ChangeField(mirror::ArtField* new_f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK(new_f != nullptr);
-    field_.Assign(new_f);
-  }
-
   mirror::ArtField* GetField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return field_.Get();
   }
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index a88b2c9..433855a 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -33,14 +33,16 @@
   class Object;
 }  // namespace mirror
 
+namespace gc {
+namespace accounting {
+
 class CardTableTest : public CommonRuntimeTest {
  public:
-  std::unique_ptr<gc::accounting::CardTable> card_table_;
-  static constexpr size_t kCardSize = gc::accounting::CardTable::kCardSize;
+  std::unique_ptr<CardTable> card_table_;
 
   void CommonSetup() {
     if (card_table_.get() == nullptr) {
-      card_table_.reset(gc::accounting::CardTable::Create(heap_begin_, heap_size_));
+      card_table_.reset(CardTable::Create(heap_begin_, heap_size_));
       EXPECT_TRUE(card_table_.get() != nullptr);
     } else {
       ClearCardTable();
@@ -58,15 +60,16 @@
   byte* HeapLimit() const {
     return HeapBegin() + heap_size_;
   }
-  byte PRandCard(const byte* addr) const {
-    size_t offset = RoundDown(addr - heap_begin_, kCardSize);
+  // Return a pseudo random card for an address.
+  byte PseudoRandomCard(const byte* addr) const {
+    size_t offset = RoundDown(addr - heap_begin_, CardTable::kCardSize);
     return 1 + offset % 254;
   }
   void FillRandom() {
-    for (const byte* addr = HeapBegin(); addr != HeapLimit(); addr += kCardSize) {
+    for (const byte* addr = HeapBegin(); addr != HeapLimit(); addr += CardTable::kCardSize) {
       EXPECT_TRUE(card_table_->AddrIsInCardTable(addr));
       byte* card = card_table_->CardFromAddr(addr);
-      *card = PRandCard(addr);
+      *card = PseudoRandomCard(addr);
     }
   }
 
@@ -79,15 +82,15 @@
   CommonSetup();
   for (const byte* addr = HeapBegin(); addr < HeapLimit(); addr += kObjectAlignment) {
     auto obj = reinterpret_cast<const mirror::Object*>(addr);
-    EXPECT_EQ(card_table_->GetCard(obj), gc::accounting::CardTable::kCardClean);
+    EXPECT_EQ(card_table_->GetCard(obj), CardTable::kCardClean);
     EXPECT_TRUE(!card_table_->IsDirty(obj));
     card_table_->MarkCard(addr);
     EXPECT_TRUE(card_table_->IsDirty(obj));
-    EXPECT_EQ(card_table_->GetCard(obj), gc::accounting::CardTable::kCardDirty);
+    EXPECT_EQ(card_table_->GetCard(obj), CardTable::kCardDirty);
     byte* card_addr = card_table_->CardFromAddr(addr);
-    EXPECT_EQ(*card_addr, gc::accounting::CardTable::kCardDirty);
-    *card_addr = gc::accounting::CardTable::kCardClean;
-    EXPECT_EQ(*card_addr, gc::accounting::CardTable::kCardClean);
+    EXPECT_EQ(*card_addr, CardTable::kCardDirty);
+    *card_addr = CardTable::kCardClean;
+    EXPECT_EQ(*card_addr, CardTable::kCardClean);
   }
 }
 
@@ -103,33 +106,36 @@
 TEST_F(CardTableTest, TestModifyCardsAtomic) {
   CommonSetup();
   FillRandom();
-  const size_t delta = std::min(static_cast<size_t>(HeapLimit() - HeapBegin()), 8U * kCardSize);
+  const size_t delta = std::min(static_cast<size_t>(HeapLimit() - HeapBegin()),
+                                8U * CardTable::kCardSize);
   UpdateVisitor visitor;
   size_t start_offset = 0;
-  for (byte* cstart = HeapBegin(); cstart < HeapBegin() + delta; cstart += kCardSize) {
-    start_offset = (start_offset + kObjectAlignment) % kCardSize;
+  for (byte* cstart = HeapBegin(); cstart < HeapBegin() + delta; cstart += CardTable::kCardSize) {
+    start_offset = (start_offset + kObjectAlignment) % CardTable::kCardSize;
     size_t end_offset = 0;
-    for (byte* cend = HeapLimit() - delta; cend < HeapLimit(); cend += kCardSize) {
+    for (byte* cend = HeapLimit() - delta; cend < HeapLimit(); cend += CardTable::kCardSize) {
       // Don't always start at a card boundary.
       byte* start = cstart + start_offset;
       byte* end = cend - end_offset;
-      end_offset = (end_offset + kObjectAlignment) % kCardSize;
+      end_offset = (end_offset + kObjectAlignment) % CardTable::kCardSize;
       // Modify cards.
       card_table_->ModifyCardsAtomic(start, end, visitor, visitor);
       // Check adjacent cards not modified.
-      for (byte* cur = start - kCardSize; cur >= HeapBegin(); cur -= kCardSize) {
-        EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)), PRandCard(cur));
+      for (byte* cur = start - CardTable::kCardSize; cur >= HeapBegin();
+          cur -= CardTable::kCardSize) {
+        EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)),
+                  PseudoRandomCard(cur));
       }
-      for (byte* cur = end + kCardSize; cur < HeapLimit(); cur += kCardSize) {
-        EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)), PRandCard(cur));
+      for (byte* cur = end + CardTable::kCardSize; cur < HeapLimit();
+          cur += CardTable::kCardSize) {
+        EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)),
+                  PseudoRandomCard(cur));
       }
       // Verify Range.
-      for (byte* cur = start; cur < AlignUp(end, kCardSize); cur += kCardSize) {
+      for (byte* cur = start; cur < AlignUp(end, CardTable::kCardSize);
+          cur += CardTable::kCardSize) {
         byte* card = card_table_->CardFromAddr(cur);
-        byte value = PRandCard(cur);
-        if (visitor(value) != *card) {
-          LOG(ERROR) << reinterpret_cast<void*>(start) << " " << reinterpret_cast<void*>(cur) << " " << reinterpret_cast<void*>(end);
-        }
+        byte value = PseudoRandomCard(cur);
         EXPECT_EQ(visitor(value), *card);
         // Restore for next iteration.
         *card = value;
@@ -139,5 +145,6 @@
 }
 
 // TODO: Add test for CardTable::Scan.
-
+}  // namespace accounting
+}  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 2686af0..3acf80d 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -72,9 +72,11 @@
 
 class ModUnionUpdateObjectReferencesVisitor {
  public:
-  ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg)
-    : callback_(callback),
-      arg_(arg) {
+  ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg,
+                                        space::ContinuousSpace* from_space,
+                                        bool* contains_reference_to_other_space)
+    : callback_(callback), arg_(arg), from_space_(from_space),
+      contains_reference_to_other_space_(contains_reference_to_other_space) {
   }
 
   // Extra parameters are required since we use this same visitor signature for checking objects.
@@ -82,7 +84,9 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Only add the reference if it is non null and fits our criteria.
     mirror::HeapReference<Object>* obj_ptr = obj->GetFieldObjectReferenceAddr(offset);
-    if (obj_ptr->AsMirrorPtr() != nullptr) {
+    mirror::Object* ref = obj_ptr->AsMirrorPtr();
+    if (ref != nullptr && !from_space_->HasAddress(ref)) {
+      *contains_reference_to_other_space_ = true;
       callback_(obj_ptr, arg_);
     }
   }
@@ -90,24 +94,36 @@
  private:
   MarkHeapReferenceCallback* const callback_;
   void* arg_;
+  // Space which we are scanning
+  space::ContinuousSpace* const from_space_;
+  // Set if we have any references to another space.
+  bool* const contains_reference_to_other_space_;
 };
 
 class ModUnionScanImageRootVisitor {
  public:
-  ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg)
-      : callback_(callback), arg_(arg) {}
+  ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg,
+                               space::ContinuousSpace* from_space,
+                               bool* contains_reference_to_other_space)
+      : callback_(callback), arg_(arg), from_space_(from_space),
+        contains_reference_to_other_space_(contains_reference_to_other_space) {}
 
   void operator()(Object* root) const
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(root != NULL);
-    ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_);
+    ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_,
+                                                      contains_reference_to_other_space_);
     root->VisitReferences<kMovingClasses>(ref_visitor, VoidFunctor());
   }
 
  private:
   MarkHeapReferenceCallback* const callback_;
   void* const arg_;
+  // Space which we are scanning
+  space::ContinuousSpace* const from_space_;
+  // Set if we have any references to another space.
+  bool* const contains_reference_to_other_space_;
 };
 
 void ModUnionTableReferenceCache::ClearCards() {
@@ -313,12 +329,20 @@
 void ModUnionTableCardCache::UpdateAndMarkReferences(MarkHeapReferenceCallback* callback,
                                                      void* arg) {
   CardTable* card_table = heap_->GetCardTable();
-  ModUnionScanImageRootVisitor scan_visitor(callback, arg);
   ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
-  for (const byte* card_addr : cleared_cards_) {
-    uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
+  bool reference_to_other_space = false;
+  ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, &reference_to_other_space);
+  for (auto it = cleared_cards_.begin(), end = cleared_cards_.end(); it != end; ) {
+    uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(*it));
     DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
+    reference_to_other_space = false;
     bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
+    if (!reference_to_other_space) {
+      // No non null reference to another space, remove the card.
+      it = cleared_cards_.erase(it);
+    } else {
+      ++it;
+    }
   }
 }
 
@@ -333,6 +357,17 @@
   os << "]";
 }
 
+void ModUnionTableCardCache::SetCards() {
+  CardTable* card_table = heap_->GetCardTable();
+  for (byte* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
+       addr += CardTable::kCardSize) {
+    cleared_cards_.insert(card_table->CardFromAddr(addr));
+  }
+}
+
+void ModUnionTableReferenceCache::SetCards() {
+}
+
 }  // namespace accounting
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index f9e8261..d0e11e0 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -66,6 +66,9 @@
   // determining references to track.
   virtual void ClearCards() = 0;
 
+  // Set all the cards.
+  virtual void SetCards() = 0;
+
   // Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards
   // before a call to update, for example, back-to-back sticky GCs. Also mark references to other
   // spaces which are stored in the mod-union table.
@@ -121,6 +124,8 @@
 
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void SetCards() OVERRIDE;
+
  protected:
   // Cleared card array, used to update the mod-union table.
   ModUnionTable::CardSet cleared_cards_;
@@ -150,6 +155,8 @@
 
   void Dump(std::ostream& os);
 
+  void SetCards() OVERRIDE;
+
  protected:
   // Cleared card array, used to update the mod-union table.
   CardSet cleared_cards_;
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index ad22a2e..a7e5e74 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -569,7 +569,7 @@
 
 RosAlloc::Run* RosAlloc::RefillRun(Thread* self, size_t idx) {
   // Get the lowest address non-full run from the binary tree.
-  std::set<Run*>* const bt = &non_full_runs_[idx];
+  auto* const bt = &non_full_runs_[idx];
   if (!bt->empty()) {
     // If there's one, use it as the current run.
     auto it = bt->begin();
@@ -767,7 +767,7 @@
   }
   // Free the slot in the run.
   run->FreeSlot(ptr);
-  std::set<Run*>* non_full_runs = &non_full_runs_[idx];
+  auto* non_full_runs = &non_full_runs_[idx];
   if (run->IsAllFree()) {
     // It has just become completely free. Free the pages of this run.
     std::set<Run*>::iterator pos = non_full_runs->find(run);
@@ -793,9 +793,8 @@
     // already in the non-full run set (i.e., it was full) insert it
     // into the non-full run set.
     if (run != current_runs_[idx]) {
-      std::unordered_set<Run*, hash_run, eq_run>* full_runs =
-          kIsDebugBuild ? &full_runs_[idx] : NULL;
-      std::set<Run*>::iterator pos = non_full_runs->find(run);
+      auto* full_runs = kIsDebugBuild ? &full_runs_[idx] : NULL;
+      auto pos = non_full_runs->find(run);
       if (pos == non_full_runs->end()) {
         DCHECK(run_was_full);
         DCHECK(full_runs->find(run) != full_runs->end());
@@ -1266,9 +1265,8 @@
       }
       // Check if the run should be moved to non_full_runs_ or
       // free_page_runs_.
-      std::set<Run*>* non_full_runs = &non_full_runs_[idx];
-      std::unordered_set<Run*, hash_run, eq_run>* full_runs =
-          kIsDebugBuild ? &full_runs_[idx] : NULL;
+      auto* non_full_runs = &non_full_runs_[idx];
+      auto* full_runs = kIsDebugBuild ? &full_runs_[idx] : NULL;
       if (run->IsAllFree()) {
         // It has just become completely free. Free the pages of the
         // run.
@@ -2056,7 +2054,7 @@
     // in a run set.
     if (!is_current_run) {
       MutexLock mu(self, rosalloc->lock_);
-      std::set<Run*>& non_full_runs = rosalloc->non_full_runs_[idx];
+      auto& non_full_runs = rosalloc->non_full_runs_[idx];
       // If it's all free, it must be a free page run rather than a run.
       CHECK(!IsAllFree()) << "A free run must be in a free page run set " << Dump();
       if (!IsFull()) {
@@ -2066,7 +2064,7 @@
       } else {
         // If it's full, it must in the full run set (debug build only.)
         if (kIsDebugBuild) {
-          std::unordered_set<Run*, hash_run, eq_run>& full_runs = rosalloc->full_runs_[idx];
+          auto& full_runs = rosalloc->full_runs_[idx];
           CHECK(full_runs.find(this) != full_runs.end())
               << " A full run isn't in the full run set " << Dump();
         }
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index b2a5a3c..2fbd97a 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -26,6 +26,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "base/allocator.h"
 #include "base/mutex.h"
 #include "base/logging.h"
 #include "globals.h"
@@ -53,7 +54,7 @@
       size_t pm_idx = rosalloc->ToPageMapIndex(fpr_base);
       size_t byte_size = rosalloc->free_page_run_size_map_[pm_idx];
       DCHECK_GE(byte_size, static_cast<size_t>(0));
-      DCHECK_EQ(byte_size % kPageSize, static_cast<size_t>(0));
+      DCHECK_ALIGNED(byte_size, kPageSize);
       return byte_size;
     }
     void SetByteSize(RosAlloc* rosalloc, size_t byte_size)
@@ -403,6 +404,7 @@
 
   // We use thread-local runs for the size Brackets whose indexes
   // are less than this index. We use shared (current) runs for the rest.
+
   static const size_t kNumThreadLocalSizeBrackets = 11;
 
  private:
@@ -423,12 +425,13 @@
 
   // The run sets that hold the runs whose slots are not all
   // full. non_full_runs_[i] is guarded by size_bracket_locks_[i].
-  std::set<Run*> non_full_runs_[kNumOfSizeBrackets];
+  AllocationTrackingSet<Run*, kAllocatorTagRosAlloc> non_full_runs_[kNumOfSizeBrackets];
   // The run sets that hold the runs whose slots are all full. This is
   // debug only. full_runs_[i] is guarded by size_bracket_locks_[i].
-  std::unordered_set<Run*, hash_run, eq_run> full_runs_[kNumOfSizeBrackets];
+  std::unordered_set<Run*, hash_run, eq_run, TrackingAllocator<Run*, kAllocatorTagRosAlloc>>
+      full_runs_[kNumOfSizeBrackets];
   // The set of free pages.
-  std::set<FreePageRun*> free_page_runs_ GUARDED_BY(lock_);
+  AllocationTrackingSet<FreePageRun*, kAllocatorTagRosAlloc> free_page_runs_ GUARDED_BY(lock_);
   // The dedicated full run, it is always full and shared by all threads when revoking happens.
   // This is an optimization since enables us to avoid a null check for revoked runs.
   static Run* dedicated_full_run_;
@@ -460,7 +463,8 @@
   // The table that indicates the size of free page runs. These sizes
   // are stored here to avoid storing in the free page header and
   // release backing pages.
-  std::vector<size_t> free_page_run_size_map_ GUARDED_BY(lock_);
+  std::vector<size_t, TrackingAllocator<size_t, kAllocatorTagRosAlloc>> free_page_run_size_map_
+      GUARDED_BY(lock_);
   // The global lock. Used to guard the page map, the free page set,
   // and the footprint.
   Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 46d79bf..07b61e6 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -55,7 +55,8 @@
     : heap_(heap),
       name_(name),
       pause_histogram_((name_ + " paused").c_str(), kPauseBucketSize, kPauseBucketCount),
-      cumulative_timings_(name) {
+      cumulative_timings_(name),
+      pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true) {
   ResetCumulativeStatistics();
 }
 
@@ -65,10 +66,11 @@
 
 void GarbageCollector::ResetCumulativeStatistics() {
   cumulative_timings_.Reset();
-  pause_histogram_.Reset();
   total_time_ns_ = 0;
   total_freed_objects_ = 0;
   total_freed_bytes_ = 0;
+  MutexLock mu(Thread::Current(), pause_histogram_lock_);
+  pause_histogram_.Reset();
 }
 
 void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
@@ -95,6 +97,7 @@
   }
   total_time_ns_ += current_iteration->GetDurationNs();
   for (uint64_t pause_time : current_iteration->GetPauseTimes()) {
+    MutexLock mu(self, pause_histogram_lock_);
     pause_histogram_.AddValue(pause_time / 1000);
   }
   ATRACE_END();
@@ -137,8 +140,11 @@
 }
 
 void GarbageCollector::ResetMeasurements() {
+  {
+    MutexLock mu(Thread::Current(), pause_histogram_lock_);
+    pause_histogram_.Reset();
+  }
   cumulative_timings_.Reset();
-  pause_histogram_.Reset();
   total_time_ns_ = 0;
   total_freed_objects_ = 0;
   total_freed_bytes_ = 0;
@@ -171,6 +177,38 @@
   heap_->RecordFree(freed.objects, freed.bytes);
 }
 
+uint64_t GarbageCollector::GetTotalPausedTimeNs() {
+  MutexLock mu(Thread::Current(), pause_histogram_lock_);
+  return pause_histogram_.AdjustedSum();
+}
+
+void GarbageCollector::DumpPerformanceInfo(std::ostream& os) {
+  const CumulativeLogger& logger = GetCumulativeTimings();
+  const size_t iterations = logger.GetIterations();
+  if (iterations == 0) {
+    return;
+  }
+  os << ConstDumpable<CumulativeLogger>(logger);
+  const uint64_t total_ns = logger.GetTotalNs();
+  double seconds = NsToMs(logger.GetTotalNs()) / 1000.0;
+  const uint64_t freed_bytes = GetTotalFreedBytes();
+  const uint64_t freed_objects = GetTotalFreedObjects();
+  {
+    MutexLock mu(Thread::Current(), pause_histogram_lock_);
+    if (pause_histogram_.SampleSize() > 0) {
+      Histogram<uint64_t>::CumulativeData cumulative_data;
+      pause_histogram_.CreateHistogram(&cumulative_data);
+      pause_histogram_.PrintConfidenceIntervals(os, 0.99, cumulative_data);
+    }
+  }
+  os << GetName() << " total time: " << PrettyDuration(total_ns)
+     << " mean time: " << PrettyDuration(total_ns / iterations) << "\n"
+     << GetName() << " freed: " << freed_objects
+     << " objects with total size " << PrettySize(freed_bytes) << "\n"
+     << GetName() << " throughput: " << freed_objects / seconds << "/s / "
+     << PrettySize(freed_bytes / seconds) << "/s\n";
+}
+
 }  // namespace collector
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index 885569e..b809469 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -119,18 +119,13 @@
 
   GarbageCollector(Heap* heap, const std::string& name);
   virtual ~GarbageCollector() { }
-
   const char* GetName() const {
     return name_.c_str();
   }
-
   virtual GcType GetGcType() const = 0;
-
   virtual CollectorType GetCollectorType() const = 0;
-
   // Run the garbage collector.
   void Run(GcCause gc_cause, bool clear_soft_references);
-
   Heap* GetHeap() const {
     return heap_;
   }
@@ -138,24 +133,17 @@
   const CumulativeLogger& GetCumulativeTimings() const {
     return cumulative_timings_;
   }
-
   void ResetCumulativeStatistics();
-
   // Swap the live and mark bitmaps of spaces that are active for the collector. For partial GC,
   // this is the allocation space, for full GC then we swap the zygote bitmaps too.
   void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
-  uint64_t GetTotalPausedTimeNs() const {
-    return pause_histogram_.AdjustedSum();
-  }
+  uint64_t GetTotalPausedTimeNs() LOCKS_EXCLUDED(pause_histogram_lock_);
   int64_t GetTotalFreedBytes() const {
     return total_freed_bytes_;
   }
   uint64_t GetTotalFreedObjects() const {
     return total_freed_objects_;
   }
-  const Histogram<uint64_t>& GetPauseHistogram() const {
-    return pause_histogram_;
-  }
   // Reset the cumulative timings and pause histogram.
   void ResetMeasurements();
   // Returns the estimated throughput in bytes / second.
@@ -174,11 +162,11 @@
   void RecordFree(const ObjectBytePair& freed);
   // Record a free of large objects.
   void RecordFreeLOS(const ObjectBytePair& freed);
+  void DumpPerformanceInfo(std::ostream& os) LOCKS_EXCLUDED(pause_histogram_lock_);
 
  protected:
   // Run all of the GC phases.
   virtual void RunPhases() = 0;
-
   // Revoke all the thread-local buffers.
   virtual void RevokeAllThreadLocalBuffers() = 0;
 
@@ -188,11 +176,12 @@
   Heap* const heap_;
   std::string name_;
   // Cumulative statistics.
-  Histogram<uint64_t> pause_histogram_;
+  Histogram<uint64_t> pause_histogram_ GUARDED_BY(pause_histogram_lock_);
   uint64_t total_time_ns_;
   uint64_t total_freed_objects_;
   int64_t total_freed_bytes_;
   CumulativeLogger cumulative_timings_;
+  mutable Mutex pause_histogram_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 };
 
 }  // namespace collector
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 4044852..b3bed64 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -547,8 +547,11 @@
 }
 
 void MarkCompact::SweepLargeObjects(bool swap_bitmaps) {
-  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
-  RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+  space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace();
+  if (los != nullptr) {
+    TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());\
+    RecordFreeLOS(los->Sweep(swap_bitmaps));
+  }
 }
 
 // Process the "referent" field in a java.lang.ref.Reference.  If the referent has not yet been
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 95530be..930499a 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -374,7 +374,8 @@
     }
     space::LargeObjectSpace* large_object_space = mark_sweep_->GetHeap()->GetLargeObjectsSpace();
     if (UNLIKELY(obj == nullptr || !IsAligned<kPageSize>(obj) ||
-                 (kIsDebugBuild && !large_object_space->Contains(obj)))) {
+                 (kIsDebugBuild && large_object_space != nullptr &&
+                     !large_object_space->Contains(obj)))) {
       LOG(ERROR) << "Tried to mark " << obj << " not contained by any spaces";
       LOG(ERROR) << "Attempting see if it's a bad root";
       mark_sweep_->VerifyRoots();
@@ -481,7 +482,7 @@
   // See if the root is on any space bitmap.
   if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
     space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
-    if (!large_object_space->Contains(root)) {
+    if (large_object_space != nullptr && !large_object_space->Contains(root)) {
       LOG(ERROR) << "Found invalid root: " << root << " with type " << root_type;
       if (visitor != NULL) {
         LOG(ERROR) << visitor->DescribeLocation() << " in VReg: " << vreg;
@@ -1074,20 +1075,22 @@
   }
   // Handle the large object space.
   space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
-  accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap();
-  accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap();
-  if (swap_bitmaps) {
-    std::swap(large_live_objects, large_mark_objects);
-  }
-  for (size_t i = 0; i < count; ++i) {
-    Object* obj = objects[i];
-    // Handle large objects.
-    if (kUseThreadLocalAllocationStack && obj == nullptr) {
-      continue;
+  if (large_object_space != nullptr) {
+    accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap();
+    accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap();
+    if (swap_bitmaps) {
+      std::swap(large_live_objects, large_mark_objects);
     }
-    if (!large_mark_objects->Test(obj)) {
-      ++freed_los.objects;
-      freed_los.bytes += large_object_space->Free(self, obj);
+    for (size_t i = 0; i < count; ++i) {
+      Object* obj = objects[i];
+      // Handle large objects.
+      if (kUseThreadLocalAllocationStack && obj == nullptr) {
+        continue;
+      }
+      if (!large_mark_objects->Test(obj)) {
+        ++freed_los.objects;
+        freed_los.bytes += large_object_space->Free(self, obj);
+      }
     }
   }
   {
@@ -1125,8 +1128,11 @@
 }
 
 void MarkSweep::SweepLargeObjects(bool swap_bitmaps) {
-  TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
-  RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+  space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace();
+  if (los != nullptr) {
+    TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
+    RecordFreeLOS(los->Sweep(swap_bitmaps));
+  }
 }
 
 // Process the "referent" field in a java.lang.ref.Reference.  If the referent has not yet been
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 8fb33ce..c8fa869 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -365,23 +365,23 @@
   }
 
   CHECK_EQ(is_large_object_space_immune_, collect_from_space_only_);
-  if (is_large_object_space_immune_) {
+  space::LargeObjectSpace* los = GetHeap()->GetLargeObjectsSpace();
+  if (is_large_object_space_immune_ && los != nullptr) {
     TimingLogger::ScopedTiming t("VisitLargeObjects", GetTimings());
     DCHECK(collect_from_space_only_);
     // Delay copying the live set to the marked set until here from
     // BindBitmaps() as the large objects on the allocation stack may
     // be newly added to the live set above in MarkAllocStackAsLive().
-    GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked();
+    los->CopyLiveToMarked();
 
     // When the large object space is immune, we need to scan the
     // large object space as roots as they contain references to their
     // classes (primitive array classes) that could move though they
     // don't contain any other references.
-    space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
-    accounting::LargeObjectBitmap* large_live_bitmap = large_object_space->GetLiveBitmap();
+    accounting::LargeObjectBitmap* large_live_bitmap = los->GetLiveBitmap();
     SemiSpaceScanObjectVisitor visitor(this);
-    large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(large_object_space->Begin()),
-                                        reinterpret_cast<uintptr_t>(large_object_space->End()),
+    large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(los->Begin()),
+                                        reinterpret_cast<uintptr_t>(los->End()),
                                         visitor);
   }
   // Recursively process the mark stack.
@@ -655,8 +655,11 @@
 
 void SemiSpace::SweepLargeObjects(bool swap_bitmaps) {
   DCHECK(!is_large_object_space_immune_);
-  TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings());
-  RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+  space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace();
+  if (los != nullptr) {
+    TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings());
+    RecordFreeLOS(los->Sweep(swap_bitmaps));
+  }
 }
 
 // Process the "referent" field in a java.lang.ref.Reference.  If the referent has not yet been
@@ -751,6 +754,7 @@
   from_space_ = nullptr;
   CHECK(mark_stack_->IsEmpty());
   mark_stack_->Reset();
+  space::LargeObjectSpace* los = GetHeap()->GetLargeObjectsSpace();
   if (generational_) {
     // Decide whether to do a whole heap collection or a bump pointer
     // only space collection at the next collection by updating
@@ -762,7 +766,7 @@
       bytes_promoted_since_last_whole_heap_collection_ += bytes_promoted_;
       bool bytes_promoted_threshold_exceeded =
           bytes_promoted_since_last_whole_heap_collection_ >= kBytesPromotedThreshold;
-      uint64_t current_los_bytes_allocated = GetHeap()->GetLargeObjectsSpace()->GetBytesAllocated();
+      uint64_t current_los_bytes_allocated = los != nullptr ? los->GetBytesAllocated() : 0U;
       uint64_t last_los_bytes_allocated =
           large_object_bytes_allocated_at_last_whole_heap_collection_;
       bool large_object_bytes_threshold_exceeded =
@@ -775,7 +779,7 @@
       // Reset the counters.
       bytes_promoted_since_last_whole_heap_collection_ = bytes_promoted_;
       large_object_bytes_allocated_at_last_whole_heap_collection_ =
-          GetHeap()->GetLargeObjectsSpace()->GetBytesAllocated();
+          los != nullptr ? los->GetBytesAllocated() : 0U;
       collect_from_space_only_ = true;
     }
   }
diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc
index 5a58446..4ed6abc 100644
--- a/runtime/gc/collector/sticky_mark_sweep.cc
+++ b/runtime/gc/collector/sticky_mark_sweep.cc
@@ -16,7 +16,7 @@
 
 #include "gc/heap.h"
 #include "gc/space/large_object_space.h"
-#include "gc/space/space.h"
+#include "gc/space/space-inl.h"
 #include "sticky_mark_sweep.h"
 #include "thread-inl.h"
 
@@ -32,7 +32,6 @@
 
 void StickyMarkSweep::BindBitmaps() {
   PartialMarkSweep::BindBitmaps();
-
   WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
   // For sticky GC, we want to bind the bitmaps of all spaces as the allocation stack lets us
   // know what was allocated since the last GC. A side-effect of binding the allocation space mark
@@ -44,7 +43,10 @@
       space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap();
     }
   }
-  GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked();
+  for (const auto& space : GetHeap()->GetDiscontinuousSpaces()) {
+    CHECK(space->IsLargeObjectSpace());
+    space->AsLargeObjectSpace()->CopyLiveToMarked();
+  }
 }
 
 void StickyMarkSweep::MarkReachableObjects() {
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index d1fb600..3101c68 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -140,7 +140,7 @@
   }
   if (kInstrumented) {
     if (Dbg::IsAllocTrackingEnabled()) {
-      Dbg::RecordAllocation(klass, bytes_allocated);
+      Dbg::RecordAllocation(self, klass, bytes_allocated);
     }
   } else {
     DCHECK(!Dbg::IsAllocTrackingEnabled());
@@ -277,7 +277,7 @@
       heap_->total_allocation_time_.FetchAndAddSequentiallyConsistent(allocation_end_time - allocation_start_time_);
     }
   }
-};
+}
 
 inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const {
   // We need to have a zygote space or else our newly allocated large object can end up in the
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 6af98cf..d672510 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -83,13 +83,6 @@
 // relative to partial/full GC. This may be desirable since sticky GCs interfere less with mutator
 // threads (lower pauses, use less memory bandwidth).
 static constexpr double kStickyGcThroughputAdjustment = 1.0;
-// Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
-// since this means that we have to use the slow msync loop in MemMap::MapAnonymous.
-#if USE_ART_LOW_4G_ALLOCATOR
-static constexpr bool kUseFreeListSpaceForLOS = true;
-#else
-static constexpr bool kUseFreeListSpaceForLOS = false;
-#endif
 // Whether or not we compact the zygote in PreZygoteFork.
 static constexpr bool kCompactZygote = kMovingCollector;
 // How many reserve entries are at the end of the allocation stack, these are only needed if the
@@ -107,8 +100,9 @@
            double target_utilization, double foreground_heap_growth_multiplier,
            size_t capacity, size_t non_moving_space_capacity, const std::string& image_file_name,
            const InstructionSet image_instruction_set, CollectorType foreground_collector_type,
-           CollectorType background_collector_type, size_t parallel_gc_threads,
-           size_t conc_gc_threads, bool low_memory_mode,
+           CollectorType background_collector_type,
+           space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold,
+           size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode,
            size_t long_pause_log_threshold, size_t long_gc_log_threshold,
            bool ignore_max_footprint, bool use_tlab,
            bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap,
@@ -134,8 +128,8 @@
       long_gc_log_threshold_(long_gc_log_threshold),
       ignore_max_footprint_(ignore_max_footprint),
       zygote_creation_lock_("zygote creation lock", kZygoteCreationLock),
-      have_zygote_space_(false),
-      large_object_threshold_(std::numeric_limits<size_t>::max()),  // Starts out disabled.
+      zygote_space_(nullptr),
+      large_object_threshold_(large_object_threshold),
       collector_type_running_(kCollectorTypeNone),
       last_gc_type_(collector::kGcTypeNone),
       next_gc_type_(collector::kGcTypePartial),
@@ -195,7 +189,6 @@
   // entrypoints.
   const bool is_zygote = Runtime::Current()->IsZygote();
   if (!is_zygote) {
-    large_object_threshold_ = kDefaultLargeObjectThreshold;
     // Background compaction is currently not supported for command line runs.
     if (background_collector_type_ != foreground_collector_type_) {
       VLOG(heap) << "Disabling background compaction for non zygote";
@@ -339,13 +332,21 @@
   CHECK(non_moving_space_ != nullptr);
   CHECK(!non_moving_space_->CanMoveObjects());
   // Allocate the large object space.
-  if (kUseFreeListSpaceForLOS) {
-    large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity_);
+  if (large_object_space_type == space::kLargeObjectSpaceTypeFreeList) {
+    large_object_space_ = space::FreeListSpace::Create("free list large object space", nullptr,
+                                                       capacity_);
+    CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
+  } else if (large_object_space_type == space::kLargeObjectSpaceTypeMap) {
+    large_object_space_ = space::LargeObjectMapSpace::Create("mem map large object space");
+    CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
   } else {
-    large_object_space_ = space::LargeObjectMapSpace::Create("large object space");
+    // Disable the large object space by making the cutoff excessively large.
+    large_object_threshold_ = std::numeric_limits<size_t>::max();
+    large_object_space_ = nullptr;
   }
-  CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
-  AddSpace(large_object_space_);
+  if (large_object_space_ != nullptr) {
+    AddSpace(large_object_space_);
+  }
   // Compute heap capacity. Continuous spaces are sorted in order of Begin().
   CHECK(!continuous_spaces_.empty());
   // Relies on the spaces being sorted.
@@ -412,9 +413,11 @@
     mark_compact_collector_ = new collector::MarkCompact(this);
     garbage_collectors_.push_back(mark_compact_collector_);
   }
-  if (GetImageSpace() != nullptr && non_moving_space_ != nullptr) {
+  if (GetImageSpace() != nullptr && non_moving_space_ != nullptr &&
+      (is_zygote || separate_non_moving_space || foreground_collector_type_ == kCollectorTypeGSS)) {
     // Check that there's no gap between the image space and the non moving space so that the
-    // immune region won't break (eg. due to a large object allocated in the gap).
+    // immune region won't break (eg. due to a large object allocated in the gap). This is only
+    // required when we're the zygote or using GSS.
     bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(),
                                       non_moving_space_->GetMemMap());
     if (!no_gap) {
@@ -482,7 +485,7 @@
     // After the zygote we want this to be false if we don't have background compaction enabled so
     // that getting primitive array elements is faster.
     // We never have homogeneous compaction with GSS and don't need a space with movable objects.
-    can_move_objects = !have_zygote_space_ && foreground_collector_type_ != kCollectorTypeGSS;
+    can_move_objects = !HasZygoteSpace() && foreground_collector_type_ != kCollectorTypeGSS;
   }
   if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) {
     RemoveRememberedSet(main_space_);
@@ -682,9 +685,8 @@
 }
 
 void Heap::VisitObjects(ObjectCallback callback, void* arg) {
-  Thread* self = Thread::Current();
   // GCs can move objects, so don't allow this.
-  const char* old_cause = self->StartAssertNoThreadSuspension("Visiting objects");
+  ScopedAssertNoThreadSuspension ants(Thread::Current(), "Visiting objects");
   if (bump_pointer_space_ != nullptr) {
     // Visit objects in bump pointer space.
     bump_pointer_space_->Walk(callback, arg);
@@ -701,7 +703,6 @@
     }
   }
   GetLiveBitmap()->Walk(callback, arg);
-  self->EndAssertNoThreadSuspension(old_cause);
 }
 
 void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
@@ -711,7 +712,8 @@
   CHECK(space1 != nullptr);
   CHECK(space2 != nullptr);
   MarkAllocStack(space1->GetLiveBitmap(), space2->GetLiveBitmap(),
-                 large_object_space_->GetLiveBitmap(), stack);
+                 (large_object_space_ != nullptr ? large_object_space_->GetLiveBitmap() : nullptr),
+                 stack);
 }
 
 void Heap::DeleteThreadPool() {
@@ -800,28 +802,9 @@
   // Dump cumulative loggers for each GC type.
   uint64_t total_paused_time = 0;
   for (auto& collector : garbage_collectors_) {
-    const CumulativeLogger& logger = collector->GetCumulativeTimings();
-    const size_t iterations = logger.GetIterations();
-    const Histogram<uint64_t>& pause_histogram = collector->GetPauseHistogram();
-    if (iterations != 0 && pause_histogram.SampleSize() != 0) {
-      os << ConstDumpable<CumulativeLogger>(logger);
-      const uint64_t total_ns = logger.GetTotalNs();
-      const uint64_t total_pause_ns = collector->GetTotalPausedTimeNs();
-      double seconds = NsToMs(logger.GetTotalNs()) / 1000.0;
-      const uint64_t freed_bytes = collector->GetTotalFreedBytes();
-      const uint64_t freed_objects = collector->GetTotalFreedObjects();
-      Histogram<uint64_t>::CumulativeData cumulative_data;
-      pause_histogram.CreateHistogram(&cumulative_data);
-      pause_histogram.PrintConfidenceIntervals(os, 0.99, cumulative_data);
-      os << collector->GetName() << " total time: " << PrettyDuration(total_ns)
-         << " mean time: " << PrettyDuration(total_ns / iterations) << "\n"
-         << collector->GetName() << " freed: " << freed_objects
-         << " objects with total size " << PrettySize(freed_bytes) << "\n"
-         << collector->GetName() << " throughput: " << freed_objects / seconds << "/s / "
-         << PrettySize(freed_bytes / seconds) << "/s\n";
-      total_duration += total_ns;
-      total_paused_time += total_pause_ns;
-    }
+    total_duration += collector->GetCumulativeTimings().GetTotalNs();
+    total_paused_time += collector->GetTotalPausedTimeNs();
+    collector->DumpPerformanceInfo(os);
     collector->ResetMeasurements();
   }
   uint64_t allocation_time =
@@ -848,6 +831,9 @@
     os << "Mean allocation time: " << PrettyDuration(allocation_time / total_objects_allocated)
        << "\n";
   }
+  if (HasZygoteSpace()) {
+    os << "Zygote space size " << PrettySize(zygote_space_->Size()) << "\n";
+  }
   os << "Total mutator paused time: " << PrettyDuration(total_paused_time) << "\n";
   os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_) << "\n";
   BaseMutex::DumpAll(os);
@@ -1017,7 +1003,10 @@
       total_alloc_space_size += malloc_space->Size();
     }
   }
-  total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated();
+  total_alloc_space_allocated = GetBytesAllocated();
+  if (large_object_space_ != nullptr) {
+    total_alloc_space_allocated -= large_object_space_->GetBytesAllocated();
+  }
   if (bump_pointer_space_ != nullptr) {
     total_alloc_space_allocated -= bump_pointer_space_->Size();
   }
@@ -1438,12 +1427,10 @@
 void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from,
                           uint64_t* counts) {
   // Can't do any GC in this function since this may move classes.
-  Thread* self = Thread::Current();
-  auto* old_cause = self->StartAssertNoThreadSuspension("CountInstances");
+  ScopedAssertNoThreadSuspension ants(Thread::Current(), "CountInstances");
   InstanceCounter counter(classes, use_is_assignable_from, counts);
-  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   VisitObjects(InstanceCounter::Callback, &counter);
-  self->EndAssertNoThreadSuspension(old_cause);
 }
 
 class InstanceCollector {
@@ -1456,8 +1443,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     DCHECK(arg != nullptr);
     InstanceCollector* instance_collector = reinterpret_cast<InstanceCollector*>(arg);
-    mirror::Class* instance_class = obj->GetClass();
-    if (instance_class == instance_collector->class_) {
+    if (obj->GetClass() == instance_collector->class_) {
       if (instance_collector->max_count_ == 0 ||
           instance_collector->instances_.size() < instance_collector->max_count_) {
         instance_collector->instances_.push_back(obj);
@@ -1466,8 +1452,8 @@
   }
 
  private:
-  mirror::Class* class_;
-  uint32_t max_count_;
+  const mirror::Class* const class_;
+  const uint32_t max_count_;
   std::vector<mirror::Object*>& instances_;
   DISALLOW_COPY_AND_ASSIGN(InstanceCollector);
 };
@@ -1475,12 +1461,10 @@
 void Heap::GetInstances(mirror::Class* c, int32_t max_count,
                         std::vector<mirror::Object*>& instances) {
   // Can't do any GC in this function since this may move classes.
-  Thread* self = Thread::Current();
-  auto* old_cause = self->StartAssertNoThreadSuspension("GetInstances");
+  ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetInstances");
   InstanceCollector collector(c, max_count, instances);
-  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   VisitObjects(&InstanceCollector::Callback, &collector);
-  self->EndAssertNoThreadSuspension(old_cause);
 }
 
 class ReferringObjectsFinder {
@@ -1513,8 +1497,8 @@
   }
 
  private:
-  mirror::Object* object_;
-  uint32_t max_count_;
+  const mirror::Object* const object_;
+  const uint32_t max_count_;
   std::vector<mirror::Object*>& referring_objects_;
   DISALLOW_COPY_AND_ASSIGN(ReferringObjectsFinder);
 };
@@ -1522,12 +1506,10 @@
 void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count,
                                std::vector<mirror::Object*>& referring_objects) {
   // Can't do any GC in this function since this may move the object o.
-  Thread* self = Thread::Current();
-  auto* old_cause = self->StartAssertNoThreadSuspension("GetReferringObjects");
+  ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetReferringObjects");
   ReferringObjectsFinder finder(o, max_count, referring_objects);
-  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   VisitObjects(&ReferringObjectsFinder::Callback, &finder);
-  self->EndAssertNoThreadSuspension(old_cause);
 }
 
 void Heap::CollectGarbage(bool clear_soft_references) {
@@ -1905,7 +1887,8 @@
   Thread* self = Thread::Current();
   MutexLock mu(self, zygote_creation_lock_);
   // Try to see if we have any Zygote spaces.
-  if (have_zygote_space_) {
+  if (HasZygoteSpace()) {
+    LOG(WARNING) << __FUNCTION__ << " called when we already have a zygote space.";
     return;
   }
   VLOG(heap) << "Starting PreZygoteFork";
@@ -1980,26 +1963,26 @@
     // from this point on.
     RemoveRememberedSet(old_alloc_space);
   }
-  space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space",
-                                                                        low_memory_mode_,
-                                                                        &non_moving_space_);
+  zygote_space_ = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_,
+                                                     &non_moving_space_);
   CHECK(!non_moving_space_->CanMoveObjects());
   if (same_space) {
     main_space_ = non_moving_space_;
     SetSpaceAsDefault(main_space_);
   }
   delete old_alloc_space;
-  CHECK(zygote_space != nullptr) << "Failed creating zygote space";
-  AddSpace(zygote_space);
+  CHECK(HasZygoteSpace()) << "Failed creating zygote space";
+  AddSpace(zygote_space_);
   non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
   AddSpace(non_moving_space_);
-  have_zygote_space_ = true;
-  // Enable large object space allocations.
-  large_object_threshold_ = kDefaultLargeObjectThreshold;
   // Create the zygote space mod union table.
   accounting::ModUnionTable* mod_union_table =
-      new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space);
+      new accounting::ModUnionTableCardCache("zygote space mod-union table", this,
+                                             zygote_space_);
   CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table";
+  // Set all the cards in the mod-union table since we don't know which objects contain references
+  // to large objects.
+  mod_union_table->SetCards();
   AddModUnionTable(mod_union_table);
   if (collector::SemiSpace::kUseRememberedSet) {
     // Add a new remembered set for the post-zygote non-moving space.
@@ -2032,6 +2015,7 @@
       } else if (bitmap2->HasAddress(obj)) {
         bitmap2->Set(obj);
       } else {
+        DCHECK(large_objects != nullptr);
         large_objects->Set(obj);
       }
     }
@@ -2069,7 +2053,7 @@
   // If the heap can't run the GC, silently fail and return that no GC was run.
   switch (gc_type) {
     case collector::kGcTypePartial: {
-      if (!have_zygote_space_) {
+      if (!HasZygoteSpace()) {
         return collector::kGcTypeNone;
       }
       break;
@@ -2601,6 +2585,17 @@
   }
 }
 
+void Heap::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+  if (kIsDebugBuild) {
+    if (rosalloc_space_ != nullptr) {
+      rosalloc_space_->AssertThreadLocalBuffersAreRevoked(thread);
+    }
+    if (bump_pointer_space_ != nullptr) {
+      bump_pointer_space_->AssertThreadLocalBuffersAreRevoked(thread);
+    }
+  }
+}
+
 void Heap::AssertAllBumpPointerSpaceThreadLocalBuffersAreRevoked() {
   if (kIsDebugBuild) {
     if (bump_pointer_space_ != nullptr) {
@@ -2882,7 +2877,7 @@
     next_gc_type_ = collector::kGcTypeSticky;
   } else {
     collector::GcType non_sticky_gc_type =
-        have_zygote_space_ ? collector::kGcTypePartial : collector::kGcTypeFull;
+        HasZygoteSpace() ? collector::kGcTypePartial : collector::kGcTypeFull;
     // Find what the next non sticky collector will be.
     collector::GarbageCollector* non_sticky_collector = FindCollectorByGcType(non_sticky_gc_type);
     // If the throughput of the current sticky GC >= throughput of the non sticky collector, then
@@ -3095,8 +3090,6 @@
   }
   env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
                             WellKnownClasses::java_lang_System_runFinalization);
-  env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
-                            WellKnownClasses::java_lang_System_runFinalization);
 }
 
 void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) {
@@ -3110,7 +3103,7 @@
   size_t new_native_bytes_allocated = native_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes);
   new_native_bytes_allocated += bytes;
   if (new_native_bytes_allocated > native_footprint_gc_watermark_) {
-    collector::GcType gc_type = have_zygote_space_ ? collector::kGcTypePartial :
+    collector::GcType gc_type = HasZygoteSpace() ? collector::kGcTypePartial :
         collector::kGcTypeFull;
 
     // The second watermark is higher than the gc watermark. If you hit this it means you are
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index cf297bd..faaea40 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -30,6 +30,7 @@
 #include "gc/collector/garbage_collector.h"
 #include "gc/collector/gc_type.h"
 #include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
 #include "globals.h"
 #include "gtest/gtest.h"
 #include "instruction_set.h"
@@ -79,6 +80,7 @@
 namespace space {
   class AllocSpace;
   class BumpPointerSpace;
+  class ContinuousMemMapAllocSpace;
   class DiscontinuousSpace;
   class DlMallocSpace;
   class ImageSpace;
@@ -87,7 +89,7 @@
   class RosAllocSpace;
   class Space;
   class SpaceTest;
-  class ContinuousMemMapAllocSpace;
+  class ZygoteSpace;
 }  // namespace space
 
 class AgeCardVisitor {
@@ -128,8 +130,6 @@
  public:
   // If true, measure the total allocation time.
   static constexpr bool kMeasureAllocationTime = false;
-  // Primitive arrays larger than this size are put in the large object space.
-  static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize;
   static constexpr size_t kDefaultStartingSize = kPageSize;
   static constexpr size_t kDefaultInitialSize = 2 * MB;
   static constexpr size_t kDefaultMaximumSize = 256 * MB;
@@ -141,7 +141,17 @@
   static constexpr size_t kDefaultTLABSize = 256 * KB;
   static constexpr double kDefaultTargetUtilization = 0.5;
   static constexpr double kDefaultHeapGrowthMultiplier = 2.0;
-
+  // Primitive arrays larger than this size are put in the large object space.
+  static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize;
+  // Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
+  // since this means that we have to use the slow msync loop in MemMap::MapAnonymous.
+#if USE_ART_LOW_4G_ALLOCATOR
+  static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
+      space::kLargeObjectSpaceTypeFreeList;
+#else
+  static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
+      space::kLargeObjectSpaceTypeMap;
+#endif
   // Used so that we don't overflow the allocation time atomic integer.
   static constexpr size_t kTimeAdjust = 1024;
 
@@ -160,6 +170,7 @@
                 const std::string& original_image_file_name,
                 InstructionSet image_instruction_set,
                 CollectorType foreground_collector_type, CollectorType background_collector_type,
+                space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold,
                 size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode,
                 size_t long_pause_threshold, size_t long_gc_threshold,
                 bool ignore_max_footprint, bool use_tlab,
@@ -458,7 +469,7 @@
                                                               bool fail_ok) const;
   space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const;
 
-  void DumpForSigQuit(std::ostream& os) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DumpForSigQuit(std::ostream& os);
 
   // Do a pending heap transition or trim.
   void DoPendingTransitionOrTrim() LOCKS_EXCLUDED(heap_trim_request_lock_);
@@ -469,6 +480,7 @@
   void RevokeThreadLocalBuffers(Thread* thread);
   void RevokeRosAllocThreadLocalBuffers(Thread* thread);
   void RevokeAllThreadLocalBuffers();
+  void AssertThreadLocalBuffersAreRevoked(Thread* thread);
   void AssertAllBumpPointerSpaceThreadLocalBuffersAreRevoked();
   void RosAllocVerification(TimingLogger* timings, const char* name)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -599,6 +611,10 @@
     return &reference_processor_;
   }
 
+  bool HasZygoteSpace() const {
+    return zygote_space_ != nullptr;
+  }
+
  private:
   // Compact source space to target space.
   void Compact(space::ContinuousMemMapAllocSpace* target_space,
@@ -676,7 +692,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template <bool kGrow>
-  bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
+  ALWAYS_INLINE bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
 
   // Returns true if the address passed in is within the address range of a continuous space.
   bool IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const
@@ -851,8 +867,9 @@
   // Lock which guards zygote space creation.
   Mutex zygote_creation_lock_;
 
-  // If we have a zygote space.
-  bool have_zygote_space_;
+  // Non-null iff we have a zygote space. Doesn't contain the large objects allocated before
+  // zygote space creation.
+  space::ZygoteSpace* zygote_space_;
 
   // Minimum allocation size of large object.
   size_t large_object_threshold_;
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 2a43712..dad5855 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -120,7 +120,7 @@
   mirror::Object* obj = reinterpret_cast<mirror::Object*>(mem_map->Begin());
   large_objects_.push_back(obj);
   mem_maps_.Put(obj, mem_map);
-  size_t allocation_size = mem_map->Size();
+  const size_t allocation_size = mem_map->BaseSize();
   DCHECK(bytes_allocated != nullptr);
   begin_ = std::min(begin_, reinterpret_cast<byte*>(obj));
   byte* obj_end = reinterpret_cast<byte*>(obj) + allocation_size;
@@ -145,8 +145,9 @@
     Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
     LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live";
   }
-  DCHECK_GE(num_bytes_allocated_, found->second->Size());
-  size_t allocation_size = found->second->Size();
+  const size_t map_size = found->second->BaseSize();
+  DCHECK_GE(num_bytes_allocated_, map_size);
+  size_t allocation_size = map_size;
   num_bytes_allocated_ -= allocation_size;
   --num_objects_allocated_;
   delete found->second;
@@ -158,7 +159,7 @@
   MutexLock mu(Thread::Current(), lock_);
   auto found = mem_maps_.find(obj);
   CHECK(found != mem_maps_.end()) << "Attempted to get size of a large object which is not live";
-  return found->second->Size();
+  return found->second->BaseSize();
 }
 
 size_t LargeObjectSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 9d5e090..a63c5c0 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -31,6 +31,12 @@
 
 class AllocationInfo;
 
+enum LargeObjectSpaceType {
+  kLargeObjectSpaceTypeDisabled,
+  kLargeObjectSpaceTypeMap,
+  kLargeObjectSpaceTypeFreeList,
+};
+
 // Abstraction implemented by all large object spaces.
 class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace {
  public:
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 92c6f53..3f39c77 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -338,6 +338,12 @@
   rosalloc_->RevokeAllThreadLocalRuns();
 }
 
+void RosAllocSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+  if (kIsDebugBuild) {
+    rosalloc_->AssertThreadLocalRunsAreRevoked(thread);
+  }
+}
+
 void RosAllocSpace::AssertAllThreadLocalBuffersAreRevoked() {
   if (kIsDebugBuild) {
     rosalloc_->AssertAllThreadLocalRunsAreRevoked();
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index f505305..f1ce115 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -101,6 +101,7 @@
 
   void RevokeThreadLocalBuffers(Thread* thread);
   void RevokeAllThreadLocalBuffers();
+  void AssertThreadLocalBuffersAreRevoked(Thread* thread);
   void AssertAllThreadLocalBuffersAreRevoked();
 
   // Returns the class of a recently freed object.
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 0291155..7211bb4 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -181,7 +181,7 @@
   // Succeeds, fits without adjusting the footprint limit.
   size_t ptr1_bytes_allocated, ptr1_usable_size;
   StackHandleScope<3> hs(soa.Self());
-  Handle<mirror::Object> ptr1(
+  MutableHandle<mirror::Object> ptr1(
       hs.NewHandle(Alloc(space, self, 1 * MB, &ptr1_bytes_allocated, &ptr1_usable_size)));
   EXPECT_TRUE(ptr1.Get() != nullptr);
   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
@@ -194,7 +194,7 @@
 
   // Succeeds, adjusts the footprint.
   size_t ptr3_bytes_allocated, ptr3_usable_size;
-  Handle<mirror::Object> ptr3(
+  MutableHandle<mirror::Object> ptr3(
       hs.NewHandle(AllocWithGrowth(space, self, 8 * MB, &ptr3_bytes_allocated, &ptr3_usable_size)));
   EXPECT_TRUE(ptr3.Get() != nullptr);
   EXPECT_LE(8U * MB, ptr3_bytes_allocated);
@@ -284,7 +284,7 @@
   // Succeeds, fits without adjusting the footprint limit.
   size_t ptr1_bytes_allocated, ptr1_usable_size;
   StackHandleScope<3> hs(soa.Self());
-  Handle<mirror::Object> ptr1(
+  MutableHandle<mirror::Object> ptr1(
       hs.NewHandle(Alloc(space, self, 1 * MB, &ptr1_bytes_allocated, &ptr1_usable_size)));
   EXPECT_TRUE(ptr1.Get() != nullptr);
   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
@@ -297,7 +297,7 @@
 
   // Succeeds, adjusts the footprint.
   size_t ptr3_bytes_allocated, ptr3_usable_size;
-  Handle<mirror::Object> ptr3(
+  MutableHandle<mirror::Object> ptr3(
       hs.NewHandle(AllocWithGrowth(space, self, 8 * MB, &ptr3_bytes_allocated, &ptr3_usable_size)));
   EXPECT_TRUE(ptr3.Get() != nullptr);
   EXPECT_LE(8U * MB, ptr3_bytes_allocated);
diff --git a/runtime/gc_root-inl.h b/runtime/gc_root-inl.h
index 2661e54..482f7bc 100644
--- a/runtime/gc_root-inl.h
+++ b/runtime/gc_root-inl.h
@@ -29,10 +29,5 @@
   return ReadBarrier::BarrierForRoot<MirrorType, kReadBarrierOption>(&root_);
 }
 
-template<class MirrorType>
-inline void GcRoot<MirrorType>::Assign(MirrorType* value) {
-  root_ = value;
-}
-
 }  // namespace art
 #endif  // ART_RUNTIME_GC_ROOT_INL_H_
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 3928f5d..b10a55c 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -28,7 +28,6 @@
  public:
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   ALWAYS_INLINE MirrorType* Read() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ALWAYS_INLINE void Assign(MirrorType* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) {
     callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
diff --git a/runtime/handle.h b/runtime/handle.h
index f70faf4..addb663 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -30,23 +30,23 @@
 
 // Handles are memory locations that contain GC roots. As the mirror::Object*s within a handle are
 // GC visible then the GC may move the references within them, something that couldn't be done with
-// a wrap pointer. Handles are generally allocated within HandleScopes. ConstHandle is a super-class
-// of Handle and doesn't support assignment operations.
+// a wrap pointer. Handles are generally allocated within HandleScopes. Handle is a super-class
+// of MutableHandle and doesn't support assignment operations.
 template<class T>
-class ConstHandle {
+class Handle {
  public:
-  ConstHandle() : reference_(nullptr) {
+  Handle() : reference_(nullptr) {
   }
 
-  ALWAYS_INLINE ConstHandle(const ConstHandle<T>& handle) : reference_(handle.reference_) {
+  ALWAYS_INLINE Handle(const Handle<T>& handle) : reference_(handle.reference_) {
   }
 
-  ALWAYS_INLINE ConstHandle<T>& operator=(const ConstHandle<T>& handle) {
+  ALWAYS_INLINE Handle<T>& operator=(const Handle<T>& handle) {
     reference_ = handle.reference_;
     return *this;
   }
 
-  ALWAYS_INLINE explicit ConstHandle(StackReference<T>* reference) : reference_(reference) {
+  ALWAYS_INLINE explicit Handle(StackReference<T>* reference) : reference_(reference) {
   }
 
   ALWAYS_INLINE T& operator*() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -73,11 +73,11 @@
   StackReference<T>* reference_;
 
   template<typename S>
-  explicit ConstHandle(StackReference<S>* reference)
+  explicit Handle(StackReference<S>* reference)
       : reference_(reinterpret_cast<StackReference<T>*>(reference)) {
   }
   template<typename S>
-  explicit ConstHandle(const ConstHandle<S>& handle)
+  explicit Handle(const Handle<S>& handle)
       : reference_(reinterpret_cast<StackReference<T>*>(handle.reference_)) {
   }
 
@@ -91,7 +91,7 @@
 
  private:
   friend class BuildGenericJniFrameVisitor;
-  template<class S> friend class ConstHandle;
+  template<class S> friend class Handle;
   friend class HandleScope;
   template<class S> friend class HandleWrapper;
   template<size_t kNumReferences> friend class StackHandleScope;
@@ -99,42 +99,43 @@
 
 // Handles that support assignment.
 template<class T>
-class Handle : public ConstHandle<T> {
+class MutableHandle : public Handle<T> {
  public:
-  Handle() {
+  MutableHandle() {
   }
 
-  ALWAYS_INLINE Handle(const Handle<T>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : ConstHandle<T>(handle.reference_) {
+  ALWAYS_INLINE MutableHandle(const MutableHandle<T>& handle)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Handle<T>(handle.reference_) {
   }
 
-  ALWAYS_INLINE Handle<T>& operator=(const Handle<T>& handle)
+  ALWAYS_INLINE MutableHandle<T>& operator=(const MutableHandle<T>& handle)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ConstHandle<T>::operator=(handle);
+    Handle<T>::operator=(handle);
     return *this;
   }
 
-  ALWAYS_INLINE explicit Handle(StackReference<T>* reference)
+  ALWAYS_INLINE explicit MutableHandle(StackReference<T>* reference)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : ConstHandle<T>(reference) {
+      : Handle<T>(reference) {
   }
 
   ALWAYS_INLINE T* Assign(T* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    StackReference<T>* ref = ConstHandle<T>::GetReference();
+    StackReference<T>* ref = Handle<T>::GetReference();
     T* const old = ref->AsMirrorPtr();
     ref->Assign(reference);
     return old;
   }
 
   template<typename S>
-  explicit Handle(const Handle<S>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : ConstHandle<T>(handle) {
+  explicit MutableHandle(const MutableHandle<S>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Handle<T>(handle) {
   }
 
  protected:
   template<typename S>
-  explicit Handle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : ConstHandle<T>(reference) {
+  explicit MutableHandle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Handle<T>(reference) {
   }
 
  private:
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 42ef779..99059f9 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -89,6 +89,12 @@
     return Handle<mirror::Object>(&references_[i]);
   }
 
+  MutableHandle<mirror::Object> GetMutableHandle(size_t i)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    DCHECK_LT(i, number_of_references_);
+    return MutableHandle<mirror::Object>(&references_[i]);
+  }
+
   void SetReference(size_t i, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       ALWAYS_INLINE {
     DCHECK_LT(i, number_of_references_);
@@ -139,14 +145,14 @@
 // A wrapper which wraps around Object** and restores the pointer in the destructor.
 // TODO: Add more functionality.
 template<class T>
-class HandleWrapper : public Handle<T> {
+class HandleWrapper : public MutableHandle<T> {
  public:
-  HandleWrapper(T** obj, const Handle<T>& handle)
-     : Handle<T>(handle), obj_(obj) {
+  HandleWrapper(T** obj, const MutableHandle<T>& handle)
+     : MutableHandle<T>(handle), obj_(obj) {
   }
 
   ~HandleWrapper() {
-    *obj_ = Handle<T>::Get();
+    *obj_ = MutableHandle<T>::Get();
   }
 
  private:
@@ -169,10 +175,10 @@
     return references_storage_[i].AsMirrorPtr();
   }
 
-  Handle<mirror::Object> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+  MutableHandle<mirror::Object> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       ALWAYS_INLINE {
     DCHECK_LT(i, number_of_references_);
-    return Handle<mirror::Object>(&references_storage_[i]);
+    return MutableHandle<mirror::Object>(&references_storage_[i]);
   }
 
   void SetReference(size_t i, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -182,9 +188,9 @@
   }
 
   template<class T>
-  Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     SetReference(pos_, object);
-    Handle<T> h(GetHandle(pos_));
+    MutableHandle<T> h(GetHandle(pos_));
     pos_++;
     return h;
   }
@@ -192,7 +198,7 @@
   template<class T>
   HandleWrapper<T> NewHandleWrapper(T** object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     SetReference(pos_, *object);
-    Handle<T> h(GetHandle(pos_));
+    MutableHandle<T> h(GetHandle(pos_));
     pos_++;
     return HandleWrapper<T>(object, h);
   }
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index c826716..7e770f6 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -19,6 +19,7 @@
 
 #include "indirect_reference_table.h"
 
+#include "runtime-inl.h"
 #include "verify_object-inl.h"
 
 namespace art {
@@ -46,7 +47,7 @@
     AbortIfNoCheckJNI();
     return false;
   }
-  if (UNLIKELY(table_[idx].IsNull())) {
+  if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
     LOG(ERROR) << "JNI ERROR (app bug): accessed deleted " << kind_ << " " << iref;
     AbortIfNoCheckJNI();
     return false;
@@ -73,15 +74,11 @@
 template<ReadBarrierOption kReadBarrierOption>
 inline mirror::Object* IndirectReferenceTable::Get(IndirectRef iref) const {
   if (!GetChecked(iref)) {
-    return kInvalidIndirectRefObject;
+    return nullptr;
   }
   uint32_t idx = ExtractIndex(iref);
-  mirror::Object* obj = table_[idx].Read<kWithoutReadBarrier>();
-  if (LIKELY(obj != kClearedJniWeakGlobal)) {
-    // The read barrier or VerifyObject won't handle kClearedJniWeakGlobal.
-    obj = table_[idx].Read();
-    VerifyObject(obj);
-  }
+  mirror::Object* obj = table_[idx].GetReference()->Read<kWithoutReadBarrier>();
+  VerifyObject(obj);
   return obj;
 }
 
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 9b2b82e..c1455fd 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -56,41 +56,30 @@
 
 void IndirectReferenceTable::AbortIfNoCheckJNI() {
   // If -Xcheck:jni is on, it'll give a more detailed error before aborting.
-  if (!Runtime::Current()->GetJavaVM()->check_jni) {
+  JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+  if (!vm->IsCheckJniEnabled()) {
     // Otherwise, we want to abort rather than hand back a bad reference.
     LOG(FATAL) << "JNI ERROR (app bug): see above.";
   }
 }
 
 IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
-                                               size_t maxCount, IndirectRefKind desiredKind) {
+                                               size_t maxCount, IndirectRefKind desiredKind)
+    : kind_(desiredKind),
+      max_entries_(maxCount) {
   CHECK_GT(initialCount, 0U);
   CHECK_LE(initialCount, maxCount);
   CHECK_NE(desiredKind, kHandleScopeOrInvalid);
 
   std::string error_str;
-  const size_t initial_bytes = initialCount * sizeof(const mirror::Object*);
-  const size_t table_bytes = maxCount * sizeof(const mirror::Object*);
+  const size_t table_bytes = maxCount * sizeof(IrtEntry);
   table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
                                             PROT_READ | PROT_WRITE, false, &error_str));
   CHECK(table_mem_map_.get() != nullptr) << error_str;
   CHECK_EQ(table_mem_map_->Size(), table_bytes);
-
-  table_ = reinterpret_cast<GcRoot<mirror::Object>*>(table_mem_map_->Begin());
+  table_ = reinterpret_cast<IrtEntry*>(table_mem_map_->Begin());
   CHECK(table_ != nullptr);
-  memset(table_, 0xd1, initial_bytes);
-
-  const size_t slot_bytes = maxCount * sizeof(IndirectRefSlot);
-  slot_mem_map_.reset(MemMap::MapAnonymous("indirect ref table slots", nullptr, slot_bytes,
-                                           PROT_READ | PROT_WRITE, false, &error_str));
-  CHECK(slot_mem_map_.get() != nullptr) << error_str;
-  slot_data_ = reinterpret_cast<IndirectRefSlot*>(slot_mem_map_->Begin());
-  CHECK(slot_data_ != nullptr);
-
   segment_state_.all = IRT_FIRST_SEGMENT;
-  alloc_entries_ = initialCount;
-  max_entries_ = maxCount;
-  kind_ = desiredKind;
 }
 
 IndirectReferenceTable::~IndirectReferenceTable() {
@@ -104,24 +93,12 @@
   CHECK(obj != NULL);
   VerifyObject(obj);
   DCHECK(table_ != NULL);
-  DCHECK_LE(alloc_entries_, max_entries_);
   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
 
-  if (topIndex == alloc_entries_) {
-    // reached end of allocated space; did we hit buffer max?
-    if (topIndex == max_entries_) {
-      LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
-                 << "(max=" << max_entries_ << ")\n"
-                 << MutatorLockedDumpable<IndirectReferenceTable>(*this);
-    }
-
-    size_t newSize = alloc_entries_ * 2;
-    if (newSize > max_entries_) {
-      newSize = max_entries_;
-    }
-    DCHECK_GT(newSize, alloc_entries_);
-
-    alloc_entries_ = newSize;
+  if (topIndex == max_entries_) {
+    LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
+               << "(max=" << max_entries_ << ")\n"
+               << MutatorLockedDumpable<IndirectReferenceTable>(*this);
   }
 
   // We know there's enough room in the table.  Now we just need to find
@@ -129,27 +106,26 @@
   // add to the end of the list.
   IndirectRef result;
   int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
+  size_t index;
   if (numHoles > 0) {
     DCHECK_GT(topIndex, 1U);
     // Find the first hole; likely to be near the end of the list.
-    GcRoot<mirror::Object>* pScan = &table_[topIndex - 1];
-    DCHECK(!pScan->IsNull());
+    IrtEntry* pScan = &table_[topIndex - 1];
+    DCHECK(!pScan->GetReference()->IsNull());
     --pScan;
-    while (!pScan->IsNull()) {
+    while (!pScan->GetReference()->IsNull()) {
       DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
       --pScan;
     }
-    UpdateSlotAdd(obj, pScan - table_);
-    result = ToIndirectRef(pScan - table_);
-    *pScan = GcRoot<mirror::Object>(obj);
+    index = pScan - table_;
     segment_state_.parts.numHoles--;
   } else {
     // Add to the end.
-    UpdateSlotAdd(obj, topIndex);
-    result = ToIndirectRef(topIndex);
-    table_[topIndex++] = GcRoot<mirror::Object>(obj);
+    index = topIndex++;
     segment_state_.parts.topIndex = topIndex;
   }
+  table_[index].Add(obj);
+  result = ToIndirectRef(index);
   if (false) {
     LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex
               << " holes=" << segment_state_.parts.numHoles;
@@ -160,10 +136,12 @@
 }
 
 void IndirectReferenceTable::AssertEmpty() {
-  if (UNLIKELY(begin() != end())) {
-    ScopedObjectAccess soa(Thread::Current());
-    LOG(FATAL) << "Internal Error: non-empty local reference table\n"
-               << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+  for (size_t i = 0; i < Capacity(); ++i) {
+    if (!table_[i].GetReference()->IsNull()) {
+      ScopedObjectAccess soa(Thread::Current());
+      LOG(FATAL) << "Internal Error: non-empty local reference table\n"
+                 << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+    }
   }
 }
 
@@ -182,7 +160,6 @@
   int bottomIndex = prevState.parts.topIndex;
 
   DCHECK(table_ != NULL);
-  DCHECK_LE(alloc_entries_, max_entries_);
   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
 
   int idx = ExtractIndex(iref);
@@ -192,7 +169,6 @@
     LOG(WARNING) << "Attempt to remove local handle scope entry from IRT, ignoring";
     return true;
   }
-
   if (idx < bottomIndex) {
     // Wrong segment.
     LOG(WARNING) << "Attempt to remove index outside index area (" << idx
@@ -206,23 +182,23 @@
     return false;
   }
 
-  if (idx == topIndex-1) {
+  if (idx == topIndex - 1) {
     // Top-most entry.  Scan up and consume holes.
 
     if (!CheckEntry("remove", iref, idx)) {
       return false;
     }
 
-    table_[idx] = GcRoot<mirror::Object>(nullptr);
+    *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
     int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
     if (numHoles != 0) {
       while (--topIndex > bottomIndex && numHoles != 0) {
         if (false) {
-          LOG(INFO) << "+++ checking for hole at " << topIndex-1
+          LOG(INFO) << "+++ checking for hole at " << topIndex - 1
                     << " (cookie=" << cookie << ") val="
-                    << table_[topIndex - 1].Read<kWithoutReadBarrier>();
+                    << table_[topIndex - 1].GetReference()->Read<kWithoutReadBarrier>();
         }
-        if (!table_[topIndex-1].IsNull()) {
+        if (!table_[topIndex - 1].GetReference()->IsNull()) {
           break;
         }
         if (false) {
@@ -242,7 +218,7 @@
     // Not the top-most entry.  This creates a hole.  We NULL out the
     // entry to prevent somebody from deleting it twice and screwing up
     // the hole count.
-    if (table_[idx].IsNull()) {
+    if (table_[idx].GetReference()->IsNull()) {
       LOG(INFO) << "--- WEIRD: removing null entry " << idx;
       return false;
     }
@@ -250,7 +226,7 @@
       return false;
     }
 
-    table_[idx] = GcRoot<mirror::Object>(nullptr);
+    *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
     segment_state_.parts.numHoles++;
     if (false) {
       LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles;
@@ -263,6 +239,11 @@
 void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
                                         RootType root_type) {
   for (auto ref : *this) {
+    if (*ref == nullptr) {
+      // Need to skip null entries to make it possible to do the
+      // non-null check after the call back.
+      continue;
+    }
     callback(ref, arg, tid, root_type);
     DCHECK(*ref != nullptr);
   }
@@ -272,15 +253,11 @@
   os << kind_ << " table dump:\n";
   ReferenceTable::Table entries;
   for (size_t i = 0; i < Capacity(); ++i) {
-    mirror::Object* obj = table_[i].Read<kWithoutReadBarrier>();
+    mirror::Object* obj = table_[i].GetReference()->Read<kWithoutReadBarrier>();
     if (UNLIKELY(obj == nullptr)) {
       // Remove NULLs.
-    } else if (UNLIKELY(obj == kClearedJniWeakGlobal)) {
-      // ReferenceTable::Dump() will handle kClearedJniWeakGlobal
-      // while the read barrier won't.
-      entries.push_back(GcRoot<mirror::Object>(obj));
     } else {
-      obj = table_[i].Read();
+      obj = table_[i].GetReference()->Read();
       entries.push_back(GcRoot<mirror::Object>(obj));
     }
   }
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fb910e2..168f9f2 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -25,16 +25,18 @@
 #include "base/logging.h"
 #include "base/mutex.h"
 #include "gc_root.h"
-#include "mem_map.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "read_barrier_option.h"
 
 namespace art {
+
 namespace mirror {
 class Object;
 }  // namespace mirror
 
+class MemMap;
+
 /*
  * Maintain a table of indirect references.  Used for local/global JNI
  * references.
@@ -103,10 +105,6 @@
  */
 typedef void* IndirectRef;
 
-// Magic failure values; must not pass Heap::ValidateObject() or Heap::IsHeapAddress().
-static mirror::Object* const kInvalidIndirectRefObject = reinterpret_cast<mirror::Object*>(0xdead4321);
-static mirror::Object* const kClearedJniWeakGlobal = reinterpret_cast<mirror::Object*>(0xdead1234);
-
 /*
  * Indirect reference kind, used as the two low bits of IndirectRef.
  *
@@ -127,16 +125,6 @@
   return static_cast<IndirectRefKind>(reinterpret_cast<uintptr_t>(iref) & 0x03);
 }
 
-/*
- * Extended debugging structure.  We keep a parallel array of these, one
- * per slot in the table.
- */
-static const size_t kIRTPrevCount = 4;
-struct IndirectRefSlot {
-  uint32_t serial;
-  const mirror::Object* previous[kIRTPrevCount];
-};
-
 /* use as initial value for "cookie", and when table has only one segment */
 static const uint32_t IRT_FIRST_SEGMENT = 0;
 
@@ -203,23 +191,47 @@
   } parts;
 };
 
+// Try to choose kIRTPrevCount so that sizeof(IrtEntry) is a power of 2.
+// Contains multiple entries but only one active one, this helps us detect use after free errors
+// since the serial stored in the indirect ref wont match.
+static const size_t kIRTPrevCount = kIsDebugBuild ? 7 : 3;
+class PACKED(4) IrtEntry {
+ public:
+  void Add(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ++serial_;
+    if (serial_ == kIRTPrevCount) {
+      serial_ = 0;
+    }
+    references_[serial_] = GcRoot<mirror::Object>(obj);
+  }
+  GcRoot<mirror::Object>* GetReference() {
+    DCHECK_LT(serial_, kIRTPrevCount);
+    return &references_[serial_];
+  }
+  uint32_t GetSerial() const {
+    return serial_;
+  }
+
+ private:
+  uint32_t serial_;
+  GcRoot<mirror::Object> references_[kIRTPrevCount];
+};
+
 class IrtIterator {
  public:
-  explicit IrtIterator(GcRoot<mirror::Object>* table, size_t i, size_t capacity)
+  explicit IrtIterator(IrtEntry* table, size_t i, size_t capacity)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : table_(table), i_(i), capacity_(capacity) {
-    SkipNullsAndTombstones();
   }
 
   IrtIterator& operator++() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ++i_;
-    SkipNullsAndTombstones();
     return *this;
   }
 
   mirror::Object** operator*() {
     // This does not have a read barrier as this is used to visit roots.
-    return table_[i_].AddressWithoutBarrier();
+    return table_[i_].GetReference()->AddressWithoutBarrier();
   }
 
   bool equals(const IrtIterator& rhs) const {
@@ -227,18 +239,9 @@
   }
 
  private:
-  void SkipNullsAndTombstones() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // We skip NULLs and tombstones. Clients don't want to see implementation details.
-    while (i_ < capacity_ &&
-           (table_[i_].IsNull() ||
-            table_[i_].Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal)) {
-      ++i_;
-    }
-  }
-
-  GcRoot<mirror::Object>* const table_;
+  IrtEntry* const table_;
   size_t i_;
-  size_t capacity_;
+  const size_t capacity_;
 };
 
 bool inline operator==(const IrtIterator& lhs, const IrtIterator& rhs) {
@@ -329,9 +332,7 @@
   }
 
  private:
-  /*
-   * Extract the table index from an indirect reference.
-   */
+  // Extract the table index from an indirect reference.
   static uint32_t ExtractIndex(IndirectRef iref) {
     uintptr_t uref = reinterpret_cast<uintptr_t>(iref);
     return (uref >> 2) & 0xffff;
@@ -343,25 +344,11 @@
    */
   IndirectRef ToIndirectRef(uint32_t tableIndex) const {
     DCHECK_LT(tableIndex, 65536U);
-    uint32_t serialChunk = slot_data_[tableIndex].serial;
-    uintptr_t uref = serialChunk << 20 | (tableIndex << 2) | kind_;
+    uint32_t serialChunk = table_[tableIndex].GetSerial();
+    uintptr_t uref = (serialChunk << 20) | (tableIndex << 2) | kind_;
     return reinterpret_cast<IndirectRef>(uref);
   }
 
-  /*
-   * Update extended debug info when an entry is added.
-   *
-   * We advance the serial number, invalidating any outstanding references to
-   * this slot.
-   */
-  void UpdateSlotAdd(const mirror::Object* obj, int slot) {
-    if (slot_data_ != NULL) {
-      IndirectRefSlot* pSlot = &slot_data_[slot];
-      pSlot->serial++;
-      pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
-    }
-  }
-
   // Abort if check_jni is not enabled.
   static void AbortIfNoCheckJNI();
 
@@ -374,19 +361,13 @@
 
   // Mem map where we store the indirect refs.
   std::unique_ptr<MemMap> table_mem_map_;
-  // Mem map where we store the extended debugging info.
-  std::unique_ptr<MemMap> slot_mem_map_;
   // bottom of the stack. Do not directly access the object references
   // in this as they are roots. Use Get() that has a read barrier.
-  GcRoot<mirror::Object>* table_;
+  IrtEntry* table_;
   /* bit mask, ORed into all irefs */
-  IndirectRefKind kind_;
-  /* extended debugging info */
-  IndirectRefSlot* slot_data_;
-  /* #of entries we have space for */
-  size_t alloc_entries_;
+  const IndirectRefKind kind_;
   /* max #of entries allowed */
-  size_t max_entries_;
+  const size_t max_entries_;
 };
 
 }  // namespace art
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index a33a981..99ee597 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -49,15 +49,15 @@
   IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal);
 
   mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
-  ASSERT_TRUE(c != NULL);
+  ASSERT_TRUE(c != nullptr);
   mirror::Object* obj0 = c->AllocObject(soa.Self());
-  ASSERT_TRUE(obj0 != NULL);
+  ASSERT_TRUE(obj0 != nullptr);
   mirror::Object* obj1 = c->AllocObject(soa.Self());
-  ASSERT_TRUE(obj1 != NULL);
+  ASSERT_TRUE(obj1 != nullptr);
   mirror::Object* obj2 = c->AllocObject(soa.Self());
-  ASSERT_TRUE(obj2 != NULL);
+  ASSERT_TRUE(obj2 != nullptr);
   mirror::Object* obj3 = c->AllocObject(soa.Self());
-  ASSERT_TRUE(obj3 != NULL);
+  ASSERT_TRUE(obj3 != nullptr);
 
   const uint32_t cookie = IRT_FIRST_SEGMENT;
 
@@ -68,13 +68,13 @@
 
   // Add three, check, remove in the order in which they were added.
   iref0 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref0 != NULL);
+  EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
   IndirectRef iref1 = irt.Add(cookie, obj1);
-  EXPECT_TRUE(iref1 != NULL);
+  EXPECT_TRUE(iref1 != nullptr);
   CheckDump(&irt, 2, 2);
   IndirectRef iref2 = irt.Add(cookie, obj2);
-  EXPECT_TRUE(iref2 != NULL);
+  EXPECT_TRUE(iref2 != nullptr);
   CheckDump(&irt, 3, 3);
 
   EXPECT_EQ(obj0, irt.Get(iref0));
@@ -92,15 +92,15 @@
   EXPECT_EQ(0U, irt.Capacity());
 
   // Get invalid entry (off the end of the list).
-  EXPECT_EQ(kInvalidIndirectRefObject, irt.Get(iref0));
+  EXPECT_TRUE(irt.Get(iref0) == nullptr);
 
   // Add three, remove in the opposite order.
   iref0 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref0 != NULL);
+  EXPECT_TRUE(iref0 != nullptr);
   iref1 = irt.Add(cookie, obj1);
-  EXPECT_TRUE(iref1 != NULL);
+  EXPECT_TRUE(iref1 != nullptr);
   iref2 = irt.Add(cookie, obj2);
-  EXPECT_TRUE(iref2 != NULL);
+  EXPECT_TRUE(iref2 != nullptr);
   CheckDump(&irt, 3, 3);
 
   ASSERT_TRUE(irt.Remove(cookie, iref2));
@@ -116,11 +116,11 @@
   // Add three, remove middle / middle / bottom / top.  (Second attempt
   // to remove middle should fail.)
   iref0 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref0 != NULL);
+  EXPECT_TRUE(iref0 != nullptr);
   iref1 = irt.Add(cookie, obj1);
-  EXPECT_TRUE(iref1 != NULL);
+  EXPECT_TRUE(iref1 != nullptr);
   iref2 = irt.Add(cookie, obj2);
-  EXPECT_TRUE(iref2 != NULL);
+  EXPECT_TRUE(iref2 != nullptr);
   CheckDump(&irt, 3, 3);
 
   ASSERT_EQ(3U, irt.Capacity());
@@ -131,7 +131,7 @@
   CheckDump(&irt, 2, 2);
 
   // Get invalid entry (from hole).
-  EXPECT_EQ(kInvalidIndirectRefObject, irt.Get(iref1));
+  EXPECT_TRUE(irt.Get(iref1) == nullptr);
 
   ASSERT_TRUE(irt.Remove(cookie, iref2));
   CheckDump(&irt, 1, 1);
@@ -145,20 +145,20 @@
   // is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
   // that we delete one and don't hole-compact the other.
   iref0 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref0 != NULL);
+  EXPECT_TRUE(iref0 != nullptr);
   iref1 = irt.Add(cookie, obj1);
-  EXPECT_TRUE(iref1 != NULL);
+  EXPECT_TRUE(iref1 != nullptr);
   iref2 = irt.Add(cookie, obj2);
-  EXPECT_TRUE(iref2 != NULL);
+  EXPECT_TRUE(iref2 != nullptr);
   IndirectRef iref3 = irt.Add(cookie, obj3);
-  EXPECT_TRUE(iref3 != NULL);
+  EXPECT_TRUE(iref3 != nullptr);
   CheckDump(&irt, 4, 4);
 
   ASSERT_TRUE(irt.Remove(cookie, iref1));
   CheckDump(&irt, 3, 3);
 
   iref1 = irt.Add(cookie, obj1);
-  EXPECT_TRUE(iref1 != NULL);
+  EXPECT_TRUE(iref1 != nullptr);
 
   ASSERT_EQ(4U, irt.Capacity()) << "hole not filled";
   CheckDump(&irt, 4, 4);
@@ -181,12 +181,12 @@
   // iref.  They have the same slot number but are for different objects.
   // With the extended checks in place, this should fail.
   iref0 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref0 != NULL);
+  EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_TRUE(irt.Remove(cookie, iref0));
   CheckDump(&irt, 0, 0);
   iref1 = irt.Add(cookie, obj1);
-  EXPECT_TRUE(iref1 != NULL);
+  EXPECT_TRUE(iref1 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded";
   CheckDump(&irt, 1, 1);
@@ -197,12 +197,12 @@
   // Same as above, but with the same object.  A more rigorous checker
   // (e.g. with slot serialization) will catch this.
   iref0 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref0 != NULL);
+  EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_TRUE(irt.Remove(cookie, iref0));
   CheckDump(&irt, 0, 0);
   iref1 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref1 != NULL);
+  EXPECT_TRUE(iref1 != nullptr);
   CheckDump(&irt, 1, 1);
   if (iref0 != iref1) {
     // Try 0, should not work.
@@ -212,15 +212,15 @@
   ASSERT_EQ(0U, irt.Capacity()) << "temporal del not empty";
   CheckDump(&irt, 0, 0);
 
-  // NULL isn't a valid iref.
-  ASSERT_EQ(kInvalidIndirectRefObject, irt.Get(NULL));
+  // nullptr isn't a valid iref.
+  ASSERT_TRUE(irt.Get(nullptr) == nullptr);
 
   // Stale lookup.
   iref0 = irt.Add(cookie, obj0);
-  EXPECT_TRUE(iref0 != NULL);
+  EXPECT_TRUE(iref0 != nullptr);
   CheckDump(&irt, 1, 1);
   ASSERT_TRUE(irt.Remove(cookie, iref0));
-  EXPECT_EQ(kInvalidIndirectRefObject, irt.Get(iref0)) << "stale lookup succeeded";
+  EXPECT_TRUE(irt.Get(iref0) == nullptr) << "stale lookup succeeded";
   CheckDump(&irt, 0, 0);
 
   // Test table resizing.
@@ -228,12 +228,12 @@
   IndirectRef manyRefs[kTableInitial];
   for (size_t i = 0; i < kTableInitial; i++) {
     manyRefs[i] = irt.Add(cookie, obj0);
-    ASSERT_TRUE(manyRefs[i] != NULL) << "Failed adding " << i;
+    ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
     CheckDump(&irt, i + 1, 1);
   }
   // ...this one causes overflow.
   iref0 = irt.Add(cookie, obj0);
-  ASSERT_TRUE(iref0 != NULL);
+  ASSERT_TRUE(iref0 != nullptr);
   ASSERT_EQ(kTableInitial + 1, irt.Capacity());
   CheckDump(&irt, kTableInitial + 1, 1);
 
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index ae8eeac..de6d0f4 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -56,6 +56,7 @@
 static constexpr size_t kArmPointerSize = 4;
 static constexpr size_t kArm64PointerSize = 8;
 static constexpr size_t kMipsPointerSize = 4;
+static constexpr size_t kMips64PointerSize = 8;
 static constexpr size_t kX86PointerSize = 4;
 static constexpr size_t kX86_64PointerSize = 8;
 
@@ -93,6 +94,8 @@
       return kX86_64PointerSize;
     case kMips:
       return kMipsPointerSize;
+    case kMips64:
+      return kMips64PointerSize;
     case kNone:
       LOG(FATAL) << "ISA kNone does not have pointer size.";
       return 0;
@@ -114,6 +117,7 @@
 
     case kArm64:
     case kX86_64:
+    case kMips64:
       return true;
 
     case kNone:
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 652d29b..15be6b7 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -85,9 +85,7 @@
 static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code,
                               const void* portable_code, bool have_portable_code)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#if defined(ART_USE_PORTABLE_COMPILER)
   method->SetEntryPointFromPortableCompiledCode(portable_code);
-#endif
   method->SetEntryPointFromQuickCompiledCode(quick_code);
   bool portable_enabled = method->IsPortableCompiled();
   if (have_portable_code && !portable_enabled) {
@@ -104,13 +102,9 @@
          && !method->IsNative() && !method->IsProxyMethod())) {
       if (kIsDebugBuild) {
         if (quick_code == GetQuickToInterpreterBridge()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
           DCHECK(portable_code == GetPortableToInterpreterBridge());
-#endif
         } else if (quick_code == class_linker->GetQuickResolutionTrampoline()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
           DCHECK(portable_code == class_linker->GetPortableResolutionTrampoline());
-#endif
         }
       }
       DCHECK(!method->IsNative()) << PrettyMethod(method);
@@ -138,32 +132,21 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   bool is_class_initialized = method->GetDeclaringClass()->IsInitialized();
   bool have_portable_code = false;
-#if !defined(ART_USE_PORTABLE_COMPILER)
-  new_portable_code = nullptr;
-#endif
   if (uninstall) {
     if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
       new_portable_code = GetPortableToInterpreterBridge();
-#endif
       new_quick_code = GetQuickToInterpreterBridge();
     } else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
       new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
-#endif
       new_quick_code = class_linker->GetQuickOatCodeFor(method);
     } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
       new_portable_code = class_linker->GetPortableResolutionTrampoline();
-#endif
       new_quick_code = class_linker->GetQuickResolutionTrampoline();
     }
   } else {  // !uninstall
     if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) &&
         !method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
       new_portable_code = GetPortableToInterpreterBridge();
-#endif
       new_quick_code = GetQuickToInterpreterBridge();
     } else {
       // Do not overwrite resolution trampoline. When the trampoline initializes the method's
@@ -171,21 +154,15 @@
       // For more details, see ClassLinker::FixupStaticTrampolines.
       if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
         if (entry_exit_stubs_installed_) {
-#if defined(ART_USE_PORTABLE_COMPILER)
           new_portable_code = GetPortableToInterpreterBridge();
-#endif
           new_quick_code = GetQuickInstrumentationEntryPoint();
         } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
           new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
-#endif
           new_quick_code = class_linker->GetQuickOatCodeFor(method);
           DCHECK(new_quick_code != class_linker->GetQuickToInterpreterBridgeTrampoline());
         }
       } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
         new_portable_code = class_linker->GetPortableResolutionTrampoline();
-#endif
         new_quick_code = class_linker->GetQuickResolutionTrampoline();
       }
     }
@@ -687,11 +664,7 @@
     new_have_portable_code = have_portable_code;
   } else {
     if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
       new_portable_code = GetPortableToInterpreterBridge();
-#else
-      new_portable_code = portable_code;
-#endif
       new_quick_code = GetQuickToInterpreterBridge();
       new_have_portable_code = false;
     } else {
@@ -699,20 +672,14 @@
       if (quick_code == class_linker->GetQuickResolutionTrampoline() ||
           quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() ||
           quick_code == GetQuickToInterpreterBridge()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
         DCHECK((portable_code == class_linker->GetPortableResolutionTrampoline()) ||
                (portable_code == GetPortableToInterpreterBridge()));
-#endif
         new_portable_code = portable_code;
         new_quick_code = quick_code;
         new_have_portable_code = have_portable_code;
       } else if (entry_exit_stubs_installed_) {
         new_quick_code = GetQuickInstrumentationEntryPoint();
-#if defined(ART_USE_PORTABLE_COMPILER)
         new_portable_code = GetPortableToInterpreterBridge();
-#else
-        new_portable_code = portable_code;
-#endif
         new_have_portable_code = false;
       } else {
         new_portable_code = portable_code;
@@ -794,12 +761,7 @@
         << " is already deoptimized";
   }
   if (!interpreter_stubs_installed_) {
-    UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(),
-#if defined(ART_USE_PORTABLE_COMPILER)
-                      GetPortableToInterpreterBridge(),
-#else
-                      nullptr,
-#endif
+    UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(), GetPortableToInterpreterBridge(),
                       false);
 
     // Install instrumentation exit stub and instrumentation frames. We may already have installed
@@ -833,20 +795,11 @@
         !method->GetDeclaringClass()->IsInitialized()) {
       // TODO: we're updating to entrypoints in the image here, we can avoid the trampoline.
       UpdateEntrypoints(method, class_linker->GetQuickResolutionTrampoline(),
-#if defined(ART_USE_PORTABLE_COMPILER)
-                        class_linker->GetPortableResolutionTrampoline(),
-#else
-                        nullptr,
-#endif
-                        false);
+                        class_linker->GetPortableResolutionTrampoline(), false);
     } else {
       bool have_portable_code = false;
       const void* quick_code = class_linker->GetQuickOatCodeFor(method);
-#if defined(ART_USE_PORTABLE_COMPILER)
       const void* portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
-#else
-      const void* portable_code = nullptr;
-#endif
       UpdateEntrypoints(method, quick_code, portable_code, have_portable_code);
     }
 
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index c66f99e..f6e6661 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -103,7 +103,7 @@
   Locks::intern_table_lock_->AssertHeld(Thread::Current());
   auto it = table->find(GcRoot<mirror::String>(s));
   if (LIKELY(it != table->end())) {
-    return const_cast<GcRoot<mirror::String>&>(*it).Read<kWithReadBarrier>();
+    return const_cast<GcRoot<mirror::String>&>(*it).Read();
   }
   return nullptr;
 }
@@ -299,7 +299,7 @@
     if (new_object == nullptr) {
       it = weak_interns_.erase(it);
     } else {
-      root.Assign(down_cast<mirror::String*>(new_object));
+      root = GcRoot<mirror::String>(down_cast<mirror::String*>(new_object));
       ++it;
     }
   }
@@ -309,8 +309,7 @@
   if (kIsDebugBuild) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
-  return static_cast<size_t>(
-      const_cast<GcRoot<mirror::String>&>(root).Read<kWithoutReadBarrier>()->GetHashCode());
+  return static_cast<size_t>(const_cast<GcRoot<mirror::String>&>(root).Read()->GetHashCode());
 }
 
 bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
@@ -318,8 +317,8 @@
   if (kIsDebugBuild) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
-  return const_cast<GcRoot<mirror::String>&>(a).Read<kWithoutReadBarrier>()->Equals(
-      const_cast<GcRoot<mirror::String>&>(b).Read<kWithoutReadBarrier>());
+  return const_cast<GcRoot<mirror::String>&>(a).Read()->Equals(
+      const_cast<GcRoot<mirror::String>&>(b).Read());
 }
 
 }  // namespace art
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 47a7f0d..07224ef 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -36,8 +36,8 @@
     mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(self, &element_class);
     DCHECK(array_class != nullptr);
     gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
-    result->SetL(mirror::Array::Alloc<true>(self, array_class, length,
-                                            array_class->GetComponentSize(), allocator, true));
+    result->SetL(mirror::Array::Alloc<true, true>(self, array_class, length,
+                                                  array_class->GetComponentSizeShift(), allocator));
   } else if (name == "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") {
     result->SetL(NULL);
   } else if (name == "java.lang.Class dalvik.system.VMStack.getStackClass2()") {
@@ -462,7 +462,7 @@
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
-    if (UNLIKELY(!class_linker->EnsureInitialized(h_class, true, true))) {
+    if (UNLIKELY(!class_linker->EnsureInitialized(self, h_class, true, true))) {
       CHECK(self->IsExceptionPending());
       self->PopShadowFrame();
       return;
@@ -537,7 +537,7 @@
       StackHandleScope<1> hs(self);
       HandleWrapper<Class> h_declaring_class(hs.NewHandleWrapper(&declaring_class));
       if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
-          h_declaring_class, true, true))) {
+          self, h_declaring_class, true, true))) {
         DCHECK(self->IsExceptionPending());
         self->PopShadowFrame();
         return;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 6705695..733f1d1 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -32,7 +32,7 @@
   const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
   const uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
   ArtField* f = FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
-                                                              Primitive::FieldSize(field_type));
+                                                              Primitive::ComponentSize(field_type));
   if (UNLIKELY(f == nullptr)) {
     CHECK(self->IsExceptionPending());
     return false;
@@ -96,22 +96,22 @@
     EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, true);
 
 // iget-XXX
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstanceObjectRead, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstanceObjectRead, Primitive::kPrimNot)
 
 // sget-XXX
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot)
 
 #undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
@@ -208,7 +208,7 @@
   bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
   uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
   ArtField* f = FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
-                                                              Primitive::FieldSize(field_type));
+                                                              Primitive::ComponentSize(field_type));
   if (UNLIKELY(f == nullptr)) {
     CHECK(self->IsExceptionPending());
     return false;
@@ -301,22 +301,22 @@
     EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, true);
 
 // iput-XXX
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstanceObjectWrite, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstanceObjectWrite, Primitive::kPrimNot)
 
 // sput-XXX
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot)
 
 #undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL
@@ -346,6 +346,18 @@
   }
   // Note: iput-x-quick instructions are only for non-volatile fields.
   switch (field_type) {
+    case Primitive::kPrimBoolean:
+      obj->SetFieldBoolean<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+      break;
+    case Primitive::kPrimByte:
+      obj->SetFieldByte<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+      break;
+    case Primitive::kPrimChar:
+      obj->SetFieldChar<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+      break;
+    case Primitive::kPrimShort:
+      obj->SetFieldShort<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+      break;
     case Primitive::kPrimInt:
       obj->SetField32<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
       break;
@@ -371,9 +383,13 @@
   EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, false);     \
   EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, true);
 
-EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimInt);    // iget-quick.
-EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimLong);   // iget-wide-quick.
-EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot);    // iget-object-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimInt)      // iput-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimBoolean)  // iput-boolean-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimByte)     // iput-byte-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimChar)     // iput-char-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimShort)    // iput-short-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimLong)     // iput-wide-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot)      // iput-object-quick.
 #undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
 
@@ -439,7 +455,7 @@
   Thread* const self_;
   StackHandleScope<1> handle_scope_;
   Handle<mirror::Throwable>* exception_;
-  Handle<mirror::ArtMethod> catch_method_;
+  MutableHandle<mirror::ArtMethod> catch_method_;
   uint32_t catch_dex_pc_;
   bool clear_exception_;
 
@@ -692,7 +708,8 @@
     }
     return false;
   }
-  Object* newArray = Array::Alloc<true>(self, arrayClass, length, arrayClass->GetComponentSize(),
+  Object* newArray = Array::Alloc<true>(self, arrayClass, length,
+                                        arrayClass->GetComponentSizeShift(),
                                         Runtime::Current()->GetHeap()->GetCurrentAllocator());
   if (UNLIKELY(newArray == NULL)) {
     DCHECK(self->IsExceptionPending());
@@ -784,7 +801,7 @@
   if (found != nullptr && initialize_class) {
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> h_class(hs.NewHandle(found));
-    if (!class_linker->EnsureInitialized(h_class, true, true)) {
+    if (!class_linker->EnsureInitialized(self, h_class, true, true)) {
       CHECK(self->IsExceptionPending());
       return;
     }
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 5a1d01e..a8345ad 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -192,7 +192,7 @@
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> h_class(hs.NewHandle(java_lang_string_class));
-    if (UNLIKELY(!class_linker->EnsureInitialized(h_class, true, true))) {
+    if (UNLIKELY(!class_linker->EnsureInitialized(self, h_class, true, true))) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
     }
@@ -388,11 +388,11 @@
   EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false);   \
   EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true);
 
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kStatic);      // invoke-static/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kDirect);      // invoke-direct/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kVirtual);     // invoke-virtual/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kSuper);       // invoke-super/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface);   // invoke-interface/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kStatic)      // invoke-static/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kDirect)      // invoke-direct/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kVirtual)     // invoke-virtual/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kSuper)       // invoke-super/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface)   // invoke-interface/range.
 #undef EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL
 
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index e098ac8..5c8a6c6 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -249,9 +249,7 @@
       // perform the memory barrier now.
       QuasiAtomic::ThreadFenceForConstructor();
     }
-    if (UNLIKELY(self->TestAllFlags())) {
-      CheckSuspend(self);
-    }
+    self->AllowThreadSuspension();
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -268,9 +266,7 @@
   HANDLE_INSTRUCTION_START(RETURN_VOID_BARRIER) {
     QuasiAtomic::ThreadFenceForConstructor();
     JValue result;
-    if (UNLIKELY(self->TestAllFlags())) {
-      CheckSuspend(self);
-    }
+    self->AllowThreadSuspension();
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -288,9 +284,7 @@
     JValue result;
     result.SetJ(0);
     result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
-    if (UNLIKELY(self->TestAllFlags())) {
-      CheckSuspend(self);
-    }
+    self->AllowThreadSuspension();
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -307,9 +301,7 @@
   HANDLE_INSTRUCTION_START(RETURN_WIDE) {
     JValue result;
     result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
-    if (UNLIKELY(self->TestAllFlags())) {
-      CheckSuspend(self);
-    }
+    self->AllowThreadSuspension();
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -325,9 +317,7 @@
 
   HANDLE_INSTRUCTION_START(RETURN_OBJECT) {
     JValue result;
-    if (UNLIKELY(self->TestAllFlags())) {
-      CheckSuspend(self);
-    }
+    self->AllowThreadSuspension();
     const uint8_t vreg_index = inst->VRegA_11x(inst_data);
     Object* obj_result = shadow_frame.GetVRegReference(vreg_index);
     if (do_assignability_check && obj_result != NULL) {
@@ -632,7 +622,7 @@
     int8_t offset = inst->VRegA_10t(inst_data);
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -644,7 +634,7 @@
     int16_t offset = inst->VRegA_20t();
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -656,7 +646,7 @@
     int32_t offset = inst->VRegA_30t();
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -668,7 +658,7 @@
     int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -680,7 +670,7 @@
     int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
-        CheckSuspend(self);
+        self->CheckSuspend();
         UPDATE_HANDLER_TABLE();
       }
     }
@@ -773,7 +763,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -789,7 +779,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -805,7 +795,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -821,7 +811,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -837,7 +827,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -853,7 +843,7 @@
       int16_t offset = inst->VRegC_22t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -869,7 +859,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -885,7 +875,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -901,7 +891,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -917,7 +907,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -933,7 +923,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -949,7 +939,7 @@
       int16_t offset = inst->VRegB_21t();
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
+          self->CheckSuspend();
           UPDATE_HANDLER_TABLE();
         }
       }
@@ -1369,6 +1359,30 @@
   }
   HANDLE_INSTRUCTION_END();
 
+  HANDLE_INSTRUCTION_START(IPUT_BOOLEAN_QUICK) {
+    bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+  }
+  HANDLE_INSTRUCTION_END();
+
+  HANDLE_INSTRUCTION_START(IPUT_BYTE_QUICK) {
+    bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+  }
+  HANDLE_INSTRUCTION_END();
+
+  HANDLE_INSTRUCTION_START(IPUT_CHAR_QUICK) {
+    bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+  }
+  HANDLE_INSTRUCTION_END();
+
+  HANDLE_INSTRUCTION_START(IPUT_SHORT_QUICK) {
+    bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(shadow_frame, inst, inst_data);
+    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+  }
+  HANDLE_INSTRUCTION_END();
+
   HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) {
     bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
@@ -2304,22 +2318,6 @@
     UnexpectedOpcode(inst, mh);
   HANDLE_INSTRUCTION_END();
 
-  HANDLE_INSTRUCTION_START(UNUSED_EB)
-    UnexpectedOpcode(inst, mh);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_EC)
-    UnexpectedOpcode(inst, mh);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_ED)
-    UnexpectedOpcode(inst, mh);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_EE)
-    UnexpectedOpcode(inst, mh);
-  HANDLE_INSTRUCTION_END();
-
   HANDLE_INSTRUCTION_START(UNUSED_EF)
     UnexpectedOpcode(inst, mh);
   HANDLE_INSTRUCTION_END();
@@ -2391,7 +2389,7 @@
   exception_pending_label: {
     CHECK(self->IsExceptionPending());
     if (UNLIKELY(self->TestAllFlags())) {
-      CheckSuspend(self);
+      self->CheckSuspend();
       UPDATE_HANDLER_TABLE();
     }
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 5401495..c6cef6a 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -22,9 +22,7 @@
 #define HANDLE_PENDING_EXCEPTION()                                                              \
   do {                                                                                          \
     DCHECK(self->IsExceptionPending());                                                         \
-    if (UNLIKELY(self->TestAllFlags())) {                                                       \
-      CheckSuspend(self);                                                                       \
-    }                                                                                           \
+    self->AllowThreadSuspension();                                                              \
     uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame,           \
                                                                   inst->GetDexPc(insns),        \
                                                                   instrumentation);             \
@@ -175,9 +173,7 @@
           // perform the memory barrier now.
           QuasiAtomic::ThreadFenceForConstructor();
         }
-        if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
-        }
+        self->AllowThreadSuspension();
         if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
           instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                            shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -191,9 +187,7 @@
       case Instruction::RETURN_VOID_BARRIER: {
         QuasiAtomic::ThreadFenceForConstructor();
         JValue result;
-        if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
-        }
+        self->AllowThreadSuspension();
         if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
           instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                            shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -208,9 +202,7 @@
         JValue result;
         result.SetJ(0);
         result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
-        if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
-        }
+        self->AllowThreadSuspension();
         if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
           instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                            shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -224,9 +216,7 @@
       case Instruction::RETURN_WIDE: {
         JValue result;
         result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
-        if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
-        }
+        self->AllowThreadSuspension();
         if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
           instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                            shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -239,9 +229,7 @@
       }
       case Instruction::RETURN_OBJECT: {
         JValue result;
-        if (UNLIKELY(self->TestAllFlags())) {
-          CheckSuspend(self);
-        }
+        self->AllowThreadSuspension();
         const size_t ref_idx = inst->VRegA_11x(inst_data);
         Object* obj_result = shadow_frame.GetVRegReference(ref_idx);
         if (do_assignability_check && obj_result != NULL) {
@@ -545,9 +533,7 @@
         PREAMBLE();
         int8_t offset = inst->VRegA_10t(inst_data);
         if (IsBackwardBranch(offset)) {
-          if (UNLIKELY(self->TestAllFlags())) {
-            CheckSuspend(self);
-          }
+          self->AllowThreadSuspension();
         }
         inst = inst->RelativeAt(offset);
         break;
@@ -556,9 +542,7 @@
         PREAMBLE();
         int16_t offset = inst->VRegA_20t();
         if (IsBackwardBranch(offset)) {
-          if (UNLIKELY(self->TestAllFlags())) {
-            CheckSuspend(self);
-          }
+          self->AllowThreadSuspension();
         }
         inst = inst->RelativeAt(offset);
         break;
@@ -567,9 +551,7 @@
         PREAMBLE();
         int32_t offset = inst->VRegA_30t();
         if (IsBackwardBranch(offset)) {
-          if (UNLIKELY(self->TestAllFlags())) {
-            CheckSuspend(self);
-          }
+          self->AllowThreadSuspension();
         }
         inst = inst->RelativeAt(offset);
         break;
@@ -578,9 +560,7 @@
         PREAMBLE();
         int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
         if (IsBackwardBranch(offset)) {
-          if (UNLIKELY(self->TestAllFlags())) {
-            CheckSuspend(self);
-          }
+          self->AllowThreadSuspension();
         }
         inst = inst->RelativeAt(offset);
         break;
@@ -589,9 +569,7 @@
         PREAMBLE();
         int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
         if (IsBackwardBranch(offset)) {
-          if (UNLIKELY(self->TestAllFlags())) {
-            CheckSuspend(self);
-          }
+          self->AllowThreadSuspension();
         }
         inst = inst->RelativeAt(offset);
         break;
@@ -682,9 +660,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -697,9 +673,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -712,9 +686,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -727,9 +699,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -742,9 +712,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -757,9 +725,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -772,9 +738,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
           int16_t offset = inst->VRegB_21t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -787,9 +751,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
           int16_t offset = inst->VRegB_21t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -802,9 +764,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
           int16_t offset = inst->VRegB_21t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -817,9 +777,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
           int16_t offset = inst->VRegB_21t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -832,9 +790,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
           int16_t offset = inst->VRegB_21t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -847,9 +803,7 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
           int16_t offset = inst->VRegB_21t();
           if (IsBackwardBranch(offset)) {
-            if (UNLIKELY(self->TestAllFlags())) {
-              CheckSuspend(self);
-            }
+            self->AllowThreadSuspension();
           }
           inst = inst->RelativeAt(offset);
         } else {
@@ -1266,6 +1220,30 @@
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
+      case Instruction::IPUT_BOOLEAN_QUICK: {
+        PREAMBLE();
+        bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
+      case Instruction::IPUT_BYTE_QUICK: {
+        PREAMBLE();
+        bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
+      case Instruction::IPUT_CHAR_QUICK: {
+        PREAMBLE();
+        bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
+      case Instruction::IPUT_SHORT_QUICK: {
+        PREAMBLE();
+        bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
       case Instruction::IPUT_WIDE_QUICK: {
         PREAMBLE();
         bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
@@ -2164,7 +2142,7 @@
         inst = inst->Next_2xx();
         break;
       case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
-      case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
+      case Instruction::UNUSED_EF ... Instruction::UNUSED_FF:
       case Instruction::UNUSED_79:
       case Instruction::UNUSED_7A:
         UnexpectedOpcode(inst, mh);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
new file mode 100644
index 0000000..1444d97
--- /dev/null
+++ b/runtime/java_vm_ext.cc
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 2011 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 "jni_internal.h"
+
+#include <dlfcn.h>
+
+#include "base/mutex.h"
+#include "base/stl_util.h"
+#include "check_jni.h"
+#include "indirect_reference_table-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "nativebridge/native_bridge.h"
+#include "java_vm_ext.h"
+#include "parsed_options.h"
+#include "runtime-inl.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace art {
+
+static size_t gGlobalsInitial = 512;  // Arbitrary.
+static size_t gGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
+
+static const size_t kWeakGlobalsInitial = 16;  // Arbitrary.
+static const size_t kWeakGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
+
+static bool IsBadJniVersion(int version) {
+  // We don't support JNI_VERSION_1_1. These are the only other valid versions.
+  return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
+}
+
+class SharedLibrary {
+ public:
+  SharedLibrary(JNIEnv* env, Thread* self, const std::string& path, void* handle,
+                jobject class_loader)
+      : path_(path),
+        handle_(handle),
+        needs_native_bridge_(false),
+        class_loader_(env->NewGlobalRef(class_loader)),
+        jni_on_load_lock_("JNI_OnLoad lock"),
+        jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
+        jni_on_load_thread_id_(self->GetThreadId()),
+        jni_on_load_result_(kPending) {
+  }
+
+  ~SharedLibrary() {
+    Thread* self = Thread::Current();
+    if (self != nullptr) {
+      self->GetJniEnv()->DeleteGlobalRef(class_loader_);
+    }
+  }
+
+  jobject GetClassLoader() const {
+    return class_loader_;
+  }
+
+  const std::string& GetPath() const {
+    return path_;
+  }
+
+  /*
+   * Check the result of an earlier call to JNI_OnLoad on this library.
+   * If the call has not yet finished in another thread, wait for it.
+   */
+  bool CheckOnLoadResult()
+      LOCKS_EXCLUDED(jni_on_load_lock_) {
+    Thread* self = Thread::Current();
+    bool okay;
+    {
+      MutexLock mu(self, jni_on_load_lock_);
+
+      if (jni_on_load_thread_id_ == self->GetThreadId()) {
+        // Check this so we don't end up waiting for ourselves.  We need to return "true" so the
+        // caller can continue.
+        LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
+        okay = true;
+      } else {
+        while (jni_on_load_result_ == kPending) {
+          VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]";
+          jni_on_load_cond_.Wait(self);
+        }
+
+        okay = (jni_on_load_result_ == kOkay);
+        VLOG(jni) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
+            << (okay ? "succeeded" : "failed") << "]";
+      }
+    }
+    return okay;
+  }
+
+  void SetResult(bool result) LOCKS_EXCLUDED(jni_on_load_lock_) {
+    Thread* self = Thread::Current();
+    MutexLock mu(self, jni_on_load_lock_);
+
+    jni_on_load_result_ = result ? kOkay : kFailed;
+    jni_on_load_thread_id_ = 0;
+
+    // Broadcast a wakeup to anybody sleeping on the condition variable.
+    jni_on_load_cond_.Broadcast(self);
+  }
+
+  void SetNeedsNativeBridge() {
+    needs_native_bridge_ = true;
+  }
+
+  bool NeedsNativeBridge() const {
+    return needs_native_bridge_;
+  }
+
+  void* FindSymbol(const std::string& symbol_name) {
+    return dlsym(handle_, symbol_name.c_str());
+  }
+
+  void* FindSymbolWithNativeBridge(const std::string& symbol_name, const char* shorty) {
+    CHECK(NeedsNativeBridge());
+
+    uint32_t len = 0;
+    return android::NativeBridgeGetTrampoline(handle_, symbol_name.c_str(), shorty, len);
+  }
+
+ private:
+  enum JNI_OnLoadState {
+    kPending,
+    kFailed,
+    kOkay,
+  };
+
+  // Path to library "/system/lib/libjni.so".
+  const std::string path_;
+
+  // The void* returned by dlopen(3).
+  void* const handle_;
+
+  // True if a native bridge is required.
+  bool needs_native_bridge_;
+
+  // The ClassLoader this library is associated with, a global JNI reference that is
+  // created/deleted with the scope of the library.
+  const jobject class_loader_;
+
+  // Guards remaining items.
+  Mutex jni_on_load_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  // Wait for JNI_OnLoad in other thread.
+  ConditionVariable jni_on_load_cond_ GUARDED_BY(jni_on_load_lock_);
+  // Recursive invocation guard.
+  uint32_t jni_on_load_thread_id_ GUARDED_BY(jni_on_load_lock_);
+  // Result of earlier JNI_OnLoad call.
+  JNI_OnLoadState jni_on_load_result_ GUARDED_BY(jni_on_load_lock_);
+};
+
+// This exists mainly to keep implementation details out of the header file.
+class Libraries {
+ public:
+  Libraries() {
+  }
+
+  ~Libraries() {
+    STLDeleteValues(&libraries_);
+  }
+
+  void Dump(std::ostream& os) const {
+    bool first = true;
+    for (const auto& library : libraries_) {
+      if (!first) {
+        os << ' ';
+      }
+      first = false;
+      os << library.first;
+    }
+  }
+
+  size_t size() const {
+    return libraries_.size();
+  }
+
+  SharedLibrary* Get(const std::string& path) {
+    auto it = libraries_.find(path);
+    return (it == libraries_.end()) ? nullptr : it->second;
+  }
+
+  void Put(const std::string& path, SharedLibrary* library) {
+    libraries_.Put(path, library);
+  }
+
+  // See section 11.3 "Linking Native Methods" of the JNI spec.
+  void* FindNativeMethod(mirror::ArtMethod* m, std::string& detail)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::jni_libraries_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    std::string jni_short_name(JniShortName(m));
+    std::string jni_long_name(JniLongName(m));
+    const mirror::ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
+    ScopedObjectAccessUnchecked soa(Thread::Current());
+    for (const auto& lib : libraries_) {
+      SharedLibrary* library = lib.second;
+      if (soa.Decode<mirror::ClassLoader*>(library->GetClassLoader()) != declaring_class_loader) {
+        // We only search libraries loaded by the appropriate ClassLoader.
+        continue;
+      }
+      // Try the short name then the long name...
+      void* fn;
+      if (library->NeedsNativeBridge()) {
+        const char* shorty = m->GetShorty();
+        fn = library->FindSymbolWithNativeBridge(jni_short_name, shorty);
+        if (fn == nullptr) {
+          fn = library->FindSymbolWithNativeBridge(jni_long_name, shorty);
+        }
+      } else {
+        fn = library->FindSymbol(jni_short_name);
+        if (fn == nullptr) {
+          fn = library->FindSymbol(jni_long_name);
+        }
+      }
+      if (fn == nullptr) {
+        fn = library->FindSymbol(jni_long_name);
+      }
+      if (fn != nullptr) {
+        VLOG(jni) << "[Found native code for " << PrettyMethod(m)
+                  << " in \"" << library->GetPath() << "\"]";
+        return fn;
+      }
+    }
+    detail += "No implementation found for ";
+    detail += PrettyMethod(m);
+    detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
+    LOG(ERROR) << detail;
+    return nullptr;
+  }
+
+ private:
+  AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibrarires> libraries_;
+};
+
+
+class JII {
+ public:
+  static jint DestroyJavaVM(JavaVM* vm) {
+    if (vm == nullptr) {
+      return JNI_ERR;
+    }
+    JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+    delete raw_vm->GetRuntime();
+    return JNI_OK;
+  }
+
+  static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    return AttachCurrentThreadInternal(vm, p_env, thr_args, false);
+  }
+
+  static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    return AttachCurrentThreadInternal(vm, p_env, thr_args, true);
+  }
+
+  static jint DetachCurrentThread(JavaVM* vm) {
+    if (vm == nullptr || Thread::Current() == nullptr) {
+      return JNI_ERR;
+    }
+    JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+    Runtime* runtime = raw_vm->GetRuntime();
+    runtime->DetachCurrentThread();
+    return JNI_OK;
+  }
+
+  static jint GetEnv(JavaVM* vm, void** env, jint version) {
+    // GetEnv always returns a JNIEnv* for the most current supported JNI version,
+    // and unlike other calls that take a JNI version doesn't care if you supply
+    // JNI_VERSION_1_1, which we don't otherwise support.
+    if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
+      LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
+      return JNI_EVERSION;
+    }
+    if (vm == nullptr || env == nullptr) {
+      return JNI_ERR;
+    }
+    Thread* thread = Thread::Current();
+    if (thread == nullptr) {
+      *env = nullptr;
+      return JNI_EDETACHED;
+    }
+    *env = thread->GetJniEnv();
+    return JNI_OK;
+  }
+
+ private:
+  static jint AttachCurrentThreadInternal(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
+    if (vm == nullptr || p_env == nullptr) {
+      return JNI_ERR;
+    }
+
+    // Return immediately if we're already attached.
+    Thread* self = Thread::Current();
+    if (self != nullptr) {
+      *p_env = self->GetJniEnv();
+      return JNI_OK;
+    }
+
+    Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->GetRuntime();
+
+    // No threads allowed in zygote mode.
+    if (runtime->IsZygote()) {
+      LOG(ERROR) << "Attempt to attach a thread in the zygote";
+      return JNI_ERR;
+    }
+
+    JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
+    const char* thread_name = nullptr;
+    jobject thread_group = nullptr;
+    if (args != nullptr) {
+      if (IsBadJniVersion(args->version)) {
+        LOG(ERROR) << "Bad JNI version passed to "
+                   << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
+                   << args->version;
+        return JNI_EVERSION;
+      }
+      thread_name = args->name;
+      thread_group = args->group;
+    }
+
+    if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group, !runtime->IsCompiler())) {
+      *p_env = nullptr;
+      return JNI_ERR;
+    } else {
+      *p_env = Thread::Current()->GetJniEnv();
+      return JNI_OK;
+    }
+  }
+};
+
+const JNIInvokeInterface gJniInvokeInterface = {
+  nullptr,  // reserved0
+  nullptr,  // reserved1
+  nullptr,  // reserved2
+  JII::DestroyJavaVM,
+  JII::AttachCurrentThread,
+  JII::DetachCurrentThread,
+  JII::GetEnv,
+  JII::AttachCurrentThreadAsDaemon
+};
+
+JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
+    : runtime_(runtime),
+      check_jni_abort_hook_(nullptr),
+      check_jni_abort_hook_data_(nullptr),
+      check_jni_(false),  // Initialized properly in the constructor body below.
+      force_copy_(options->force_copy_),
+      tracing_enabled_(!options->jni_trace_.empty() || VLOG_IS_ON(third_party_jni)),
+      trace_(options->jni_trace_),
+      globals_lock_("JNI global reference table lock"),
+      globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
+      libraries_(new Libraries),
+      unchecked_functions_(&gJniInvokeInterface),
+      weak_globals_lock_("JNI weak global reference table lock"),
+      weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
+      allow_new_weak_globals_(true),
+      weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
+  functions = unchecked_functions_;
+  if (options->check_jni_) {
+    SetCheckJniEnabled(true);
+  }
+}
+
+JavaVMExt::~JavaVMExt() {
+}
+
+void JavaVMExt::JniAbort(const char* jni_function_name, const char* msg) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  mirror::ArtMethod* current_method = self->GetCurrentMethod(nullptr);
+
+  std::ostringstream os;
+  os << "JNI DETECTED ERROR IN APPLICATION: " << msg;
+
+  if (jni_function_name != nullptr) {
+    os << "\n    in call to " << jni_function_name;
+  }
+  // TODO: is this useful given that we're about to dump the calling thread's stack?
+  if (current_method != nullptr) {
+    os << "\n    from " << PrettyMethod(current_method);
+  }
+  os << "\n";
+  self->Dump(os);
+
+  if (check_jni_abort_hook_ != nullptr) {
+    check_jni_abort_hook_(check_jni_abort_hook_data_, os.str());
+  } else {
+    // Ensure that we get a native stack trace for this thread.
+    self->TransitionFromRunnableToSuspended(kNative);
+    LOG(FATAL) << os.str();
+    self->TransitionFromSuspendedToRunnable();  // Unreachable, keep annotalysis happy.
+  }
+}
+
+void JavaVMExt::JniAbortV(const char* jni_function_name, const char* fmt, va_list ap) {
+  std::string msg;
+  StringAppendV(&msg, fmt, ap);
+  JniAbort(jni_function_name, msg.c_str());
+}
+
+void JavaVMExt::JniAbortF(const char* jni_function_name, const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  JniAbortV(jni_function_name, fmt, args);
+  va_end(args);
+}
+
+bool JavaVMExt::ShouldTrace(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Fast where no tracing is enabled.
+  if (trace_.empty() && !VLOG_IS_ON(third_party_jni)) {
+    return false;
+  }
+  // Perform checks based on class name.
+  StringPiece class_name(method->GetDeclaringClassDescriptor());
+  if (!trace_.empty() && class_name.find(trace_) != std::string::npos) {
+    return true;
+  }
+  if (!VLOG_IS_ON(third_party_jni)) {
+    return false;
+  }
+  // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
+  // like part of Android.
+  static const char* gBuiltInPrefixes[] = {
+      "Landroid/",
+      "Lcom/android/",
+      "Lcom/google/android/",
+      "Ldalvik/",
+      "Ljava/",
+      "Ljavax/",
+      "Llibcore/",
+      "Lorg/apache/harmony/",
+  };
+  for (size_t i = 0; i < arraysize(gBuiltInPrefixes); ++i) {
+    if (class_name.starts_with(gBuiltInPrefixes[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+jobject JavaVMExt::AddGlobalRef(Thread* self, mirror::Object* obj) {
+  // Check for null after decoding the object to handle cleared weak globals.
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  WriterMutexLock mu(self, globals_lock_);
+  IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj);
+  return reinterpret_cast<jobject>(ref);
+}
+
+jweak JavaVMExt::AddWeakGlobalRef(Thread* self, mirror::Object* obj) {
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  MutexLock mu(self, weak_globals_lock_);
+  while (UNLIKELY(!allow_new_weak_globals_)) {
+    weak_globals_add_condition_.WaitHoldingLocks(self);
+  }
+  IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
+  return reinterpret_cast<jweak>(ref);
+}
+
+void JavaVMExt::DeleteGlobalRef(Thread* self, jobject obj) {
+  if (obj == nullptr) {
+    return;
+  }
+  WriterMutexLock mu(self, globals_lock_);
+  if (!globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
+    LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
+                 << "failed to find entry";
+  }
+}
+
+void JavaVMExt::DeleteWeakGlobalRef(Thread* self, jweak obj) {
+  if (obj == nullptr) {
+    return;
+  }
+  MutexLock mu(self, weak_globals_lock_);
+  if (!weak_globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
+    LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
+                 << "failed to find entry";
+  }
+}
+
+static void ThreadEnableCheckJni(Thread* thread, void* arg) {
+  bool* check_jni = reinterpret_cast<bool*>(arg);
+  thread->GetJniEnv()->SetCheckJniEnabled(*check_jni);
+}
+
+bool JavaVMExt::SetCheckJniEnabled(bool enabled) {
+  bool old_check_jni = check_jni_;
+  check_jni_ = enabled;
+  functions = enabled ? GetCheckJniInvokeInterface() : unchecked_functions_;
+  MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+  runtime_->GetThreadList()->ForEach(ThreadEnableCheckJni, &check_jni_);
+  return old_check_jni;
+}
+
+void JavaVMExt::DumpForSigQuit(std::ostream& os) {
+  os << "JNI: CheckJNI is " << (check_jni_ ? "on" : "off");
+  if (force_copy_) {
+    os << " (with forcecopy)";
+  }
+  Thread* self = Thread::Current();
+  {
+    ReaderMutexLock mu(self, globals_lock_);
+    os << "; globals=" << globals_.Capacity();
+  }
+  {
+    MutexLock mu(self, weak_globals_lock_);
+    if (weak_globals_.Capacity() > 0) {
+      os << " (plus " << weak_globals_.Capacity() << " weak)";
+    }
+  }
+  os << '\n';
+
+  {
+    MutexLock mu(self, *Locks::jni_libraries_lock_);
+    os << "Libraries: " << Dumpable<Libraries>(*libraries_) << " (" << libraries_->size() << ")\n";
+  }
+}
+
+void JavaVMExt::DisallowNewWeakGlobals() {
+  MutexLock mu(Thread::Current(), weak_globals_lock_);
+  allow_new_weak_globals_ = false;
+}
+
+void JavaVMExt::AllowNewWeakGlobals() {
+  Thread* self = Thread::Current();
+  MutexLock mu(self, weak_globals_lock_);
+  allow_new_weak_globals_ = true;
+  weak_globals_add_condition_.Broadcast(self);
+}
+
+mirror::Object* JavaVMExt::DecodeGlobal(Thread* self, IndirectRef ref) {
+  return globals_.SynchronizedGet(self, &globals_lock_, ref);
+}
+
+mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
+  MutexLock mu(self, weak_globals_lock_);
+  while (UNLIKELY(!allow_new_weak_globals_)) {
+    weak_globals_add_condition_.WaitHoldingLocks(self);
+  }
+  return weak_globals_.Get(ref);
+}
+
+void JavaVMExt::DumpReferenceTables(std::ostream& os) {
+  Thread* self = Thread::Current();
+  {
+    ReaderMutexLock mu(self, globals_lock_);
+    globals_.Dump(os);
+  }
+  {
+    MutexLock mu(self, weak_globals_lock_);
+    weak_globals_.Dump(os);
+  }
+}
+
+bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,
+                                  std::string* error_msg) {
+  error_msg->clear();
+
+  // See if we've already loaded this library.  If we have, and the class loader
+  // matches, return successfully without doing anything.
+  // TODO: for better results we should canonicalize the pathname (or even compare
+  // inodes). This implementation is fine if everybody is using System.loadLibrary.
+  SharedLibrary* library;
+  Thread* self = Thread::Current();
+  {
+    // TODO: move the locking (and more of this logic) into Libraries.
+    MutexLock mu(self, *Locks::jni_libraries_lock_);
+    library = libraries_->Get(path);
+  }
+  if (library != nullptr) {
+    if (env->IsSameObject(library->GetClassLoader(), class_loader) == JNI_FALSE) {
+      // The library will be associated with class_loader. The JNI
+      // spec says we can't load the same library into more than one
+      // class loader.
+      StringAppendF(error_msg, "Shared library \"%s\" already opened by "
+          "ClassLoader %p; can't open in ClassLoader %p",
+          path.c_str(), library->GetClassLoader(), class_loader);
+      LOG(WARNING) << error_msg;
+      return false;
+    }
+    VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
+              << " ClassLoader " << class_loader << "]";
+    if (!library->CheckOnLoadResult()) {
+      StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
+          "to load \"%s\"", path.c_str());
+      return false;
+    }
+    return true;
+  }
+
+  // Open the shared library.  Because we're using a full path, the system
+  // doesn't have to search through LD_LIBRARY_PATH.  (It may do so to
+  // resolve this library's dependencies though.)
+
+  // Failures here are expected when java.library.path has several entries
+  // and we have to hunt for the lib.
+
+  // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
+  // class unloading. Libraries will only be unloaded when the reference count (incremented by
+  // dlopen) becomes zero from dlclose.
+
+  Locks::mutator_lock_->AssertNotHeld(self);
+  const char* path_str = path.empty() ? nullptr : path.c_str();
+  void* handle = dlopen(path_str, RTLD_LAZY);
+  bool needs_native_bridge = false;
+  if (handle == nullptr) {
+    if (android::NativeBridgeIsSupported(path_str)) {
+      handle = android::NativeBridgeLoadLibrary(path_str, RTLD_LAZY);
+      needs_native_bridge = true;
+    }
+  }
+
+  VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
+
+  if (handle == nullptr) {
+    *error_msg = dlerror();
+    LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << *error_msg;
+    return false;
+  }
+
+  if (env->ExceptionCheck() == JNI_TRUE) {
+    LOG(ERROR) << "Unexpected exception:";
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+  }
+  // Create a new entry.
+  // TODO: move the locking (and more of this logic) into Libraries.
+  bool created_library = false;
+  {
+    // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
+    std::unique_ptr<SharedLibrary> new_library(
+        new SharedLibrary(env, self, path, handle, class_loader));
+    MutexLock mu(self, *Locks::jni_libraries_lock_);
+    library = libraries_->Get(path);
+    if (library == nullptr) {  // We won race to get libraries_lock.
+      library = new_library.release();
+      libraries_->Put(path, library);
+      created_library = true;
+    }
+  }
+  if (!created_library) {
+    LOG(INFO) << "WOW: we lost a race to add shared library: "
+        << "\"" << path << "\" ClassLoader=" << class_loader;
+    return library->CheckOnLoadResult();
+  }
+  VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
+
+  bool was_successful = false;
+  void* sym;
+  if (needs_native_bridge) {
+    library->SetNeedsNativeBridge();
+    sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
+  } else {
+    sym = dlsym(handle, "JNI_OnLoad");
+  }
+  if (sym == nullptr) {
+    VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
+    was_successful = true;
+  } else {
+    // Call JNI_OnLoad.  We have to override the current class
+    // loader, which will always be "null" since the stuff at the
+    // top of the stack is around Runtime.loadLibrary().  (See
+    // the comments in the JNI FindClass function.)
+    ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
+    self->SetClassLoaderOverride(class_loader);
+
+    VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+    typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
+    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
+    int version = (*jni_on_load)(this, nullptr);
+
+    self->SetClassLoaderOverride(old_class_loader.get());
+
+    if (version == JNI_ERR) {
+      StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
+    } else if (IsBadJniVersion(version)) {
+      StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
+                    path.c_str(), version);
+      // It's unwise to call dlclose() here, but we can mark it
+      // as bad and ensure that future load attempts will fail.
+      // We don't know how far JNI_OnLoad got, so there could
+      // be some partially-initialized stuff accessible through
+      // newly-registered native method calls.  We could try to
+      // unregister them, but that doesn't seem worthwhile.
+    } else {
+      was_successful = true;
+    }
+    VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
+              << " from JNI_OnLoad in \"" << path << "\"]";
+  }
+
+  library->SetResult(was_successful);
+  return was_successful;
+}
+
+void* JavaVMExt::FindCodeForNativeMethod(mirror::ArtMethod* m) {
+  CHECK(m->IsNative());
+  mirror::Class* c = m->GetDeclaringClass();
+  // If this is a static method, it could be called before the class has been initialized.
+  CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m);
+  std::string detail;
+  void* native_method;
+  Thread* self = Thread::Current();
+  {
+    MutexLock mu(self, *Locks::jni_libraries_lock_);
+    native_method = libraries_->FindNativeMethod(m, detail);
+  }
+  // Throwing can cause libraries_lock to be reacquired.
+  if (native_method == nullptr) {
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
+  }
+  return native_method;
+}
+
+void JavaVMExt::SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg) {
+  MutexLock mu(Thread::Current(), weak_globals_lock_);
+  for (mirror::Object** entry : weak_globals_) {
+    // Since this is called by the GC, we don't need a read barrier.
+    mirror::Object* obj = *entry;
+    if (obj == nullptr) {
+      // Need to skip null here to distinguish between null entries
+      // and cleared weak ref entries.
+      continue;
+    }
+    mirror::Object* new_obj = callback(obj, arg);
+    if (new_obj == nullptr) {
+      new_obj = Runtime::Current()->GetClearedJniWeakGlobal();
+    }
+    *entry = new_obj;
+  }
+}
+
+void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
+  Thread* self = Thread::Current();
+  {
+    ReaderMutexLock mu(self, globals_lock_);
+    globals_.VisitRoots(callback, arg, 0, kRootJNIGlobal);
+  }
+  // The weak_globals table is visited by the GC itself (because it mutates the table).
+}
+
+// JNI Invocation interface.
+
+extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+  const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
+  if (IsBadJniVersion(args->version)) {
+    LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
+    return JNI_EVERSION;
+  }
+  RuntimeOptions options;
+  for (int i = 0; i < args->nOptions; ++i) {
+    JavaVMOption* option = &args->options[i];
+    options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
+  }
+  bool ignore_unrecognized = args->ignoreUnrecognized;
+  if (!Runtime::Create(options, ignore_unrecognized)) {
+    return JNI_ERR;
+  }
+  Runtime* runtime = Runtime::Current();
+  bool started = runtime->Start();
+  if (!started) {
+    delete Thread::Current()->GetJniEnv();
+    delete runtime->GetJavaVM();
+    LOG(WARNING) << "CreateJavaVM failed";
+    return JNI_ERR;
+  }
+  *p_env = Thread::Current()->GetJniEnv();
+  *p_vm = runtime->GetJavaVM();
+  return JNI_OK;
+}
+
+extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime == nullptr) {
+    *vm_count = 0;
+  } else {
+    *vm_count = 1;
+    vms[0] = runtime->GetJavaVM();
+  }
+  return JNI_OK;
+}
+
+// Historically unsupported.
+extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* /*vm_args*/) {
+  return JNI_ERR;
+}
+
+}  // namespace art
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
new file mode 100644
index 0000000..2957ba3
--- /dev/null
+++ b/runtime/java_vm_ext.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JAVA_VM_EXT_H_
+#define ART_RUNTIME_JAVA_VM_EXT_H_
+
+#include "jni.h"
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "indirect_reference_table.h"
+#include "reference_table.h"
+
+namespace art {
+
+namespace mirror {
+  class ArtMethod;
+  class Array;
+}  // namespace mirror
+
+class Libraries;
+class ParsedOptions;
+class Runtime;
+
+class JavaVMExt : public JavaVM {
+ public:
+  JavaVMExt(Runtime* runtime, ParsedOptions* options);
+  ~JavaVMExt();
+
+  bool ForceCopy() const {
+    return force_copy_;
+  }
+
+  bool IsCheckJniEnabled() const {
+    return check_jni_;
+  }
+
+  bool IsTracingEnabled() const {
+    return tracing_enabled_;
+  }
+
+  Runtime* GetRuntime() const {
+    return runtime_;
+  }
+
+  void SetCheckJniAbortHook(void (*hook)(void*, const std::string&), void* data) {
+    check_jni_abort_hook_ = hook;
+    check_jni_abort_hook_data_ = data;
+  }
+
+  // Aborts execution unless there is an abort handler installed in which case it will return. Its
+  // therefore important that callers return after aborting as otherwise code following the abort
+  // will be executed in the abort handler case.
+  void JniAbort(const char* jni_function_name, const char* msg);
+
+  void JniAbortV(const char* jni_function_name, const char* fmt, va_list ap);
+
+  void JniAbortF(const char* jni_function_name, const char* fmt, ...)
+      __attribute__((__format__(__printf__, 3, 4)));
+
+  // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
+  // when a native method that matches the -Xjnitrace argument calls a JNI function
+  // such as NewByteArray.
+  // If -verbose:third-party-jni is on, we want to log any JNI function calls
+  // made by a third-party native method.
+  bool ShouldTrace(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  /**
+   * Loads the given shared library. 'path' is an absolute pathname.
+   *
+   * Returns 'true' on success. On failure, sets 'detail' to a
+   * human-readable description of the error.
+   */
+  bool LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject javaLoader,
+                         std::string* error_msg);
+
+  /**
+   * Returns a pointer to the code for the native method 'm', found
+   * using dlsym(3) on every native library that's been loaded so far.
+   */
+  void* FindCodeForNativeMethod(mirror::ArtMethod* m)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void DumpForSigQuit(std::ostream& os)
+      LOCKS_EXCLUDED(Locks::jni_libraries_lock_, globals_lock_, weak_globals_lock_);
+
+  void DumpReferenceTables(std::ostream& os)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool SetCheckJniEnabled(bool enabled);
+
+  void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  jobject AddGlobalRef(Thread* self, mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  jweak AddWeakGlobalRef(Thread* self, mirror::Object* obj)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void DeleteGlobalRef(Thread* self, jobject obj);
+
+  void DeleteWeakGlobalRef(Thread* self, jweak obj);
+
+  void SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  mirror::Object* DecodeGlobal(Thread* self, IndirectRef ref)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  const JNIInvokeInterface* GetUncheckedFunctions() const {
+    return unchecked_functions_;
+  }
+
+ private:
+  Runtime* const runtime_;
+
+  // Used for testing. By default, we'll LOG(FATAL) the reason.
+  void (*check_jni_abort_hook_)(void* data, const std::string& reason);
+  void* check_jni_abort_hook_data_;
+
+  // Extra checking.
+  bool check_jni_;
+  bool force_copy_;
+  const bool tracing_enabled_;
+
+  // Extra diagnostics.
+  const std::string trace_;
+
+  // JNI global references.
+  ReaderWriterMutex globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
+  IndirectReferenceTable globals_;
+
+  std::unique_ptr<Libraries> libraries_ GUARDED_BY(Locks::jni_libraries_lock_);
+
+  // Used by -Xcheck:jni.
+  const JNIInvokeInterface* const unchecked_functions_;
+
+  // JNI weak global references.
+  Mutex weak_globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  // Since weak_globals_ contain weak roots, be careful not to
+  // directly access the object references in it. Use Get() with the
+  // read barrier enabled.
+  IndirectReferenceTable weak_globals_ GUARDED_BY(weak_globals_lock_);
+  bool allow_new_weak_globals_ GUARDED_BY(weak_globals_lock_);
+  ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
+
+  DISALLOW_COPY_AND_ASSIGN(JavaVMExt);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_JAVA_VM_EXT_H_
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index bb89813..0c9451c 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -299,7 +299,7 @@
 
  private:
   explicit JdwpState(const JdwpOptions* options);
-  size_t ProcessRequest(Request& request, ExpandBuf* pReply);
+  size_t ProcessRequest(Request* request, ExpandBuf* pReply);
   bool InvokeInProgress();
   bool IsConnected();
   void SuspendByPolicy(JdwpSuspendPolicy suspend_policy, JDWP::ObjectId thread_self_id)
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index e49a408..7c8c63c 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -783,7 +783,7 @@
                << StringPrintf(" (requestId=%#" PRIx32 ")", pEvent->requestId);
   }
   std::string thread_name;
-  JdwpError error = Dbg::GetThreadName(thread_id, thread_name);
+  JdwpError error = Dbg::GetThreadName(thread_id, &thread_name);
   if (error != JDWP::ERR_NONE) {
     thread_name = "<unknown>";
   }
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 1d56fe7..16a774f 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -65,7 +65,7 @@
 static JdwpError WriteTaggedObject(ExpandBuf* reply, ObjectId object_id)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   uint8_t tag;
-  JdwpError rc = Dbg::GetObjectTag(object_id, tag);
+  JdwpError rc = Dbg::GetObjectTag(object_id, &tag);
   if (rc == ERR_NONE) {
     expandBufAdd1(reply, tag);
     expandBufAddObjectId(reply, object_id);
@@ -91,13 +91,13 @@
  * If "is_constructor" is set, this returns "object_id" rather than the
  * expected-to-be-void return value of the called function.
  */
-static JdwpError FinishInvoke(JdwpState*, Request& request, ExpandBuf* pReply,
+static JdwpError FinishInvoke(JdwpState*, Request* request, ExpandBuf* pReply,
                               ObjectId thread_id, ObjectId object_id,
                               RefTypeId class_id, MethodId method_id, bool is_constructor)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   CHECK(!is_constructor || object_id != 0);
 
-  int32_t arg_count = request.ReadSigned32("argument count");
+  int32_t arg_count = request->ReadSigned32("argument count");
 
   VLOG(jdwp) << StringPrintf("    --> thread_id=%#" PRIx64 " object_id=%#" PRIx64,
                              thread_id, object_id);
@@ -109,14 +109,14 @@
   std::unique_ptr<JdwpTag[]> argTypes(arg_count > 0 ? new JdwpTag[arg_count] : NULL);
   std::unique_ptr<uint64_t[]> argValues(arg_count > 0 ? new uint64_t[arg_count] : NULL);
   for (int32_t i = 0; i < arg_count; ++i) {
-    argTypes[i] = request.ReadTag();
+    argTypes[i] = request->ReadTag();
     size_t width = Dbg::GetTagWidth(argTypes[i]);
-    argValues[i] = request.ReadValue(width);
+    argValues[i] = request->ReadValue(width);
     VLOG(jdwp) << "          " << argTypes[i] << StringPrintf("(%zd): %#" PRIx64, width,
                                                               argValues[i]);
   }
 
-  uint32_t options = request.ReadUnsigned32("InvokeOptions bit flags");
+  uint32_t options = request->ReadUnsigned32("InvokeOptions bit flags");
   VLOG(jdwp) << StringPrintf("        options=0x%04x%s%s", options,
                              (options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
                              (options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
@@ -166,7 +166,7 @@
   return err;
 }
 
-static JdwpError VM_Version(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_Version(JdwpState*, Request*, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Text information on runtime version.
   std::string version(StringPrintf("Android Runtime %s", Runtime::Current()->GetVersion()));
@@ -190,12 +190,12 @@
  * referenceTypeID.  We need to send back more than one if the class has
  * been loaded by multiple class loaders.
  */
-static JdwpError VM_ClassesBySignature(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError VM_ClassesBySignature(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  std::string classDescriptor(request.ReadUtf8String());
+  std::string classDescriptor(request->ReadUtf8String());
 
   std::vector<RefTypeId> ids;
-  Dbg::FindLoadedClassBySignature(classDescriptor.c_str(), ids);
+  Dbg::FindLoadedClassBySignature(classDescriptor.c_str(), &ids);
 
   expandBufAdd4BE(pReply, ids.size());
 
@@ -222,10 +222,10 @@
  * We exclude ourselves from the list, because we don't allow ourselves
  * to be suspended, and that violates some JDWP expectations.
  */
-static JdwpError VM_AllThreads(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_AllThreads(JdwpState*, Request*, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::vector<ObjectId> thread_ids;
-  Dbg::GetThreads(0, thread_ids);
+  Dbg::GetThreads(nullptr /* all thread groups */, &thread_ids);
 
   expandBufAdd4BE(pReply, thread_ids.size());
   for (uint32_t i = 0; i < thread_ids.size(); ++i) {
@@ -238,7 +238,7 @@
 /*
  * List all thread groups that do not have a parent.
  */
-static JdwpError VM_TopLevelThreadGroups(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_TopLevelThreadGroups(JdwpState*, Request*, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   /*
    * TODO: maintain a list of parentless thread groups in the VM.
@@ -259,7 +259,7 @@
  *
  * All IDs are 8 bytes.
  */
-static JdwpError VM_IDSizes(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_IDSizes(JdwpState*, Request*, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   expandBufAdd4BE(pReply, sizeof(FieldId));
   expandBufAdd4BE(pReply, sizeof(MethodId));
@@ -269,7 +269,7 @@
   return ERR_NONE;
 }
 
-static JdwpError VM_Dispose(JdwpState*, Request&, ExpandBuf*)
+static JdwpError VM_Dispose(JdwpState*, Request*, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Dbg::Disposed();
   return ERR_NONE;
@@ -281,7 +281,7 @@
  *
  * This needs to increment the "suspend count" on all threads.
  */
-static JdwpError VM_Suspend(JdwpState*, Request&, ExpandBuf*)
+static JdwpError VM_Suspend(JdwpState*, Request*, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Thread* self = Thread::Current();
   self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension);
@@ -293,16 +293,16 @@
 /*
  * Resume execution.  Decrements the "suspend count" of all threads.
  */
-static JdwpError VM_Resume(JdwpState*, Request&, ExpandBuf*)
+static JdwpError VM_Resume(JdwpState*, Request*, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Dbg::ProcessDelayedFullUndeoptimizations();
   Dbg::ResumeVM();
   return ERR_NONE;
 }
 
-static JdwpError VM_Exit(JdwpState* state, Request& request, ExpandBuf*)
+static JdwpError VM_Exit(JdwpState* state, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  uint32_t exit_status = request.ReadUnsigned32("exit_status");
+  uint32_t exit_status = request->ReadUnsigned32("exit_status");
   state->ExitAfterReplying(exit_status);
   return ERR_NONE;
 }
@@ -313,9 +313,9 @@
  * (Ctrl-Shift-I in Eclipse on an array of objects causes it to create the
  * string "java.util.Arrays".)
  */
-static JdwpError VM_CreateString(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError VM_CreateString(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  std::string str(request.ReadUtf8String());
+  std::string str(request->ReadUtf8String());
   ObjectId stringId = Dbg::CreateString(str);
   if (stringId == 0) {
     return ERR_OUT_OF_MEMORY;
@@ -324,7 +324,7 @@
   return ERR_NONE;
 }
 
-static JdwpError VM_ClassPaths(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_ClassPaths(JdwpState*, Request*, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   expandBufAddUtf8String(pReply, "/");
 
@@ -345,18 +345,18 @@
   return ERR_NONE;
 }
 
-static JdwpError VM_DisposeObjects(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError VM_DisposeObjects(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  size_t object_count = request.ReadUnsigned32("object_count");
+  size_t object_count = request->ReadUnsigned32("object_count");
   for (size_t i = 0; i < object_count; ++i) {
-    ObjectId object_id = request.ReadObjectId();
-    uint32_t reference_count = request.ReadUnsigned32("reference_count");
+    ObjectId object_id = request->ReadObjectId();
+    uint32_t reference_count = request->ReadUnsigned32("reference_count");
     Dbg::DisposeObject(object_id, reference_count);
   }
   return ERR_NONE;
 }
 
-static JdwpError VM_Capabilities(JdwpState*, Request&, ExpandBuf* reply)
+static JdwpError VM_Capabilities(JdwpState*, Request*, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   expandBufAdd1(reply, true);    // canWatchFieldModification
   expandBufAdd1(reply, true);    // canWatchFieldAccess
@@ -368,7 +368,7 @@
   return ERR_NONE;
 }
 
-static JdwpError VM_CapabilitiesNew(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError VM_CapabilitiesNew(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // The first few capabilities are the same as those reported by the older call.
   VM_Capabilities(NULL, request, reply);
@@ -398,7 +398,7 @@
 static JdwpError VM_AllClassesImpl(ExpandBuf* pReply, bool descriptor_and_status, bool generic)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::vector<JDWP::RefTypeId> classes;
-  Dbg::GetClassList(classes);
+  Dbg::GetClassList(&classes);
 
   expandBufAdd4BE(pReply, classes.size());
 
@@ -426,29 +426,29 @@
   return ERR_NONE;
 }
 
-static JdwpError VM_AllClasses(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_AllClasses(JdwpState*, Request*, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return VM_AllClassesImpl(pReply, true, false);
 }
 
-static JdwpError VM_AllClassesWithGeneric(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_AllClassesWithGeneric(JdwpState*, Request*, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return VM_AllClassesImpl(pReply, true, true);
 }
 
-static JdwpError VM_InstanceCounts(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError VM_InstanceCounts(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  int32_t class_count = request.ReadSigned32("class count");
+  int32_t class_count = request->ReadSigned32("class count");
   if (class_count < 0) {
     return ERR_ILLEGAL_ARGUMENT;
   }
   std::vector<RefTypeId> class_ids;
   for (int32_t i = 0; i < class_count; ++i) {
-    class_ids.push_back(request.ReadRefTypeId());
+    class_ids.push_back(request->ReadRefTypeId());
   }
 
   std::vector<uint64_t> counts;
-  JdwpError rc = Dbg::GetInstanceCounts(class_ids, counts);
+  JdwpError rc = Dbg::GetInstanceCounts(class_ids, &counts);
   if (rc != ERR_NONE) {
     return rc;
   }
@@ -460,22 +460,22 @@
   return ERR_NONE;
 }
 
-static JdwpError RT_Modifiers(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Modifiers(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   return Dbg::GetModifiers(refTypeId, pReply);
 }
 
 /*
  * Get values from static fields in a reference type.
  */
-static JdwpError RT_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
-  int32_t field_count = request.ReadSigned32("field count");
+  RefTypeId refTypeId = request->ReadRefTypeId();
+  int32_t field_count = request->ReadSigned32("field count");
   expandBufAdd4BE(pReply, field_count);
   for (int32_t i = 0; i < field_count; ++i) {
-    FieldId fieldId = request.ReadFieldId();
+    FieldId fieldId = request->ReadFieldId();
     JdwpError status = Dbg::GetStaticFieldValue(refTypeId, fieldId, pReply);
     if (status != ERR_NONE) {
       return status;
@@ -487,11 +487,11 @@
 /*
  * Get the name of the source file in which a reference type was declared.
  */
-static JdwpError RT_SourceFile(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_SourceFile(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   std::string source_file;
-  JdwpError status = Dbg::GetSourceFile(refTypeId, source_file);
+  JdwpError status = Dbg::GetSourceFile(refTypeId, &source_file);
   if (status != ERR_NONE) {
     return status;
   }
@@ -502,9 +502,9 @@
 /*
  * Return the current status of the reference type.
  */
-static JdwpError RT_Status(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Status(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   JDWP::JdwpTypeTag type_tag;
   uint32_t class_status;
   JDWP::JdwpError status = Dbg::GetClassInfo(refTypeId, &type_tag, &class_status, NULL);
@@ -518,20 +518,20 @@
 /*
  * Return interfaces implemented directly by this class.
  */
-static JdwpError RT_Interfaces(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Interfaces(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   return Dbg::OutputDeclaredInterfaces(refTypeId, pReply);
 }
 
 /*
  * Return the class object corresponding to this type.
  */
-static JdwpError RT_ClassObject(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_ClassObject(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   ObjectId class_object_id;
-  JdwpError status = Dbg::GetClassObject(refTypeId, class_object_id);
+  JdwpError status = Dbg::GetClassObject(refTypeId, &class_object_id);
   if (status != ERR_NONE) {
     return status;
   }
@@ -545,15 +545,15 @@
  *
  * JDB seems interested, but DEX files don't currently support this.
  */
-static JdwpError RT_SourceDebugExtension(JdwpState*, Request&, ExpandBuf*)
+static JdwpError RT_SourceDebugExtension(JdwpState*, Request*, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   /* referenceTypeId in, string out */
   return ERR_ABSENT_INFORMATION;
 }
 
-static JdwpError RT_Signature(JdwpState*, Request& request, ExpandBuf* pReply, bool with_generic)
+static JdwpError RT_Signature(JdwpState*, Request* request, ExpandBuf* pReply, bool with_generic)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
 
   std::string signature;
   JdwpError status = Dbg::GetSignature(refTypeId, &signature);
@@ -567,12 +567,12 @@
   return ERR_NONE;
 }
 
-static JdwpError RT_Signature(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Signature(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return RT_Signature(state, request, pReply, false);
 }
 
-static JdwpError RT_SignatureWithGeneric(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError RT_SignatureWithGeneric(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return RT_Signature(state, request, pReply, true);
 }
@@ -581,9 +581,9 @@
  * Return the instance of java.lang.ClassLoader that loaded the specified
  * reference type, or null if it was loaded by the system loader.
  */
-static JdwpError RT_ClassLoader(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_ClassLoader(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   return Dbg::GetClassLoader(refTypeId, pReply);
 }
 
@@ -591,16 +591,16 @@
  * Given a referenceTypeId, return a block of stuff that describes the
  * fields declared by a class.
  */
-static JdwpError RT_FieldsWithGeneric(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_FieldsWithGeneric(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   return Dbg::OutputDeclaredFields(refTypeId, true, pReply);
 }
 
 // Obsolete equivalent of FieldsWithGeneric, without the generic type information.
-static JdwpError RT_Fields(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Fields(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   return Dbg::OutputDeclaredFields(refTypeId, false, pReply);
 }
 
@@ -608,29 +608,29 @@
  * Given a referenceTypeID, return a block of goodies describing the
  * methods declared by a class.
  */
-static JdwpError RT_MethodsWithGeneric(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_MethodsWithGeneric(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   return Dbg::OutputDeclaredMethods(refTypeId, true, pReply);
 }
 
 // Obsolete equivalent of MethodsWithGeneric, without the generic type information.
-static JdwpError RT_Methods(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Methods(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
   return Dbg::OutputDeclaredMethods(refTypeId, false, pReply);
 }
 
-static JdwpError RT_Instances(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError RT_Instances(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_id = request.ReadRefTypeId();
-  int32_t max_count = request.ReadSigned32("max count");
+  RefTypeId class_id = request->ReadRefTypeId();
+  int32_t max_count = request->ReadSigned32("max count");
   if (max_count < 0) {
     return ERR_ILLEGAL_ARGUMENT;
   }
 
   std::vector<ObjectId> instances;
-  JdwpError rc = Dbg::GetInstances(class_id, max_count, instances);
+  JdwpError rc = Dbg::GetInstances(class_id, max_count, &instances);
   if (rc != ERR_NONE) {
     return rc;
   }
@@ -641,11 +641,11 @@
 /*
  * Return the immediate superclass of a class.
  */
-static JdwpError CT_Superclass(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError CT_Superclass(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_id = request.ReadRefTypeId();
+  RefTypeId class_id = request->ReadRefTypeId();
   RefTypeId superClassId;
-  JdwpError status = Dbg::GetSuperclass(class_id, superClassId);
+  JdwpError status = Dbg::GetSuperclass(class_id, &superClassId);
   if (status != ERR_NONE) {
     return status;
   }
@@ -656,18 +656,18 @@
 /*
  * Set static class values.
  */
-static JdwpError CT_SetValues(JdwpState* , Request& request, ExpandBuf*)
+static JdwpError CT_SetValues(JdwpState* , Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_id = request.ReadRefTypeId();
-  int32_t values_count = request.ReadSigned32("values count");
+  RefTypeId class_id = request->ReadRefTypeId();
+  int32_t values_count = request->ReadSigned32("values count");
 
   UNUSED(class_id);
 
   for (int32_t i = 0; i < values_count; ++i) {
-    FieldId fieldId = request.ReadFieldId();
+    FieldId fieldId = request->ReadFieldId();
     JDWP::JdwpTag fieldTag = Dbg::GetStaticFieldBasicTag(fieldId);
     size_t width = Dbg::GetTagWidth(fieldTag);
-    uint64_t value = request.ReadValue(width);
+    uint64_t value = request->ReadValue(width);
 
     VLOG(jdwp) << "    --> field=" << fieldId << " tag=" << fieldTag << " --> " << value;
     JdwpError status = Dbg::SetStaticFieldValue(fieldId, value, width);
@@ -685,11 +685,11 @@
  * Example: Eclipse sometimes uses java/lang/Class.forName(String s) on
  * values in the "variables" display.
  */
-static JdwpError CT_InvokeMethod(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError CT_InvokeMethod(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_id = request.ReadRefTypeId();
-  ObjectId thread_id = request.ReadThreadId();
-  MethodId method_id = request.ReadMethodId();
+  RefTypeId class_id = request->ReadRefTypeId();
+  ObjectId thread_id = request->ReadThreadId();
+  MethodId method_id = request->ReadMethodId();
 
   return FinishInvoke(state, request, pReply, thread_id, 0, class_id, method_id, false);
 }
@@ -701,14 +701,14 @@
  * Example: in IntelliJ, create a watch on "new String(myByteArray)" to
  * see the contents of a byte[] as a string.
  */
-static JdwpError CT_NewInstance(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError CT_NewInstance(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_id = request.ReadRefTypeId();
-  ObjectId thread_id = request.ReadThreadId();
-  MethodId method_id = request.ReadMethodId();
+  RefTypeId class_id = request->ReadRefTypeId();
+  ObjectId thread_id = request->ReadThreadId();
+  MethodId method_id = request->ReadMethodId();
 
   ObjectId object_id;
-  JdwpError status = Dbg::CreateObject(class_id, object_id);
+  JdwpError status = Dbg::CreateObject(class_id, &object_id);
   if (status != ERR_NONE) {
     return status;
   }
@@ -721,13 +721,13 @@
 /*
  * Create a new array object of the requested type and length.
  */
-static JdwpError AT_newInstance(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError AT_newInstance(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId arrayTypeId = request.ReadRefTypeId();
-  int32_t length = request.ReadSigned32("length");
+  RefTypeId arrayTypeId = request->ReadRefTypeId();
+  int32_t length = request->ReadSigned32("length");
 
   ObjectId object_id;
-  JdwpError status = Dbg::CreateArrayObject(arrayTypeId, length, object_id);
+  JdwpError status = Dbg::CreateArrayObject(arrayTypeId, length, &object_id);
   if (status != ERR_NONE) {
     return status;
   }
@@ -742,21 +742,21 @@
 /*
  * Return line number information for the method, if present.
  */
-static JdwpError M_LineTable(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError M_LineTable(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId refTypeId = request.ReadRefTypeId();
-  MethodId method_id = request.ReadMethodId();
+  RefTypeId refTypeId = request->ReadRefTypeId();
+  MethodId method_id = request->ReadMethodId();
 
   Dbg::OutputLineTable(refTypeId, method_id, pReply);
 
   return ERR_NONE;
 }
 
-static JdwpError M_VariableTable(JdwpState*, Request& request, ExpandBuf* pReply,
+static JdwpError M_VariableTable(JdwpState*, Request* request, ExpandBuf* pReply,
                                  bool generic)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_id = request.ReadRefTypeId();
-  MethodId method_id = request.ReadMethodId();
+  RefTypeId class_id = request->ReadRefTypeId();
+  MethodId method_id = request->ReadMethodId();
 
   // We could return ERR_ABSENT_INFORMATION here if the DEX file was built without local variable
   // information. That will cause Eclipse to make a best-effort attempt at displaying local
@@ -766,23 +766,23 @@
   return ERR_NONE;
 }
 
-static JdwpError M_VariableTable(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError M_VariableTable(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return M_VariableTable(state, request, pReply, false);
 }
 
-static JdwpError M_VariableTableWithGeneric(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError M_VariableTableWithGeneric(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return M_VariableTable(state, request, pReply, true);
 }
 
-static JdwpError M_Bytecodes(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError M_Bytecodes(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_id = request.ReadRefTypeId();
-  MethodId method_id = request.ReadMethodId();
+  RefTypeId class_id = request->ReadRefTypeId();
+  MethodId method_id = request->ReadMethodId();
 
   std::vector<uint8_t> bytecodes;
-  JdwpError rc = Dbg::GetBytecodes(class_id, method_id, bytecodes);
+  JdwpError rc = Dbg::GetBytecodes(class_id, method_id, &bytecodes);
   if (rc != ERR_NONE) {
     return rc;
   }
@@ -802,23 +802,23 @@
  * This can get called on different things, e.g. thread_id gets
  * passed in here.
  */
-static JdwpError OR_ReferenceType(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError OR_ReferenceType(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
+  ObjectId object_id = request->ReadObjectId();
   return Dbg::GetReferenceType(object_id, pReply);
 }
 
 /*
  * Get values from the fields of an object.
  */
-static JdwpError OR_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError OR_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
-  int32_t field_count = request.ReadSigned32("field count");
+  ObjectId object_id = request->ReadObjectId();
+  int32_t field_count = request->ReadSigned32("field count");
 
   expandBufAdd4BE(pReply, field_count);
   for (int32_t i = 0; i < field_count; ++i) {
-    FieldId fieldId = request.ReadFieldId();
+    FieldId fieldId = request->ReadFieldId();
     JdwpError status = Dbg::GetFieldValue(object_id, fieldId, pReply);
     if (status != ERR_NONE) {
       return status;
@@ -831,17 +831,17 @@
 /*
  * Set values in the fields of an object.
  */
-static JdwpError OR_SetValues(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError OR_SetValues(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
-  int32_t field_count = request.ReadSigned32("field count");
+  ObjectId object_id = request->ReadObjectId();
+  int32_t field_count = request->ReadSigned32("field count");
 
   for (int32_t i = 0; i < field_count; ++i) {
-    FieldId fieldId = request.ReadFieldId();
+    FieldId fieldId = request->ReadFieldId();
 
     JDWP::JdwpTag fieldTag = Dbg::GetFieldBasicTag(fieldId);
     size_t width = Dbg::GetTagWidth(fieldTag);
-    uint64_t value = request.ReadValue(width);
+    uint64_t value = request->ReadValue(width);
 
     VLOG(jdwp) << "    --> fieldId=" << fieldId << " tag=" << fieldTag << "(" << width << ") value=" << value;
     JdwpError status = Dbg::SetFieldValue(object_id, fieldId, value, width);
@@ -853,9 +853,9 @@
   return ERR_NONE;
 }
 
-static JdwpError OR_MonitorInfo(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError OR_MonitorInfo(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
+  ObjectId object_id = request->ReadObjectId();
   return Dbg::GetMonitorInfo(object_id, reply);
 }
 
@@ -870,47 +870,47 @@
  * object), it will try to invoke the object's toString() function.  This
  * feature becomes crucial when examining ArrayLists with Eclipse.
  */
-static JdwpError OR_InvokeMethod(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError OR_InvokeMethod(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
-  ObjectId thread_id = request.ReadThreadId();
-  RefTypeId class_id = request.ReadRefTypeId();
-  MethodId method_id = request.ReadMethodId();
+  ObjectId object_id = request->ReadObjectId();
+  ObjectId thread_id = request->ReadThreadId();
+  RefTypeId class_id = request->ReadRefTypeId();
+  MethodId method_id = request->ReadMethodId();
 
   return FinishInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, false);
 }
 
-static JdwpError OR_DisableCollection(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError OR_DisableCollection(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
+  ObjectId object_id = request->ReadObjectId();
   return Dbg::DisableCollection(object_id);
 }
 
-static JdwpError OR_EnableCollection(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError OR_EnableCollection(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
+  ObjectId object_id = request->ReadObjectId();
   return Dbg::EnableCollection(object_id);
 }
 
-static JdwpError OR_IsCollected(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError OR_IsCollected(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
+  ObjectId object_id = request->ReadObjectId();
   bool is_collected;
-  JdwpError rc = Dbg::IsCollected(object_id, is_collected);
+  JdwpError rc = Dbg::IsCollected(object_id, &is_collected);
   expandBufAdd1(pReply, is_collected ? 1 : 0);
   return rc;
 }
 
-static JdwpError OR_ReferringObjects(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError OR_ReferringObjects(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId object_id = request.ReadObjectId();
-  int32_t max_count = request.ReadSigned32("max count");
+  ObjectId object_id = request->ReadObjectId();
+  int32_t max_count = request->ReadSigned32("max count");
   if (max_count < 0) {
     return ERR_ILLEGAL_ARGUMENT;
   }
 
   std::vector<ObjectId> referring_objects;
-  JdwpError rc = Dbg::GetReferringObjects(object_id, max_count, referring_objects);
+  JdwpError rc = Dbg::GetReferringObjects(object_id, max_count, &referring_objects);
   if (rc != ERR_NONE) {
     return rc;
   }
@@ -921,9 +921,9 @@
 /*
  * Return the string value in a string object.
  */
-static JdwpError SR_Value(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError SR_Value(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId stringObject = request.ReadObjectId();
+  ObjectId stringObject = request->ReadObjectId();
   std::string str;
   JDWP::JdwpError error = Dbg::StringToUtf8(stringObject, &str);
   if (error != JDWP::ERR_NONE) {
@@ -940,12 +940,12 @@
 /*
  * Return a thread's name.
  */
-static JdwpError TR_Name(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_Name(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
 
   std::string name;
-  JdwpError error = Dbg::GetThreadName(thread_id, name);
+  JdwpError error = Dbg::GetThreadName(thread_id, &name);
   if (error != ERR_NONE) {
     return error;
   }
@@ -961,9 +961,9 @@
  * It's supposed to remain suspended even if interpreted code wants to
  * resume it; only the JDI is allowed to resume it.
  */
-static JdwpError TR_Suspend(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError TR_Suspend(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
 
   if (thread_id == Dbg::GetThreadSelfId()) {
     LOG(INFO) << "  Warning: ignoring request to suspend self";
@@ -980,9 +980,9 @@
 /*
  * Resume the specified thread.
  */
-static JdwpError TR_Resume(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError TR_Resume(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
 
   if (thread_id == Dbg::GetThreadSelfId()) {
     LOG(INFO) << "  Warning: ignoring request to resume self";
@@ -998,9 +998,9 @@
 /*
  * Return status of specified thread.
  */
-static JdwpError TR_Status(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_Status(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
 
   JDWP::JdwpThreadStatus threadStatus;
   JDWP::JdwpSuspendStatus suspendStatus;
@@ -1020,9 +1020,9 @@
 /*
  * Return the thread group that the specified thread is a member of.
  */
-static JdwpError TR_ThreadGroup(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_ThreadGroup(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
   return Dbg::GetThreadGroup(thread_id, pReply);
 }
 
@@ -1032,14 +1032,14 @@
  * If the thread isn't suspended, the error code isn't defined, but should
  * be THREAD_NOT_SUSPENDED.
  */
-static JdwpError TR_Frames(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_Frames(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
-  uint32_t start_frame = request.ReadUnsigned32("start frame");
-  uint32_t length = request.ReadUnsigned32("length");
+  ObjectId thread_id = request->ReadThreadId();
+  uint32_t start_frame = request->ReadUnsigned32("start frame");
+  uint32_t length = request->ReadUnsigned32("length");
 
   size_t actual_frame_count;
-  JdwpError error = Dbg::GetThreadFrameCount(thread_id, actual_frame_count);
+  JdwpError error = Dbg::GetThreadFrameCount(thread_id, &actual_frame_count);
   if (error != ERR_NONE) {
     return error;
   }
@@ -1064,12 +1064,12 @@
 /*
  * Returns the #of frames on the specified thread, which must be suspended.
  */
-static JdwpError TR_FrameCount(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_FrameCount(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
 
   size_t frame_count;
-  JdwpError rc = Dbg::GetThreadFrameCount(thread_id, frame_count);
+  JdwpError rc = Dbg::GetThreadFrameCount(thread_id, &frame_count);
   if (rc != ERR_NONE) {
     return rc;
   }
@@ -1078,13 +1078,13 @@
   return ERR_NONE;
 }
 
-static JdwpError TR_OwnedMonitors(Request& request, ExpandBuf* reply, bool with_stack_depths)
+static JdwpError TR_OwnedMonitors(Request* request, ExpandBuf* reply, bool with_stack_depths)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
 
   std::vector<ObjectId> monitors;
   std::vector<uint32_t> stack_depths;
-  JdwpError rc = Dbg::GetOwnedMonitors(thread_id, monitors, stack_depths);
+  JdwpError rc = Dbg::GetOwnedMonitors(thread_id, &monitors, &stack_depths);
   if (rc != ERR_NONE) {
     return rc;
   }
@@ -1102,31 +1102,31 @@
   return ERR_NONE;
 }
 
-static JdwpError TR_OwnedMonitors(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_OwnedMonitors(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return TR_OwnedMonitors(request, reply, false);
 }
 
-static JdwpError TR_OwnedMonitorsStackDepthInfo(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_OwnedMonitorsStackDepthInfo(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return TR_OwnedMonitors(request, reply, true);
 }
 
-static JdwpError TR_CurrentContendedMonitor(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_CurrentContendedMonitor(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
 
   ObjectId contended_monitor;
-  JdwpError rc = Dbg::GetContendedMonitor(thread_id, contended_monitor);
+  JdwpError rc = Dbg::GetContendedMonitor(thread_id, &contended_monitor);
   if (rc != ERR_NONE) {
     return rc;
   }
   return WriteTaggedObject(reply, contended_monitor);
 }
 
-static JdwpError TR_Interrupt(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_Interrupt(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
   return Dbg::Interrupt(thread_id);
 }
 
@@ -1136,9 +1136,9 @@
  * (The thread *might* still be running -- it might not have examined
  * its suspend count recently.)
  */
-static JdwpError TR_DebugSuspendCount(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_DebugSuspendCount(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
+  ObjectId thread_id = request->ReadThreadId();
   return Dbg::GetThreadDebugSuspendCount(thread_id, pReply);
 }
 
@@ -1147,63 +1147,41 @@
  *
  * The Eclipse debugger recognizes "main" and "system" as special.
  */
-static JdwpError TGR_Name(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TGR_Name(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_group_id = request.ReadThreadGroupId();
-
-  expandBufAddUtf8String(pReply, Dbg::GetThreadGroupName(thread_group_id));
-
-  return ERR_NONE;
+  ObjectId thread_group_id = request->ReadThreadGroupId();
+  return Dbg::GetThreadGroupName(thread_group_id, pReply);
 }
 
 /*
  * Returns the thread group -- if any -- that contains the specified
  * thread group.
  */
-static JdwpError TGR_Parent(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TGR_Parent(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_group_id = request.ReadThreadGroupId();
-
-  ObjectId parentGroup = Dbg::GetThreadGroupParent(thread_group_id);
-  expandBufAddObjectId(pReply, parentGroup);
-
-  return ERR_NONE;
+  ObjectId thread_group_id = request->ReadThreadGroupId();
+  return Dbg::GetThreadGroupParent(thread_group_id, pReply);
 }
 
 /*
  * Return the active threads and thread groups that are part of the
  * specified thread group.
  */
-static JdwpError TGR_Children(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TGR_Children(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_group_id = request.ReadThreadGroupId();
-
-  std::vector<ObjectId> thread_ids;
-  Dbg::GetThreads(thread_group_id, thread_ids);
-  expandBufAdd4BE(pReply, thread_ids.size());
-  for (uint32_t i = 0; i < thread_ids.size(); ++i) {
-    expandBufAddObjectId(pReply, thread_ids[i]);
-  }
-
-  std::vector<ObjectId> child_thread_groups_ids;
-  Dbg::GetChildThreadGroups(thread_group_id, child_thread_groups_ids);
-  expandBufAdd4BE(pReply, child_thread_groups_ids.size());
-  for (uint32_t i = 0; i < child_thread_groups_ids.size(); ++i) {
-    expandBufAddObjectId(pReply, child_thread_groups_ids[i]);
-  }
-
-  return ERR_NONE;
+  ObjectId thread_group_id = request->ReadThreadGroupId();
+  return Dbg::GetThreadGroupChildren(thread_group_id, pReply);
 }
 
 /*
  * Return the #of components in the array.
  */
-static JdwpError AR_Length(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError AR_Length(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId array_id = request.ReadArrayId();
+  ObjectId array_id = request->ReadArrayId();
 
-  int length;
-  JdwpError status = Dbg::GetArrayLength(array_id, length);
+  int32_t length;
+  JdwpError status = Dbg::GetArrayLength(array_id, &length);
   if (status != ERR_NONE) {
     return status;
   }
@@ -1217,28 +1195,28 @@
 /*
  * Return the values from an array.
  */
-static JdwpError AR_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError AR_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId array_id = request.ReadArrayId();
-  uint32_t offset = request.ReadUnsigned32("offset");
-  uint32_t length = request.ReadUnsigned32("length");
+  ObjectId array_id = request->ReadArrayId();
+  uint32_t offset = request->ReadUnsigned32("offset");
+  uint32_t length = request->ReadUnsigned32("length");
   return Dbg::OutputArray(array_id, offset, length, pReply);
 }
 
 /*
  * Set values in an array.
  */
-static JdwpError AR_SetValues(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError AR_SetValues(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId array_id = request.ReadArrayId();
-  uint32_t offset = request.ReadUnsigned32("offset");
-  uint32_t count = request.ReadUnsigned32("count");
+  ObjectId array_id = request->ReadArrayId();
+  uint32_t offset = request->ReadUnsigned32("offset");
+  uint32_t count = request->ReadUnsigned32("count");
   return Dbg::SetArrayElements(array_id, offset, count, request);
 }
 
-static JdwpError CLR_VisibleClasses(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError CLR_VisibleClasses(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  request.ReadObjectId();  // classLoaderObject
+  request->ReadObjectId();  // classLoaderObject
   // TODO: we should only return classes which have the given class loader as a defining or
   // initiating loader. The former would be easy; the latter is hard, because we don't have
   // any such notion.
@@ -1250,11 +1228,11 @@
  *
  * Reply with a requestID.
  */
-static JdwpError ER_Set(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError ER_Set(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  JdwpEventKind event_kind = request.ReadEnum1<JdwpEventKind>("event kind");
-  JdwpSuspendPolicy suspend_policy = request.ReadEnum1<JdwpSuspendPolicy>("suspend policy");
-  int32_t modifier_count = request.ReadSigned32("modifier count");
+  JdwpEventKind event_kind = request->ReadEnum1<JdwpEventKind>("event kind");
+  JdwpSuspendPolicy suspend_policy = request->ReadEnum1<JdwpSuspendPolicy>("suspend policy");
+  int32_t modifier_count = request->ReadSigned32("modifier count");
 
   CHECK_LT(modifier_count, 256);    /* reasonableness check */
 
@@ -1269,12 +1247,12 @@
    */
   for (int32_t i = 0; i < modifier_count; ++i) {
     JdwpEventMod& mod = pEvent->mods[i];
-    mod.modKind = request.ReadModKind();
+    mod.modKind = request->ReadModKind();
     switch (mod.modKind) {
     case MK_COUNT:
       {
         // Report once, when "--count" reaches 0.
-        uint32_t count = request.ReadUnsigned32("count");
+        uint32_t count = request->ReadUnsigned32("count");
         if (count == 0) {
           return ERR_INVALID_COUNT;
         }
@@ -1284,21 +1262,21 @@
     case MK_CONDITIONAL:
       {
         // Conditional on expression.
-        uint32_t exprId = request.ReadUnsigned32("expr id");
+        uint32_t exprId = request->ReadUnsigned32("expr id");
         mod.conditional.exprId = exprId;
       }
       break;
     case MK_THREAD_ONLY:
       {
         // Only report events in specified thread.
-        ObjectId thread_id = request.ReadThreadId();
+        ObjectId thread_id = request->ReadThreadId();
         mod.threadOnly.threadId = thread_id;
       }
       break;
     case MK_CLASS_ONLY:
       {
         // For ClassPrepare, MethodEntry.
-        RefTypeId class_id = request.ReadRefTypeId();
+        RefTypeId class_id = request->ReadRefTypeId();
         mod.classOnly.refTypeId = class_id;
       }
       break;
@@ -1306,7 +1284,7 @@
       {
         // Restrict events to matching classes.
         // pattern is "java.foo.*", we want "java/foo/*".
-        std::string pattern(request.ReadUtf8String());
+        std::string pattern(request->ReadUtf8String());
         std::replace(pattern.begin(), pattern.end(), '.', '/');
         mod.classMatch.classPattern = strdup(pattern.c_str());
       }
@@ -1315,7 +1293,7 @@
       {
         // Restrict events to non-matching classes.
         // pattern is "java.foo.*", we want "java/foo/*".
-        std::string pattern(request.ReadUtf8String());
+        std::string pattern(request->ReadUtf8String());
         std::replace(pattern.begin(), pattern.end(), '.', '/');
         mod.classExclude.classPattern = strdup(pattern.c_str());
       }
@@ -1323,23 +1301,23 @@
     case MK_LOCATION_ONLY:
       {
         // Restrict certain events based on location.
-        JdwpLocation location = request.ReadLocation();
+        JdwpLocation location = request->ReadLocation();
         mod.locationOnly.loc = location;
       }
       break;
     case MK_EXCEPTION_ONLY:
       {
         // Modifies EK_EXCEPTION events,
-        mod.exceptionOnly.refTypeId = request.ReadRefTypeId();  // null => all exceptions.
-        mod.exceptionOnly.caught = request.ReadEnum1<uint8_t>("caught");
-        mod.exceptionOnly.uncaught = request.ReadEnum1<uint8_t>("uncaught");
+        mod.exceptionOnly.refTypeId = request->ReadRefTypeId();  // null => all exceptions.
+        mod.exceptionOnly.caught = request->ReadEnum1<uint8_t>("caught");
+        mod.exceptionOnly.uncaught = request->ReadEnum1<uint8_t>("uncaught");
       }
       break;
     case MK_FIELD_ONLY:
       {
         // For field access/modification events.
-        RefTypeId declaring = request.ReadRefTypeId();
-        FieldId fieldId = request.ReadFieldId();
+        RefTypeId declaring = request->ReadRefTypeId();
+        FieldId fieldId = request->ReadFieldId();
         mod.fieldOnly.refTypeId = declaring;
         mod.fieldOnly.fieldId = fieldId;
       }
@@ -1347,9 +1325,9 @@
     case MK_STEP:
       {
         // For use with EK_SINGLE_STEP.
-        ObjectId thread_id = request.ReadThreadId();
-        uint32_t size = request.ReadUnsigned32("step size");
-        uint32_t depth = request.ReadUnsigned32("step depth");
+        ObjectId thread_id = request->ReadThreadId();
+        uint32_t size = request->ReadUnsigned32("step size");
+        uint32_t depth = request->ReadUnsigned32("step depth");
         VLOG(jdwp) << StringPrintf("    Step: thread=%#" PRIx64, thread_id)
                      << " size=" << JdwpStepSize(size) << " depth=" << JdwpStepDepth(depth);
 
@@ -1361,7 +1339,7 @@
     case MK_INSTANCE_ONLY:
       {
         // Report events related to a specific object.
-        ObjectId instance = request.ReadObjectId();
+        ObjectId instance = request->ReadObjectId();
         mod.instanceOnly.objectId = instance;
       }
       break;
@@ -1391,10 +1369,10 @@
   return err;
 }
 
-static JdwpError ER_Clear(JdwpState* state, Request& request, ExpandBuf*)
+static JdwpError ER_Clear(JdwpState* state, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  request.ReadEnum1<JdwpEventKind>("event kind");
-  uint32_t requestId = request.ReadUnsigned32("request id");
+  request->ReadEnum1<JdwpEventKind>("event kind");
+  uint32_t requestId = request->ReadUnsigned32("request id");
 
   // Failure to find an event with a matching ID is a no-op
   // and does not return an error.
@@ -1405,59 +1383,23 @@
 /*
  * Return the values of arguments and local variables.
  */
-static JdwpError SF_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError SF_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
-  FrameId frame_id = request.ReadFrameId();
-  int32_t slot_count = request.ReadSigned32("slot count");
-
-  expandBufAdd4BE(pReply, slot_count);     /* "int values" */
-  for (int32_t i = 0; i < slot_count; ++i) {
-    uint32_t slot = request.ReadUnsigned32("slot");
-    JDWP::JdwpTag reqSigByte = request.ReadTag();
-
-    VLOG(jdwp) << "    --> slot " << slot << " " << reqSigByte;
-
-    size_t width = Dbg::GetTagWidth(reqSigByte);
-    uint8_t* ptr = expandBufAddSpace(pReply, width+1);
-    JdwpError error = Dbg::GetLocalValue(thread_id, frame_id, slot, reqSigByte, ptr, width);
-    if (error != ERR_NONE) {
-      return error;
-    }
-  }
-
-  return ERR_NONE;
+  return Dbg::GetLocalValues(request, pReply);
 }
 
 /*
  * Set the values of arguments and local variables.
  */
-static JdwpError SF_SetValues(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError SF_SetValues(JdwpState*, Request* request, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
-  FrameId frame_id = request.ReadFrameId();
-  int32_t slot_count = request.ReadSigned32("slot count");
-
-  for (int32_t i = 0; i < slot_count; ++i) {
-    uint32_t slot = request.ReadUnsigned32("slot");
-    JDWP::JdwpTag sigByte = request.ReadTag();
-    size_t width = Dbg::GetTagWidth(sigByte);
-    uint64_t value = request.ReadValue(width);
-
-    VLOG(jdwp) << "    --> slot " << slot << " " << sigByte << " " << value;
-    JdwpError error = Dbg::SetLocalValue(thread_id, frame_id, slot, sigByte, value, width);
-    if (error != ERR_NONE) {
-      return error;
-    }
-  }
-
-  return ERR_NONE;
+  return Dbg::SetLocalValues(request);
 }
 
-static JdwpError SF_ThisObject(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError SF_ThisObject(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ObjectId thread_id = request.ReadThreadId();
-  FrameId frame_id = request.ReadFrameId();
+  ObjectId thread_id = request->ReadThreadId();
+  FrameId frame_id = request->ReadFrameId();
 
   ObjectId object_id;
   JdwpError rc = Dbg::GetThisObject(thread_id, frame_id, &object_id);
@@ -1475,16 +1417,16 @@
  * reused, whereas ClassIds can be recycled like any other object.  (Either
  * that, or I have no idea what this is for.)
  */
-static JdwpError COR_ReflectedType(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError COR_ReflectedType(JdwpState*, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RefTypeId class_object_id = request.ReadRefTypeId();
+  RefTypeId class_object_id = request->ReadRefTypeId();
   return Dbg::GetReflectedType(class_object_id, pReply);
 }
 
 /*
  * Handle a DDM packet with a single chunk in it.
  */
-static JdwpError DDM_Chunk(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError DDM_Chunk(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   state->NotifyDdmsActive();
   uint8_t* replyBuf = NULL;
@@ -1505,7 +1447,7 @@
 /*
  * Handler map decl.
  */
-typedef JdwpError (*JdwpRequestHandler)(JdwpState* state, Request& request, ExpandBuf* reply);
+typedef JdwpError (*JdwpRequestHandler)(JdwpState* state, Request* request, ExpandBuf* reply);
 
 struct JdwpHandlerMap {
   uint8_t cmdSet;
@@ -1648,20 +1590,20 @@
   { 199,  1,  DDM_Chunk,        "DDM.Chunk" },
 };
 
-static const char* GetCommandName(Request& request) {
+static const char* GetCommandName(Request* request) {
   for (size_t i = 0; i < arraysize(gHandlers); ++i) {
-    if (gHandlers[i].cmdSet == request.GetCommandSet() && gHandlers[i].cmd == request.GetCommand()) {
+    if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand()) {
       return gHandlers[i].name;
     }
   }
   return "?UNKNOWN?";
 }
 
-static std::string DescribeCommand(Request& request) {
+static std::string DescribeCommand(Request* request) {
   std::string result;
   result += "REQUEST: ";
   result += GetCommandName(request);
-  result += StringPrintf(" (length=%zu id=0x%06x)", request.GetLength(), request.GetId());
+  result += StringPrintf(" (length=%zu id=0x%06x)", request->GetLength(), request->GetId());
   return result;
 }
 
@@ -1670,10 +1612,10 @@
  *
  * On entry, the JDWP thread is in VMWAIT.
  */
-size_t JdwpState::ProcessRequest(Request& request, ExpandBuf* pReply) {
+size_t JdwpState::ProcessRequest(Request* request, ExpandBuf* pReply) {
   JdwpError result = ERR_NONE;
 
-  if (request.GetCommandSet() != kJDWPDdmCmdSet) {
+  if (request->GetCommandSet() != kJDWPDdmCmdSet) {
     /*
      * Activity from a debugger, not merely ddms.  Mark us as having an
      * active debugger session, and zero out the last-activity timestamp
@@ -1693,7 +1635,7 @@
    * thread to finish, and then clear the block.  Depending on the thread
    * suspend policy, this may allow events in other threads to fire,
    * but those events have no bearing on what the debugger has sent us
-   * in the current request.
+   * in the current request->
    *
    * Note that we MUST clear the event token before waking the event
    * thread up, or risk waiting for the thread to suspend after we've
@@ -1702,7 +1644,7 @@
   SetWaitForEventThread(0);
 
   /*
-   * We do not want events to be sent while we process a request. Indicate the JDWP thread starts
+   * We do not want events to be sent while we process a request-> Indicate the JDWP thread starts
    * to process a request so other threads wait for it to finish before sending an event.
    */
   StartProcessingRequest();
@@ -1718,18 +1660,18 @@
 
   size_t i;
   for (i = 0; i < arraysize(gHandlers); ++i) {
-    if (gHandlers[i].cmdSet == request.GetCommandSet() && gHandlers[i].cmd == request.GetCommand() && gHandlers[i].func != NULL) {
+    if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand() && gHandlers[i].func != NULL) {
       VLOG(jdwp) << DescribeCommand(request);
       result = (*gHandlers[i].func)(this, request, pReply);
       if (result == ERR_NONE) {
-        request.CheckConsumed();
+        request->CheckConsumed();
       }
       break;
     }
   }
   if (i == arraysize(gHandlers)) {
     LOG(ERROR) << "Command not implemented: " << DescribeCommand(request);
-    LOG(ERROR) << HexDump(request.data(), request.size(), false, "");
+    LOG(ERROR) << HexDump(request->data(), request->size(), false, "");
     result = ERR_NOT_IMPLEMENTED;
   }
 
@@ -1741,11 +1683,11 @@
   uint8_t* replyBuf = expandBufGetBuffer(pReply);
   size_t replyLength = (result == ERR_NONE) ? expandBufGetLength(pReply) : kJDWPHeaderLen;
   Set4BE(replyBuf + 0, replyLength);
-  Set4BE(replyBuf + 4, request.GetId());
+  Set4BE(replyBuf + 4, request->GetId());
   Set1(replyBuf + 8, kJDWPFlagReply);
   Set2BE(replyBuf + 9, result);
 
-  CHECK_GT(expandBufGetLength(pReply), 0U) << GetCommandName(request) << " " << request.GetId();
+  CHECK_GT(expandBufGetLength(pReply), 0U) << GetCommandName(request) << " " << request->GetId();
 
   size_t respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
   VLOG(jdwp) << "REPLY: " << GetCommandName(request) << " " << result << " (length=" << respLen << ")";
@@ -1759,7 +1701,7 @@
    * Update last-activity timestamp.  We really only need this during
    * the initial setup.  Only update if this is a non-DDMS packet.
    */
-  if (request.GetCommandSet() != kJDWPDdmCmdSet) {
+  if (request->GetCommandSet() != kJDWPDdmCmdSet) {
     last_activity_time_ms_.StoreSequentiallyConsistent(MilliTime());
   }
 
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index a4f427c..c500ef5 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -373,7 +373,7 @@
   JDWP::Request request(netStateBase->input_buffer_, netStateBase->input_count_);
 
   ExpandBuf* pReply = expandBufAlloc();
-  size_t replyLength = ProcessRequest(request, pReply);
+  size_t replyLength = ProcessRequest(&request, pReply);
   ssize_t cc = netStateBase->WritePacket(pReply, replyLength);
 
   /*
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index ad18d8a..35aaf0a 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -21,8 +21,6 @@
 
 namespace art {
 
-mirror::Object* const ObjectRegistry::kInvalidObject = reinterpret_cast<mirror::Object*>(1);
-
 std::ostream& operator<<(std::ostream& os, const ObjectRegistryEntry& rhs) {
   os << "ObjectRegistryEntry[" << rhs.jni_reference_type
      << ",reference=" << rhs.jni_reference
@@ -129,14 +127,16 @@
   id_to_entry_.clear();
 }
 
-mirror::Object* ObjectRegistry::InternalGet(JDWP::ObjectId id) {
+mirror::Object* ObjectRegistry::InternalGet(JDWP::ObjectId id, JDWP::JdwpError* error) {
   Thread* self = Thread::Current();
   MutexLock mu(self, lock_);
   auto it = id_to_entry_.find(id);
   if (it == id_to_entry_.end()) {
-    return kInvalidObject;
+    *error = JDWP::ERR_INVALID_OBJECT;
+    return nullptr;
   }
   ObjectRegistryEntry& entry = *it->second;
+  *error = JDWP::ERR_NONE;
   return self->DecodeJObject(entry.jni_reference);
 }
 
diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h
index bc3530b..faddff1 100644
--- a/runtime/jdwp/object_registry.h
+++ b/runtime/jdwp/object_registry.h
@@ -22,6 +22,7 @@
 
 #include <map>
 
+#include "base/casts.h"
 #include "jdwp/jdwp.h"
 #include "safe_map.h"
 
@@ -65,11 +66,13 @@
   JDWP::RefTypeId AddRefType(mirror::Class* c)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_);
 
-  template<typename T> T Get(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  template<typename T> T Get(JDWP::ObjectId id, JDWP::JdwpError* error)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (id == 0) {
-      return NULL;
+      *error = JDWP::ERR_NONE;
+      return nullptr;
     }
-    return reinterpret_cast<T>(InternalGet(id));
+    return down_cast<T>(InternalGet(id, error));
   }
 
   bool Contains(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -90,9 +93,6 @@
   void DisposeObject(JDWP::ObjectId id, uint32_t reference_count)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Returned by Get when passed an invalid object id.
-  static mirror::Object* const kInvalidObject;
-
   // This is needed to get the jobject instead of the Object*.
   // Avoid using this and use standard Get when possible.
   jobject GetJObject(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -102,7 +102,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(lock_, Locks::thread_list_lock_);
 
-  mirror::Object* InternalGet(JDWP::ObjectId id)
+  mirror::Object* InternalGet(JDWP::ObjectId id, JDWP::JdwpError* error)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(lock_);
 
diff --git a/runtime/jni_internal-inl.h b/runtime/jni_env_ext-inl.h
similarity index 89%
rename from runtime/jni_internal-inl.h
rename to runtime/jni_env_ext-inl.h
index 6cf9a61..dc6a3e8 100644
--- a/runtime/jni_internal-inl.h
+++ b/runtime/jni_env_ext-inl.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_JNI_INTERNAL_INL_H_
-#define ART_RUNTIME_JNI_INTERNAL_INL_H_
+#ifndef ART_RUNTIME_JNI_ENV_EXT_INL_H_
+#define ART_RUNTIME_JNI_ENV_EXT_INL_H_
 
-#include "jni_internal.h"
+#include "jni_env_ext.h"
 
 #include "utils.h"
 
@@ -44,4 +44,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_JNI_INTERNAL_INL_H_
+#endif  // ART_RUNTIME_JNI_ENV_EXT_INL_H_
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
new file mode 100644
index 0000000..180e3d7
--- /dev/null
+++ b/runtime/jni_env_ext.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 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 "jni_env_ext.h"
+
+#include "check_jni.h"
+#include "indirect_reference_table.h"
+#include "java_vm_ext.h"
+#include "jni_internal.h"
+
+namespace art {
+
+static constexpr size_t kMonitorsInitial = 32;  // Arbitrary.
+static constexpr size_t kMonitorsMax = 4096;  // Arbitrary sanity check.
+
+static constexpr size_t kLocalsInitial = 64;  // Arbitrary.
+
+JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
+    : self(self),
+      vm(vm),
+      local_ref_cookie(IRT_FIRST_SEGMENT),
+      locals(kLocalsInitial, kLocalsMax, kLocal),
+      check_jni(false),
+      critical(0),
+      monitors("monitors", kMonitorsInitial, kMonitorsMax) {
+  functions = unchecked_functions = GetJniNativeInterface();
+  if (vm->IsCheckJniEnabled()) {
+    SetCheckJniEnabled(true);
+  }
+}
+
+JNIEnvExt::~JNIEnvExt() {
+}
+
+jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
+}
+
+void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (obj != nullptr) {
+    locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
+  }
+}
+
+void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
+  check_jni = enabled;
+  functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+}
+
+void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
+  locals.Dump(os);
+  monitors.Dump(os);
+}
+
+void JNIEnvExt::PushFrame(int capacity) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  UNUSED(capacity);  // cpplint gets confused with (int) and thinks its a cast.
+  // TODO: take 'capacity' into account.
+  stacked_local_ref_cookies.push_back(local_ref_cookie);
+  local_ref_cookie = locals.GetSegmentState();
+}
+
+void JNIEnvExt::PopFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  locals.SetSegmentState(local_ref_cookie);
+  local_ref_cookie = stacked_local_ref_cookies.back();
+  stacked_local_ref_cookies.pop_back();
+}
+
+Offset JNIEnvExt::SegmentStateOffset() {
+  return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
+                IndirectReferenceTable::SegmentStateOffset().Int32Value());
+}
+
+}  // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
new file mode 100644
index 0000000..af87cb4
--- /dev/null
+++ b/runtime/jni_env_ext.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JNI_ENV_EXT_H_
+#define ART_RUNTIME_JNI_ENV_EXT_H_
+
+#include <jni.h>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "indirect_reference_table.h"
+#include "object_callbacks.h"
+#include "reference_table.h"
+
+namespace art {
+
+class JavaVMExt;
+
+// Maximum number of local references in the indirect reference table. The value is arbitrary but
+// low enough that it forces sanity checks.
+static constexpr size_t kLocalsMax = 512;
+
+struct JNIEnvExt : public JNIEnv {
+  JNIEnvExt(Thread* self, JavaVMExt* vm);
+  ~JNIEnvExt();
+
+  void DumpReferenceTables(std::ostream& os)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void SetCheckJniEnabled(bool enabled);
+
+  void PushFrame(int capacity);
+  void PopFrame();
+
+  template<typename T>
+  T AddLocalReference(mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static Offset SegmentStateOffset();
+
+  static Offset LocalRefCookieOffset() {
+    return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
+  }
+
+  static Offset SelfOffset() {
+    return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
+  }
+
+  jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  Thread* const self;
+  JavaVMExt* const vm;
+
+  // Cookie used when using the local indirect reference table.
+  uint32_t local_ref_cookie;
+
+  // JNI local references.
+  IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
+
+  // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
+  // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
+  // to a native method.
+  std::vector<uint32_t> stacked_local_ref_cookies;
+
+  // Frequently-accessed fields cached from JavaVM.
+  bool check_jni;
+
+  // How many nested "critical" JNI calls are we in?
+  int critical;
+
+  // Entered JNI monitors, for bulk exit on thread detach.
+  ReferenceTable monitors;
+
+  // Used by -Xcheck:jni.
+  const JNINativeInterface* unchecked_functions;
+};
+
+// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
+// compiler.
+class ScopedJniEnvLocalRefState {
+ public:
+  explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
+    saved_local_ref_cookie_ = env->local_ref_cookie;
+    env->local_ref_cookie = env->locals.GetSegmentState();
+  }
+
+  ~ScopedJniEnvLocalRefState() {
+    env_->locals.SetSegmentState(env_->local_ref_cookie);
+    env_->local_ref_cookie = saved_local_ref_cookie_;
+  }
+
+ private:
+  JNIEnvExt* const env_;
+  uint32_t saved_local_ref_cookie_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_JNI_ENV_EXT_H_
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 73effca..e2f9ac2 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -34,7 +34,8 @@
 #include "gc/accounting/card_table-inl.h"
 #include "indirect_reference_table-inl.h"
 #include "interpreter/interpreter.h"
-#include "jni.h"
+#include "jni_env_ext.h"
+#include "java_vm_ext.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
@@ -56,28 +57,6 @@
 
 namespace art {
 
-static const size_t kMonitorsInitial = 32;  // Arbitrary.
-static const size_t kMonitorsMax = 4096;  // Arbitrary sanity check.
-
-static const size_t kLocalsInitial = 64;  // Arbitrary.
-static const size_t kLocalsMax = 512;  // Arbitrary sanity check.
-
-static size_t gGlobalsInitial = 512;  // Arbitrary.
-static size_t gGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
-
-static const size_t kWeakGlobalsInitial = 16;  // Arbitrary.
-static const size_t kWeakGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
-
-static jweak AddWeakGlobalReference(ScopedObjectAccess& soa, mirror::Object* obj)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return soa.Vm()->AddWeakGlobalReference(soa.Self(), obj);
-}
-
-static bool IsBadJniVersion(int version) {
-  // We don't support JNI_VERSION_1_1. These are the only other valid versions.
-  return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
-}
-
 // Section 12.3.2 of the JNI spec describes JNI class descriptors. They're
 // separated with slashes but aren't wrapped with "L;" like regular descriptors
 // (i.e. "a/b/C" rather than "La/b/C;"). Arrays of reference types are an
@@ -130,7 +109,7 @@
   }
   StackHandleScope<1> hs(self);
   Handle<mirror::Class> h_klass(hs.NewHandle(klass));
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) {
     return nullptr;
   }
   return h_klass.Get();
@@ -168,7 +147,7 @@
   mirror::ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
   // If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
   if (method == soa.DecodeMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
-    return soa.Self()->GetClassLoaderOverride();
+    return soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
   }
   // If we have a method, use its ClassLoader for context.
   if (method != nullptr) {
@@ -181,7 +160,7 @@
     return class_loader;
   }
   // See if the override ClassLoader is set for gtests.
-  class_loader = soa.Self()->GetClassLoaderOverride();
+  class_loader = soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
   if (class_loader != nullptr) {
     // If so, CommonCompilerTest should have set UseCompileTimeClassPath.
     CHECK(Runtime::Current()->UseCompileTimeClassPath());
@@ -303,255 +282,10 @@
   return JNI_OK;
 }
 
-static jint JII_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
-  if (vm == nullptr || p_env == nullptr) {
-    return JNI_ERR;
-  }
-
-  // Return immediately if we're already attached.
-  Thread* self = Thread::Current();
-  if (self != nullptr) {
-    *p_env = self->GetJniEnv();
-    return JNI_OK;
-  }
-
-  Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
-
-  // No threads allowed in zygote mode.
-  if (runtime->IsZygote()) {
-    LOG(ERROR) << "Attempt to attach a thread in the zygote";
-    return JNI_ERR;
-  }
-
-  JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
-  const char* thread_name = nullptr;
-  jobject thread_group = nullptr;
-  if (args != nullptr) {
-    if (IsBadJniVersion(args->version)) {
-      LOG(ERROR) << "Bad JNI version passed to "
-                 << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
-                 << args->version;
-      return JNI_EVERSION;
-    }
-    thread_name = args->name;
-    thread_group = args->group;
-  }
-
-  if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group, !runtime->IsCompiler())) {
-    *p_env = nullptr;
-    return JNI_ERR;
-  } else {
-    *p_env = Thread::Current()->GetJniEnv();
-    return JNI_OK;
-  }
+static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) {
+  return reinterpret_cast<JNIEnvExt*>(env)->vm;
 }
 
-class SharedLibrary {
- public:
-  SharedLibrary(const std::string& path, void* handle, mirror::Object* class_loader)
-      : path_(path),
-        handle_(handle),
-        needs_native_bridge_(false),
-        class_loader_(GcRoot<mirror::Object>(class_loader)),
-        jni_on_load_lock_("JNI_OnLoad lock"),
-        jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
-        jni_on_load_thread_id_(Thread::Current()->GetThreadId()),
-        jni_on_load_result_(kPending) {
-  }
-
-  mirror::Object* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return class_loader_.Read();
-  }
-
-  std::string GetPath() {
-    return path_;
-  }
-
-  /*
-   * Check the result of an earlier call to JNI_OnLoad on this library.
-   * If the call has not yet finished in another thread, wait for it.
-   */
-  bool CheckOnLoadResult()
-      LOCKS_EXCLUDED(jni_on_load_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    Thread* self = Thread::Current();
-    self->TransitionFromRunnableToSuspended(kWaitingForJniOnLoad);
-    bool okay;
-    {
-      MutexLock mu(self, jni_on_load_lock_);
-
-      if (jni_on_load_thread_id_ == self->GetThreadId()) {
-        // Check this so we don't end up waiting for ourselves.  We need to return "true" so the
-        // caller can continue.
-        LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
-        okay = true;
-      } else {
-        while (jni_on_load_result_ == kPending) {
-          VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]";
-          jni_on_load_cond_.Wait(self);
-        }
-
-        okay = (jni_on_load_result_ == kOkay);
-        VLOG(jni) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
-            << (okay ? "succeeded" : "failed") << "]";
-      }
-    }
-    self->TransitionFromSuspendedToRunnable();
-    return okay;
-  }
-
-  void SetResult(bool result) LOCKS_EXCLUDED(jni_on_load_lock_) {
-    Thread* self = Thread::Current();
-    MutexLock mu(self, jni_on_load_lock_);
-
-    jni_on_load_result_ = result ? kOkay : kFailed;
-    jni_on_load_thread_id_ = 0;
-
-    // Broadcast a wakeup to anybody sleeping on the condition variable.
-    jni_on_load_cond_.Broadcast(self);
-  }
-
-  void SetNeedsNativeBridge() {
-    needs_native_bridge_ = true;
-  }
-
-  bool NeedsNativeBridge() const {
-    return needs_native_bridge_;
-  }
-
-  void* FindSymbol(const std::string& symbol_name) {
-    return dlsym(handle_, symbol_name.c_str());
-  }
-
-  void* FindSymbolWithNativeBridge(const std::string& symbol_name, mirror::ArtMethod* m)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    CHECK(NeedsNativeBridge());
-
-    uint32_t len = 0;
-    const char* shorty = nullptr;
-    if (m != nullptr) {
-      shorty = m->GetShorty(&len);
-    }
-    return android::NativeBridgeGetTrampoline(handle_, symbol_name.c_str(), shorty, len);
-  }
-
-  void VisitRoots(RootCallback* visitor, void* arg) {
-    if (!class_loader_.IsNull()) {
-      class_loader_.VisitRoot(visitor, arg, 0, kRootVMInternal);
-    }
-  }
-
- private:
-  enum JNI_OnLoadState {
-    kPending,
-    kFailed,
-    kOkay,
-  };
-
-  // Path to library "/system/lib/libjni.so".
-  std::string path_;
-
-  // The void* returned by dlopen(3).
-  void* handle_;
-
-  // True if a native bridge is required.
-  bool needs_native_bridge_;
-
-  // The ClassLoader this library is associated with.
-  GcRoot<mirror::Object> class_loader_;
-
-  // Guards remaining items.
-  Mutex jni_on_load_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  // Wait for JNI_OnLoad in other thread.
-  ConditionVariable jni_on_load_cond_ GUARDED_BY(jni_on_load_lock_);
-  // Recursive invocation guard.
-  uint32_t jni_on_load_thread_id_ GUARDED_BY(jni_on_load_lock_);
-  // Result of earlier JNI_OnLoad call.
-  JNI_OnLoadState jni_on_load_result_ GUARDED_BY(jni_on_load_lock_);
-};
-
-// This exists mainly to keep implementation details out of the header file.
-class Libraries {
- public:
-  Libraries() {
-  }
-
-  ~Libraries() {
-    STLDeleteValues(&libraries_);
-  }
-
-  void Dump(std::ostream& os) const {
-    bool first = true;
-    for (const auto& library : libraries_) {
-      if (!first) {
-        os << ' ';
-      }
-      first = false;
-      os << library.first;
-    }
-  }
-
-  size_t size() const {
-    return libraries_.size();
-  }
-
-  SharedLibrary* Get(const std::string& path) {
-    auto it = libraries_.find(path);
-    return (it == libraries_.end()) ? nullptr : it->second;
-  }
-
-  void Put(const std::string& path, SharedLibrary* library) {
-    libraries_.Put(path, library);
-  }
-
-  // See section 11.3 "Linking Native Methods" of the JNI spec.
-  void* FindNativeMethod(mirror::ArtMethod* m, std::string& detail)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    std::string jni_short_name(JniShortName(m));
-    std::string jni_long_name(JniLongName(m));
-    const mirror::ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
-    for (const auto& lib : libraries_) {
-      SharedLibrary* library = lib.second;
-      if (library->GetClassLoader() != declaring_class_loader) {
-        // We only search libraries loaded by the appropriate ClassLoader.
-        continue;
-      }
-      // Try the short name then the long name...
-      void* fn = nullptr;
-      if (UNLIKELY(library->NeedsNativeBridge())) {
-        fn = library->FindSymbolWithNativeBridge(jni_short_name, m);
-        if (fn == nullptr) {
-          fn = library->FindSymbolWithNativeBridge(jni_long_name, m);
-        }
-      } else {
-        fn = library->FindSymbol(jni_short_name);
-        if (fn == nullptr) {
-          fn = library->FindSymbol(jni_long_name);
-        }
-      }
-      if (fn != nullptr) {
-        VLOG(jni) << "[Found native code for " << PrettyMethod(m)
-                  << " in \"" << library->GetPath() << "\"]";
-        return fn;
-      }
-    }
-    detail += "No implementation found for ";
-    detail += PrettyMethod(m);
-    detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
-    LOG(ERROR) << detail;
-    return nullptr;
-  }
-
-  void VisitRoots(RootCallback* callback, void* arg) {
-    for (auto& lib_pair : libraries_) {
-      lib_pair.second->VisitRoots(callback, arg);
-    }
-  }
-
- private:
-  AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibrarires> libraries_;
-};
-
 #define CHECK_NON_NULL_ARGUMENT(value) \
     CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, nullptr)
 
@@ -566,13 +300,13 @@
 
 #define CHECK_NON_NULL_ARGUMENT_FN_NAME(name, value, return_val) \
   if (UNLIKELY(value == nullptr)) { \
-    JniAbortF(name, #value " == null"); \
+    JavaVmExtFromEnv(env)->JniAbortF(name, #value " == null"); \
     return return_val; \
   }
 
 #define CHECK_NON_NULL_MEMCPY_ARGUMENT(length, value) \
   if (UNLIKELY(length != 0 && value == nullptr)) { \
-    JniAbortF(__FUNCTION__, #value " == null"); \
+    JavaVmExtFromEnv(env)->JniAbortF(__FUNCTION__, #value " == null"); \
     return; \
   }
 
@@ -773,10 +507,10 @@
   static jint PushLocalFrame(JNIEnv* env, jint capacity) {
     // TODO: SOA may not be necessary but I do it to please lock annotations.
     ScopedObjectAccess soa(env);
-    if (EnsureLocalCapacity(soa, capacity, "PushLocalFrame") != JNI_OK) {
+    if (EnsureLocalCapacityInternal(soa, capacity, "PushLocalFrame") != JNI_OK) {
       return JNI_ERR;
     }
-    static_cast<JNIEnvExt*>(env)->PushFrame(capacity);
+    down_cast<JNIEnvExt*>(env)->PushFrame(capacity);
     return JNI_OK;
   }
 
@@ -790,48 +524,31 @@
   static jint EnsureLocalCapacity(JNIEnv* env, jint desired_capacity) {
     // TODO: SOA may not be necessary but I do it to please lock annotations.
     ScopedObjectAccess soa(env);
-    return EnsureLocalCapacity(soa, desired_capacity, "EnsureLocalCapacity");
+    return EnsureLocalCapacityInternal(soa, desired_capacity, "EnsureLocalCapacity");
   }
 
   static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
     ScopedObjectAccess soa(env);
     mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
-    // Check for null after decoding the object to handle cleared weak globals.
-    if (decoded_obj == nullptr) {
-      return nullptr;
-    }
-    JavaVMExt* vm = soa.Vm();
-    IndirectReferenceTable& globals = vm->globals;
-    WriterMutexLock mu(soa.Self(), vm->globals_lock);
-    IndirectRef ref = globals.Add(IRT_FIRST_SEGMENT, decoded_obj);
-    return reinterpret_cast<jobject>(ref);
+    return soa.Vm()->AddGlobalRef(soa.Self(), decoded_obj);
   }
 
   static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
-    if (obj == nullptr) {
-      return;
-    }
-    JavaVMExt* vm = reinterpret_cast<JNIEnvExt*>(env)->vm;
-    IndirectReferenceTable& globals = vm->globals;
-    Thread* self = reinterpret_cast<JNIEnvExt*>(env)->self;
-    WriterMutexLock mu(self, vm->globals_lock);
-
-    if (!globals.Remove(IRT_FIRST_SEGMENT, obj)) {
-      LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
-                   << "failed to find entry";
-    }
+    JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
+    Thread* self = down_cast<JNIEnvExt*>(env)->self;
+    vm->DeleteGlobalRef(self, obj);
   }
 
   static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
     ScopedObjectAccess soa(env);
-    return AddWeakGlobalReference(soa, soa.Decode<mirror::Object*>(obj));
+    mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
+    return soa.Vm()->AddWeakGlobalRef(soa.Self(), decoded_obj);
   }
 
   static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
-    if (obj != nullptr) {
-      ScopedObjectAccess soa(env);
-      soa.Vm()->DeleteWeakGlobalRef(soa.Self(), obj);
-    }
+    JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
+    Thread* self = down_cast<JNIEnvExt*>(env)->self;
+    vm->DeleteWeakGlobalRef(self, obj);
   }
 
   static jobject NewLocalRef(JNIEnv* env, jobject obj) {
@@ -848,7 +565,6 @@
     if (obj == nullptr) {
       return;
     }
-    ScopedObjectAccess soa(env);
     IndirectReferenceTable& locals = reinterpret_cast<JNIEnvExt*>(env)->locals;
 
     uint32_t cookie = reinterpret_cast<JNIEnvExt*>(env)->local_ref_cookie;
@@ -1914,11 +1630,11 @@
 
   static jstring NewString(JNIEnv* env, const jchar* chars, jsize char_count) {
     if (UNLIKELY(char_count < 0)) {
-      JniAbortF("NewString", "char_count < 0: %d", char_count);
+      JavaVmExtFromEnv(env)->JniAbortF("NewString", "char_count < 0: %d", char_count);
       return nullptr;
     }
     if (UNLIKELY(chars == nullptr && char_count > 0)) {
-      JniAbortF("NewString", "chars == null && char_count > 0");
+      JavaVmExtFromEnv(env)->JniAbortF("NewString", "chars == null && char_count > 0");
       return nullptr;
     }
     ScopedObjectAccess soa(env);
@@ -2066,7 +1782,8 @@
     ScopedObjectAccess soa(env);
     mirror::Object* obj = soa.Decode<mirror::Object*>(java_array);
     if (UNLIKELY(!obj->IsArrayInstance())) {
-      JniAbortF("GetArrayLength", "not an array: %s", PrettyTypeOf(obj).c_str());
+      soa.Vm()->JniAbortF("GetArrayLength", "not an array: %s", PrettyTypeOf(obj).c_str());
+      return 0;
     }
     mirror::Array* array = obj->AsArray();
     return array->GetLength();
@@ -2121,7 +1838,7 @@
   static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass,
                                      jobject initial_element) {
     if (UNLIKELY(length < 0)) {
-      JniAbortF("NewObjectArray", "negative array length: %d", length);
+      JavaVmExtFromEnv(env)->JniAbortF("NewObjectArray", "negative array length: %d", length);
       return nullptr;
     }
     CHECK_NON_NULL_ARGUMENT(element_jclass);
@@ -2132,8 +1849,8 @@
     {
       mirror::Class* element_class = soa.Decode<mirror::Class*>(element_jclass);
       if (UNLIKELY(element_class->IsPrimitive())) {
-        JniAbortF("NewObjectArray", "not an object type: %s",
-                  PrettyDescriptor(element_class).c_str());
+        soa.Vm()->JniAbortF("NewObjectArray", "not an object type: %s",
+                            PrettyDescriptor(element_class).c_str());
         return nullptr;
       }
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -2151,10 +1868,11 @@
       if (initial_object != nullptr) {
         mirror::Class* element_class = result->GetClass()->GetComponentType();
         if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) {
-          JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with element "
-                    "type of '%s'", PrettyDescriptor(initial_object->GetClass()).c_str(),
-                    PrettyDescriptor(element_class).c_str());
-
+          soa.Vm()->JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with "
+                              "element type of '%s'",
+                              PrettyDescriptor(initial_object->GetClass()).c_str(),
+                              PrettyDescriptor(element_class).c_str());
+          return nullptr;
         } else {
           for (jsize i = 0; i < length; ++i) {
             result->SetWithoutChecks<false>(i, initial_object);
@@ -2174,8 +1892,8 @@
     ScopedObjectAccess soa(env);
     mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
     if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
-      JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s",
-                PrettyDescriptor(array->GetClass()).c_str());
+      soa.Vm()->JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s",
+                          PrettyDescriptor(array->GetClass()).c_str());
       return nullptr;
     }
     gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -2196,8 +1914,8 @@
     ScopedObjectAccess soa(env);
     mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
     if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
-      JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s",
-                PrettyDescriptor(array->GetClass()).c_str());
+      soa.Vm()->JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s",
+                          PrettyDescriptor(array->GetClass()).c_str());
       return;
     }
     const size_t component_size = array->GetClass()->GetComponentSize();
@@ -2369,8 +2087,9 @@
   static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
                                     jint method_count, bool return_errors) {
     if (UNLIKELY(method_count < 0)) {
-      JniAbortF("RegisterNatives", "negative method count: %d", method_count);
-      return JNI_ERR;  // Not reached.
+      JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
+                                       method_count);
+      return JNI_ERR;  // Not reached except in unit tests.
     }
     CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
     ScopedObjectAccess soa(env);
@@ -2494,17 +2213,21 @@
 
   static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
     if (capacity < 0) {
-      JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64, capacity);
+      JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64,
+                                       capacity);
       return nullptr;
     }
     if (address == nullptr && capacity != 0) {
-      JniAbortF("NewDirectByteBuffer", "non-zero capacity for nullptr pointer: %" PRId64, capacity);
+      JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer",
+                                       "non-zero capacity for nullptr pointer: %" PRId64, capacity);
       return nullptr;
     }
 
     // At the moment, the capacity of DirectByteBuffer is limited to a signed int.
     if (capacity > INT_MAX) {
-      JniAbortF("NewDirectByteBuffer", "buffer capacity greater than maximum jint: %" PRId64, capacity);
+      JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer",
+                                       "buffer capacity greater than maximum jint: %" PRId64,
+                                       capacity);
       return nullptr;
     }
     jlong address_arg = reinterpret_cast<jlong>(address);
@@ -2533,33 +2256,24 @@
     IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
     IndirectRefKind kind = GetIndirectRefKind(ref);
     switch (kind) {
-    case kLocal: {
-      ScopedObjectAccess soa(env);
-      // The local refs don't need a read barrier.
-      if (static_cast<JNIEnvExt*>(env)->locals.Get<kWithoutReadBarrier>(ref) !=
-          kInvalidIndirectRefObject) {
-        return JNILocalRefType;
-      }
-      return JNIInvalidRefType;
-    }
+    case kLocal:
+      return JNILocalRefType;
     case kGlobal:
       return JNIGlobalRefType;
     case kWeakGlobal:
       return JNIWeakGlobalRefType;
     case kHandleScopeOrInvalid:
-      // Is it in a stack IRT?
-      if (static_cast<JNIEnvExt*>(env)->self->HandleScopeContains(java_object)) {
-        return JNILocalRefType;
-      }
-      return JNIInvalidRefType;
+      // Assume value is in a handle scope.
+      return JNILocalRefType;
     }
     LOG(FATAL) << "IndirectRefKind[" << kind << "]";
     return JNIInvalidRefType;
   }
 
  private:
-  static jint EnsureLocalCapacity(ScopedObjectAccess& soa, jint desired_capacity,
-                                  const char* caller) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  static jint EnsureLocalCapacityInternal(ScopedObjectAccess& soa, jint desired_capacity,
+                                          const char* caller)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // TODO: we should try to expand the table if necessary.
     if (desired_capacity < 0 || desired_capacity > static_cast<jint>(kLocalsMax)) {
       LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity;
@@ -2576,11 +2290,11 @@
 
   template<typename JniT, typename ArtT>
   static JniT NewPrimitiveArray(JNIEnv* env, jsize length) {
+    ScopedObjectAccess soa(env);
     if (UNLIKELY(length < 0)) {
-      JniAbortF("NewPrimitiveArray", "negative array length: %d", length);
+      soa.Vm()->JniAbortF("NewPrimitiveArray", "negative array length: %d", length);
       return nullptr;
     }
-    ScopedObjectAccess soa(env);
     ArtT* result = ArtT::Alloc(soa.Self(), length);
     return soa.AddLocalReference<JniT>(result);
   }
@@ -2591,9 +2305,11 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ArtArrayT* array = soa.Decode<ArtArrayT*>(java_array);
     if (UNLIKELY(ArtArrayT::GetArrayClass() != array->GetClass())) {
-      JniAbortF(fn_name, "attempt to %s %s primitive array elements with an object of type %s",
-                operation, PrettyDescriptor(ArtArrayT::GetArrayClass()->GetComponentType()).c_str(),
-                PrettyDescriptor(array->GetClass()).c_str());
+      soa.Vm()->JniAbortF(fn_name,
+                          "attempt to %s %s primitive array elements with an object of type %s",
+                          operation,
+                          PrettyDescriptor(ArtArrayT::GetArrayClass()->GetComponentType()).c_str(),
+                          PrettyDescriptor(array->GetClass()).c_str());
       return nullptr;
     }
     DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize());
@@ -2655,8 +2371,9 @@
       // heap address. TODO: This might be slow to check, may be worth keeping track of which
       // copies we make?
       if (heap->IsNonDiscontinuousSpaceHeapAddress(reinterpret_cast<mirror::Object*>(elements))) {
-        JniAbortF("ReleaseArrayElements", "invalid element pointer %p, array elements are %p",
-                  reinterpret_cast<void*>(elements), array_data);
+        soa.Vm()->JniAbortF("ReleaseArrayElements",
+                            "invalid element pointer %p, array elements are %p",
+                            reinterpret_cast<void*>(elements), array_data);
         return;
       }
     }
@@ -2951,473 +2668,8 @@
   JNI::GetObjectRefType,
 };
 
-JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
-    : self(self),
-      vm(vm),
-      local_ref_cookie(IRT_FIRST_SEGMENT),
-      locals(kLocalsInitial, kLocalsMax, kLocal),
-      check_jni(false),
-      critical(0),
-      monitors("monitors", kMonitorsInitial, kMonitorsMax) {
-  functions = unchecked_functions = &gJniNativeInterface;
-  if (vm->check_jni) {
-    SetCheckJniEnabled(true);
-  }
-}
-
-JNIEnvExt::~JNIEnvExt() {
-}
-
-jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (obj == nullptr) {
-    return nullptr;
-  }
-  return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
-}
-
-void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (obj != nullptr) {
-    locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
-  }
-}
-void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
-  check_jni = enabled;
-  functions = enabled ? GetCheckJniNativeInterface() : &gJniNativeInterface;
-}
-
-void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
-  locals.Dump(os);
-  monitors.Dump(os);
-}
-
-void JNIEnvExt::PushFrame(int capacity) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  UNUSED(capacity);  // cpplint gets confused with (int) and thinks its a cast.
-  // TODO: take 'capacity' into account.
-  stacked_local_ref_cookies.push_back(local_ref_cookie);
-  local_ref_cookie = locals.GetSegmentState();
-}
-
-void JNIEnvExt::PopFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  locals.SetSegmentState(local_ref_cookie);
-  local_ref_cookie = stacked_local_ref_cookies.back();
-  stacked_local_ref_cookies.pop_back();
-}
-
-Offset JNIEnvExt::SegmentStateOffset() {
-  return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
-                IndirectReferenceTable::SegmentStateOffset().Int32Value());
-}
-
-// JNI Invocation interface.
-
-extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
-  const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
-  if (IsBadJniVersion(args->version)) {
-    LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
-    return JNI_EVERSION;
-  }
-  RuntimeOptions options;
-  for (int i = 0; i < args->nOptions; ++i) {
-    JavaVMOption* option = &args->options[i];
-    options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
-  }
-  bool ignore_unrecognized = args->ignoreUnrecognized;
-  if (!Runtime::Create(options, ignore_unrecognized)) {
-    return JNI_ERR;
-  }
-  Runtime* runtime = Runtime::Current();
-  bool started = runtime->Start();
-  if (!started) {
-    delete Thread::Current()->GetJniEnv();
-    delete runtime->GetJavaVM();
-    LOG(WARNING) << "CreateJavaVM failed";
-    return JNI_ERR;
-  }
-  *p_env = Thread::Current()->GetJniEnv();
-  *p_vm = runtime->GetJavaVM();
-  return JNI_OK;
-}
-
-extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
-  Runtime* runtime = Runtime::Current();
-  if (runtime == nullptr) {
-    *vm_count = 0;
-  } else {
-    *vm_count = 1;
-    vms[0] = runtime->GetJavaVM();
-  }
-  return JNI_OK;
-}
-
-// Historically unsupported.
-extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* /*vm_args*/) {
-  return JNI_ERR;
-}
-
-class JII {
- public:
-  static jint DestroyJavaVM(JavaVM* vm) {
-    if (vm == nullptr) {
-      return JNI_ERR;
-    }
-    JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
-    delete raw_vm->runtime;
-    return JNI_OK;
-  }
-
-  static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
-    return JII_AttachCurrentThread(vm, p_env, thr_args, false);
-  }
-
-  static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
-    return JII_AttachCurrentThread(vm, p_env, thr_args, true);
-  }
-
-  static jint DetachCurrentThread(JavaVM* vm) {
-    if (vm == nullptr || Thread::Current() == nullptr) {
-      return JNI_ERR;
-    }
-    JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
-    Runtime* runtime = raw_vm->runtime;
-    runtime->DetachCurrentThread();
-    return JNI_OK;
-  }
-
-  static jint GetEnv(JavaVM* vm, void** env, jint version) {
-    // GetEnv always returns a JNIEnv* for the most current supported JNI version,
-    // and unlike other calls that take a JNI version doesn't care if you supply
-    // JNI_VERSION_1_1, which we don't otherwise support.
-    if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
-      LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
-      return JNI_EVERSION;
-    }
-    if (vm == nullptr || env == nullptr) {
-      return JNI_ERR;
-    }
-    Thread* thread = Thread::Current();
-    if (thread == nullptr) {
-      *env = nullptr;
-      return JNI_EDETACHED;
-    }
-    *env = thread->GetJniEnv();
-    return JNI_OK;
-  }
-};
-
-const JNIInvokeInterface gJniInvokeInterface = {
-  nullptr,  // reserved0
-  nullptr,  // reserved1
-  nullptr,  // reserved2
-  JII::DestroyJavaVM,
-  JII::AttachCurrentThread,
-  JII::DetachCurrentThread,
-  JII::GetEnv,
-  JII::AttachCurrentThreadAsDaemon
-};
-
-JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
-    : runtime(runtime),
-      check_jni_abort_hook(nullptr),
-      check_jni_abort_hook_data(nullptr),
-      check_jni(false),
-      force_copy(false),  // TODO: add a way to enable this
-      trace(options->jni_trace_),
-      globals_lock("JNI global reference table lock"),
-      globals(gGlobalsInitial, gGlobalsMax, kGlobal),
-      libraries_lock("JNI shared libraries map lock", kLoadLibraryLock),
-      libraries(new Libraries),
-      weak_globals_lock_("JNI weak global reference table lock"),
-      weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
-      allow_new_weak_globals_(true),
-      weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
-  functions = unchecked_functions = &gJniInvokeInterface;
-  if (options->check_jni_) {
-    SetCheckJniEnabled(true);
-  }
-}
-
-JavaVMExt::~JavaVMExt() {
-  delete libraries;
-}
-
-jweak JavaVMExt::AddWeakGlobalReference(Thread* self, mirror::Object* obj) {
-  if (obj == nullptr) {
-    return nullptr;
-  }
-  MutexLock mu(self, weak_globals_lock_);
-  while (UNLIKELY(!allow_new_weak_globals_)) {
-    weak_globals_add_condition_.WaitHoldingLocks(self);
-  }
-  IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
-  return reinterpret_cast<jweak>(ref);
-}
-
-void JavaVMExt::DeleteWeakGlobalRef(Thread* self, jweak obj) {
-  MutexLock mu(self, weak_globals_lock_);
-  if (!weak_globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
-    LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
-                 << "failed to find entry";
-  }
-}
-
-void JavaVMExt::SetCheckJniEnabled(bool enabled) {
-  check_jni = enabled;
-  functions = enabled ? GetCheckJniInvokeInterface() : &gJniInvokeInterface;
-}
-
-void JavaVMExt::DumpForSigQuit(std::ostream& os) {
-  os << "JNI: CheckJNI is " << (check_jni ? "on" : "off");
-  if (force_copy) {
-    os << " (with forcecopy)";
-  }
-  Thread* self = Thread::Current();
-  {
-    ReaderMutexLock mu(self, globals_lock);
-    os << "; globals=" << globals.Capacity();
-  }
-  {
-    MutexLock mu(self, weak_globals_lock_);
-    if (weak_globals_.Capacity() > 0) {
-      os << " (plus " << weak_globals_.Capacity() << " weak)";
-    }
-  }
-  os << '\n';
-
-  {
-    MutexLock mu(self, libraries_lock);
-    os << "Libraries: " << Dumpable<Libraries>(*libraries) << " (" << libraries->size() << ")\n";
-  }
-}
-
-void JavaVMExt::DisallowNewWeakGlobals() {
-  MutexLock mu(Thread::Current(), weak_globals_lock_);
-  allow_new_weak_globals_ = false;
-}
-
-void JavaVMExt::AllowNewWeakGlobals() {
-  Thread* self = Thread::Current();
-  MutexLock mu(self, weak_globals_lock_);
-  allow_new_weak_globals_ = true;
-  weak_globals_add_condition_.Broadcast(self);
-}
-
-mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
-  MutexLock mu(self, weak_globals_lock_);
-  while (UNLIKELY(!allow_new_weak_globals_)) {
-    weak_globals_add_condition_.WaitHoldingLocks(self);
-  }
-  return weak_globals_.Get(ref);
-}
-
-void JavaVMExt::DumpReferenceTables(std::ostream& os) {
-  Thread* self = Thread::Current();
-  {
-    ReaderMutexLock mu(self, globals_lock);
-    globals.Dump(os);
-  }
-  {
-    MutexLock mu(self, weak_globals_lock_);
-    weak_globals_.Dump(os);
-  }
-}
-
-bool JavaVMExt::LoadNativeLibrary(const std::string& path,
-                                  Handle<mirror::ClassLoader> class_loader,
-                                  std::string* detail) {
-  detail->clear();
-
-  // See if we've already loaded this library.  If we have, and the class loader
-  // matches, return successfully without doing anything.
-  // TODO: for better results we should canonicalize the pathname (or even compare
-  // inodes). This implementation is fine if everybody is using System.loadLibrary.
-  SharedLibrary* library;
-  Thread* self = Thread::Current();
-  {
-    // TODO: move the locking (and more of this logic) into Libraries.
-    MutexLock mu(self, libraries_lock);
-    library = libraries->Get(path);
-  }
-  if (library != nullptr) {
-    if (library->GetClassLoader() != class_loader.Get()) {
-      // The library will be associated with class_loader. The JNI
-      // spec says we can't load the same library into more than one
-      // class loader.
-      StringAppendF(detail, "Shared library \"%s\" already opened by "
-          "ClassLoader %p; can't open in ClassLoader %p",
-          path.c_str(), library->GetClassLoader(), class_loader.Get());
-      LOG(WARNING) << detail;
-      return false;
-    }
-    VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
-              << "ClassLoader " << class_loader.Get() << "]";
-    if (!library->CheckOnLoadResult()) {
-      StringAppendF(detail, "JNI_OnLoad failed on a previous attempt "
-          "to load \"%s\"", path.c_str());
-      return false;
-    }
-    return true;
-  }
-
-  // Open the shared library.  Because we're using a full path, the system
-  // doesn't have to search through LD_LIBRARY_PATH.  (It may do so to
-  // resolve this library's dependencies though.)
-
-  // Failures here are expected when java.library.path has several entries
-  // and we have to hunt for the lib.
-
-  // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
-  // class unloading. Libraries will only be unloaded when the reference count (incremented by
-  // dlopen) becomes zero from dlclose.
-
-  // This can execute slowly for a large library on a busy system, so we
-  // want to switch from kRunnable while it executes.  This allows the GC to ignore us.
-  self->TransitionFromRunnableToSuspended(kWaitingForJniOnLoad);
-  const char* path_str = path.empty() ? nullptr : path.c_str();
-  void* handle = dlopen(path_str, RTLD_LAZY);
-  bool needs_native_bridge = false;
-  if (handle == nullptr) {
-    if (android::NativeBridgeIsSupported(path_str)) {
-      handle = android::NativeBridgeLoadLibrary(path_str, RTLD_LAZY);
-      needs_native_bridge = true;
-    }
-  }
-  self->TransitionFromSuspendedToRunnable();
-
-  VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
-
-  if (handle == nullptr) {
-    *detail = dlerror();
-    LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << *detail;
-    return false;
-  }
-
-  // Create a new entry.
-  // TODO: move the locking (and more of this logic) into Libraries.
-  bool created_library = false;
-  {
-    MutexLock mu(self, libraries_lock);
-    library = libraries->Get(path);
-    if (library == nullptr) {  // We won race to get libraries_lock
-      library = new SharedLibrary(path, handle, class_loader.Get());
-      libraries->Put(path, library);
-      created_library = true;
-    }
-  }
-  if (!created_library) {
-    LOG(INFO) << "WOW: we lost a race to add shared library: "
-        << "\"" << path << "\" ClassLoader=" << class_loader.Get();
-    return library->CheckOnLoadResult();
-  }
-
-  VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader.Get()
-      << "]";
-
-  bool was_successful = false;
-  void* sym = nullptr;
-  if (UNLIKELY(needs_native_bridge)) {
-    library->SetNeedsNativeBridge();
-    sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
-  } else {
-    sym = dlsym(handle, "JNI_OnLoad");
-  }
-
-  if (sym == nullptr) {
-    VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
-    was_successful = true;
-  } else {
-    // Call JNI_OnLoad.  We have to override the current class
-    // loader, which will always be "null" since the stuff at the
-    // top of the stack is around Runtime.loadLibrary().  (See
-    // the comments in the JNI FindClass function.)
-    typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
-    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
-    StackHandleScope<1> hs(self);
-    Handle<mirror::ClassLoader> old_class_loader(hs.NewHandle(self->GetClassLoaderOverride()));
-    self->SetClassLoaderOverride(class_loader.Get());
-
-    int version = 0;
-    {
-      ScopedThreadStateChange tsc(self, kNative);
-      VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
-      version = (*jni_on_load)(this, nullptr);
-    }
-
-    self->SetClassLoaderOverride(old_class_loader.Get());
-
-    if (version == JNI_ERR) {
-      StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
-    } else if (IsBadJniVersion(version)) {
-      StringAppendF(detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
-                    path.c_str(), version);
-      // It's unwise to call dlclose() here, but we can mark it
-      // as bad and ensure that future load attempts will fail.
-      // We don't know how far JNI_OnLoad got, so there could
-      // be some partially-initialized stuff accessible through
-      // newly-registered native method calls.  We could try to
-      // unregister them, but that doesn't seem worthwhile.
-    } else {
-      was_successful = true;
-    }
-    VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
-              << " from JNI_OnLoad in \"" << path << "\"]";
-  }
-
-  library->SetResult(was_successful);
-  return was_successful;
-}
-
-void* JavaVMExt::FindCodeForNativeMethod(mirror::ArtMethod* m) {
-  CHECK(m->IsNative());
-  mirror::Class* c = m->GetDeclaringClass();
-  // If this is a static method, it could be called before the class has been initialized.
-  if (m->IsStatic()) {
-    c = EnsureInitialized(Thread::Current(), c);
-    if (c == nullptr) {
-      return nullptr;
-    }
-  } else {
-    CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m);
-  }
-  std::string detail;
-  void* native_method;
-  Thread* self = Thread::Current();
-  {
-    MutexLock mu(self, libraries_lock);
-    native_method = libraries->FindNativeMethod(m, detail);
-  }
-  // Throwing can cause libraries_lock to be reacquired.
-  if (native_method == nullptr) {
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
-  }
-  return native_method;
-}
-
-void JavaVMExt::SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg) {
-  MutexLock mu(Thread::Current(), weak_globals_lock_);
-  for (mirror::Object** entry : weak_globals_) {
-    // Since this is called by the GC, we don't need a read barrier.
-    mirror::Object* obj = *entry;
-    mirror::Object* new_obj = callback(obj, arg);
-    if (new_obj == nullptr) {
-      new_obj = kClearedJniWeakGlobal;
-    }
-    *entry = new_obj;
-  }
-}
-
-void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
-  Thread* self = Thread::Current();
-  {
-    ReaderMutexLock mu(self, globals_lock);
-    globals.VisitRoots(callback, arg, 0, kRootJNIGlobal);
-  }
-  {
-    MutexLock mu(self, libraries_lock);
-    // Libraries contains shared libraries which hold a pointer to a class loader.
-    libraries->VisitRoots(callback, arg);
-  }
-  // The weak_globals table is visited by the GC itself (because it mutates the table).
+const JNINativeInterface* GetJniNativeInterface() {
+  return &gJniNativeInterface;
 }
 
 void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index 25ed2f6..48b10f5 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -17,16 +17,8 @@
 #ifndef ART_RUNTIME_JNI_INTERNAL_H_
 #define ART_RUNTIME_JNI_INTERNAL_H_
 
-#include "jni.h"
-
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "indirect_reference_table.h"
-#include "object_callbacks.h"
-#include "reference_table.h"
-
+#include <jni.h>
 #include <iosfwd>
-#include <string>
 
 #ifndef NATIVE_METHOD
 #define NATIVE_METHOD(className, functionName, signature) \
@@ -36,185 +28,18 @@
   RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods))
 
 namespace art {
-namespace mirror {
-  class ArtField;
-  class ArtMethod;
-  class ClassLoader;
-}  // namespace mirror
-union JValue;
-class Libraries;
-class ParsedOptions;
-class Runtime;
-class ScopedObjectAccess;
-template<class T> class Handle;
-class Thread;
 
-void JniAbortF(const char* jni_function_name, const char* fmt, ...)
-    __attribute__((__format__(__printf__, 2, 3)));
+const JNINativeInterface* GetJniNativeInterface();
+
+// Similar to RegisterNatives except its passed a descriptor for a class name and failures are
+// fatal.
 void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
                            jint method_count);
 
 int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause);
 
-class JavaVMExt : public JavaVM {
- public:
-  JavaVMExt(Runtime* runtime, ParsedOptions* options);
-  ~JavaVMExt();
-
-  /**
-   * Loads the given shared library. 'path' is an absolute pathname.
-   *
-   * Returns 'true' on success. On failure, sets 'detail' to a
-   * human-readable description of the error.
-   */
-  bool LoadNativeLibrary(const std::string& path, Handle<mirror::ClassLoader> class_loader,
-                         std::string* detail)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  /**
-   * Returns a pointer to the code for the native method 'm', found
-   * using dlsym(3) on every native library that's been loaded so far.
-   */
-  void* FindCodeForNativeMethod(mirror::ArtMethod* m)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void DumpForSigQuit(std::ostream& os);
-
-  void DumpReferenceTables(std::ostream& os)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void SetCheckJniEnabled(bool enabled);
-
-  void VisitRoots(RootCallback* callback, void* arg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  jweak AddWeakGlobalReference(Thread* self, mirror::Object* obj)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void DeleteWeakGlobalRef(Thread* self, jweak obj)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  Runtime* runtime;
-
-  // Used for testing. By default, we'll LOG(FATAL) the reason.
-  void (*check_jni_abort_hook)(void* data, const std::string& reason);
-  void* check_jni_abort_hook_data;
-
-  // Extra checking.
-  bool check_jni;
-  bool force_copy;
-
-  // Extra diagnostics.
-  std::string trace;
-
-  // JNI global references.
-  ReaderWriterMutex globals_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
-  // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
-  IndirectReferenceTable globals;
-
-  Mutex libraries_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
-  Libraries* libraries GUARDED_BY(libraries_lock);
-
-  // Used by -Xcheck:jni.
-  const JNIInvokeInterface* unchecked_functions;
-
- private:
-  // TODO: Make the other members of this class also private.
-  // JNI weak global references.
-  Mutex weak_globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  // Since weak_globals_ contain weak roots, be careful not to
-  // directly access the object references in it. Use Get() with the
-  // read barrier enabled.
-  IndirectReferenceTable weak_globals_ GUARDED_BY(weak_globals_lock_);
-  bool allow_new_weak_globals_ GUARDED_BY(weak_globals_lock_);
-  ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
-};
-
-struct JNIEnvExt : public JNIEnv {
-  JNIEnvExt(Thread* self, JavaVMExt* vm);
-  ~JNIEnvExt();
-
-  void DumpReferenceTables(std::ostream& os)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void SetCheckJniEnabled(bool enabled);
-
-  void PushFrame(int capacity);
-  void PopFrame();
-
-  template<typename T>
-  T AddLocalReference(mirror::Object* obj)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static Offset SegmentStateOffset();
-
-  static Offset LocalRefCookieOffset() {
-    return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
-  }
-
-  static Offset SelfOffset() {
-    return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
-  }
-
-  jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  Thread* const self;
-  JavaVMExt* vm;
-
-  // Cookie used when using the local indirect reference table.
-  uint32_t local_ref_cookie;
-
-  // JNI local references.
-  IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
-
-  // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
-  // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
-  // to a native method.
-  std::vector<uint32_t> stacked_local_ref_cookies;
-
-  // Frequently-accessed fields cached from JavaVM.
-  bool check_jni;
-
-  // How many nested "critical" JNI calls are we in?
-  int critical;
-
-  // Entered JNI monitors, for bulk exit on thread detach.
-  ReferenceTable monitors;
-
-  // Used by -Xcheck:jni.
-  const JNINativeInterface* unchecked_functions;
-};
-
-const JNINativeInterface* GetCheckJniNativeInterface();
-const JNIInvokeInterface* GetCheckJniInvokeInterface();
-
-// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
-// compiler
-class ScopedJniEnvLocalRefState {
- public:
-  explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
-    saved_local_ref_cookie_ = env->local_ref_cookie;
-    env->local_ref_cookie = env->locals.GetSegmentState();
-  }
-
-  ~ScopedJniEnvLocalRefState() {
-    env_->locals.SetSegmentState(env_->local_ref_cookie);
-    env_->local_ref_cookie = saved_local_ref_cookie_;
-  }
-
- private:
-  JNIEnvExt* env_;
-  uint32_t saved_local_ref_cookie_;
-  DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
-};
-
 }  // namespace art
 
 std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs);
+
 #endif  // ART_RUNTIME_JNI_INTERNAL_H_
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index bb46321..20d031c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -17,6 +17,7 @@
 #include "jni_internal.h"
 
 #include "common_compiler_test.h"
+#include "java_vm_ext.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/string-inl.h"
 #include "scoped_thread_state_change.h"
@@ -53,24 +54,15 @@
   }
 
   void ExpectException(jclass exception_class) {
-    EXPECT_TRUE(env_->ExceptionCheck());
+    ScopedObjectAccess soa(env_);
+    EXPECT_TRUE(env_->ExceptionCheck())
+        << PrettyDescriptor(soa.Decode<mirror::Class*>(exception_class));
     jthrowable exception = env_->ExceptionOccurred();
     EXPECT_NE(nullptr, exception);
     env_->ExceptionClear();
     EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class));
   }
 
-  void ExpectClassFound(const char* name) {
-    EXPECT_NE(env_->FindClass(name), nullptr) << name;
-    EXPECT_FALSE(env_->ExceptionCheck()) << name;
-  }
-
-  void ExpectClassNotFound(const char* name) {
-    EXPECT_EQ(env_->FindClass(name), nullptr) << name;
-    EXPECT_TRUE(env_->ExceptionCheck()) << name;
-    env_->ExceptionClear();
-  }
-
   void CleanUpJniEnv() {
     if (aioobe_ != nullptr) {
       env_->DeleteGlobalRef(aioobe_);
@@ -98,6 +90,510 @@
     return soa.AddLocalReference<jclass>(c);
   }
 
+  void ExpectClassFound(const char* name) {
+    EXPECT_NE(env_->FindClass(name), nullptr) << name;
+    EXPECT_FALSE(env_->ExceptionCheck()) << name;
+  }
+
+  void ExpectClassNotFound(const char* name, bool check_jni, const char* check_jni_msg,
+                           CheckJniAbortCatcher* abort_catcher) {
+    EXPECT_EQ(env_->FindClass(name), nullptr) << name;
+    if (!check_jni || check_jni_msg == nullptr) {
+      EXPECT_TRUE(env_->ExceptionCheck()) << name;
+      env_->ExceptionClear();
+    } else {
+      abort_catcher->Check(check_jni_msg);
+    }
+  }
+
+  void FindClassTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    // Null argument is always an abort.
+    env_->FindClass(nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+
+    // Reference types...
+    ExpectClassFound("java/lang/String");
+    // ...for arrays too, where you must include "L;".
+    ExpectClassFound("[Ljava/lang/String;");
+    // Primitive arrays are okay too, if the primitive type is valid.
+    ExpectClassFound("[C");
+
+    // But primitive types aren't allowed...
+    ExpectClassNotFound("C", check_jni, nullptr, &check_jni_abort_catcher);
+    ExpectClassNotFound("V", check_jni, nullptr, &check_jni_abort_catcher);
+    ExpectClassNotFound("K", check_jni, nullptr, &check_jni_abort_catcher);
+
+    if (check_jni) {
+      // Check JNI will reject invalid class names as aborts but without pending exceptions.
+      EXPECT_EQ(env_->FindClass("java.lang.String"), nullptr);
+      EXPECT_FALSE(env_->ExceptionCheck());
+      check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
+
+      EXPECT_EQ(env_->FindClass("[Ljava.lang.String;"), nullptr);
+      EXPECT_FALSE(env_->ExceptionCheck());
+      check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
+    } else {
+      // Without check JNI we're tolerant and replace '.' with '/'.
+      ExpectClassFound("java.lang.String");
+      ExpectClassFound("[Ljava.lang.String;");
+    }
+
+    ExpectClassNotFound("Ljava.lang.String;", check_jni, "illegal class name 'Ljava.lang.String;'",
+                        &check_jni_abort_catcher);
+    ExpectClassNotFound("[java.lang.String", check_jni, "illegal class name '[java.lang.String'",
+                        &check_jni_abort_catcher);
+
+    // You can't include the "L;" in a JNI class descriptor.
+    ExpectClassNotFound("Ljava/lang/String;", check_jni, "illegal class name 'Ljava/lang/String;'",
+                        &check_jni_abort_catcher);
+
+    // But you must include it for an array of any reference type.
+    ExpectClassNotFound("[java/lang/String", check_jni, "illegal class name '[java/lang/String'",
+                        &check_jni_abort_catcher);
+
+    ExpectClassNotFound("[K", check_jni, "illegal class name '[K'", &check_jni_abort_catcher);
+
+    // Void arrays aren't allowed.
+    ExpectClassNotFound("[V", check_jni, "illegal class name '[V'", &check_jni_abort_catcher);
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetFieldIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+
+    jfieldID fid = env_->GetFieldID(nullptr, "count", "I");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "GetFieldID received NULL jclass"
+                                            : "java_class == null");
+    fid = env_->GetFieldID(c, nullptr, "I");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    fid = env_->GetFieldID(c, "count", nullptr);
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetStaticFieldIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+
+    jfieldID fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "GetStaticFieldID received NULL jclass"
+                                            : "java_class == null");
+    fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
+    EXPECT_EQ(nullptr, fid);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetMethodIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jmethodID method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "GetMethodID received NULL jclass"
+                                            : "java_class == null");
+    jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
+    ASSERT_TRUE(jlnsme != nullptr);
+    method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    method = env_->GetMethodID(jlnsme, "<init>", nullptr);
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetStaticMethodIdBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jmethodID method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "GetStaticMethodID received NULL jclass"
+                                            : "java_class == null");
+    jclass jlstring = env_->FindClass("java/lang/String");
+    method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "name == null");
+    method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
+    EXPECT_EQ(nullptr, method);
+    check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+                                            : "sig == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetFromReflectedField_ToReflectedFieldBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+    jfieldID fid = env_->GetFieldID(c, "count", "I");
+    ASSERT_NE(fid, nullptr);
+
+    // Check class argument for null argument, not checked in non-check JNI.
+    jobject field = env_->ToReflectedField(nullptr, fid, JNI_FALSE);
+    if (check_jni) {
+      EXPECT_EQ(field, nullptr);
+      check_jni_abort_catcher.Check("ToReflectedField received NULL jclass");
+    } else {
+      EXPECT_NE(field, nullptr);
+    }
+
+    field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
+    EXPECT_EQ(field, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "jfieldID was NULL"
+                                            : "fid == null");
+
+    fid = env_->FromReflectedField(nullptr);
+    ASSERT_EQ(fid, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "expected non-null java.lang.reflect.Field"
+                                            : "jlr_field == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher check_jni_abort_catcher;
+
+    jclass c = env_->FindClass("java/lang/String");
+    ASSERT_NE(c, nullptr);
+    jmethodID mid = env_->GetMethodID(c, "<init>", "()V");
+    ASSERT_NE(mid, nullptr);
+
+    // Check class argument for null argument, not checked in non-check JNI.
+    jobject method = env_->ToReflectedMethod(nullptr, mid, JNI_FALSE);
+    if (check_jni) {
+      EXPECT_EQ(method, nullptr);
+      check_jni_abort_catcher.Check("ToReflectedMethod received NULL jclass");
+    } else {
+      EXPECT_NE(method, nullptr);
+    }
+
+    method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
+    EXPECT_EQ(method, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "jmethodID was NULL"
+                                            : "mid == null");
+    mid = env_->FromReflectedMethod(method);
+    ASSERT_EQ(mid, nullptr);
+    check_jni_abort_catcher.Check(check_jni ? "expected non-null method" : "jlr_method == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void RegisterAndUnregisterNativesBadArguments(bool check_jni,
+                                                CheckJniAbortCatcher* check_jni_abort_catcher) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    // Passing a class of null is a failure.
+    {
+      JNINativeMethod methods[] = { };
+      EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
+      check_jni_abort_catcher->Check(check_jni ? "RegisterNatives received NULL jclass"
+                                               : "java_class == null");
+    }
+
+    // Passing methods as null is a failure.
+    jclass jlobject = env_->FindClass("java/lang/Object");
+    EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
+    check_jni_abort_catcher->Check("methods == null");
+
+    // Unregisters null is a failure.
+    EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
+    check_jni_abort_catcher->Check(check_jni ? "UnregisterNatives received NULL jclass"
+                                             : "java_class == null");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+
+  void GetPrimitiveArrayElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jbooleanArray array = env_->NewBooleanArray(10);
+    jboolean is_copy;
+    EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to get byte primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to get short primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to get char primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to get int primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to get long primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to get float primitive array elements with an object of type boolean[]");
+    EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to get double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
+              nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to get boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
+              nullptr);
+    jni_abort_catcher.Check(
+        check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "attempt to get boolean primitive array elements with an object of type java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void ReleasePrimitiveArrayElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jbooleanArray array = env_->NewBooleanArray(10);
+    ASSERT_TRUE(array != nullptr);
+    jboolean is_copy;
+    jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
+    ASSERT_TRUE(elements != nullptr);
+    env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
+                                   reinterpret_cast<jbyte*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to release byte primitive array elements with an object of type boolean[]");
+    env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
+                                    reinterpret_cast<jshort*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to release short primitive array elements with an object of type boolean[]");
+    env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
+                                   reinterpret_cast<jchar*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to release char primitive array elements with an object of type boolean[]");
+    env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
+                                  reinterpret_cast<jint*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to release int primitive array elements with an object of type boolean[]");
+    env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
+                                   reinterpret_cast<jlong*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to release long primitive array elements with an object of type boolean[]");
+    env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
+                                    reinterpret_cast<jfloat*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to release float primitive array elements with an object of type boolean[]");
+    env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
+                                     reinterpret_cast<jdouble*>(elements), 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to release double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to release boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
+    jni_abort_catcher.Check(
+        check_jni ? "jarray argument has non-array type: java.lang.String"
+            : "attempt to release boolean primitive array elements with an object of type "
+              "java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetReleasePrimitiveArrayCriticalOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jobject object = env_->NewStringUTF("Test String");
+    jboolean is_copy;
+    void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "expected primitive array, given java.lang.String");
+    env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "expected primitive array, given java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void GetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+    constexpr size_t kLength = 10;
+    jbooleanArray array = env_->NewBooleanArray(kLength);
+    ASSERT_TRUE(array != nullptr);
+    jboolean elements[kLength];
+    env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+                             reinterpret_cast<jbyte*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to get region of byte primitive array elements with an object of type boolean[]");
+    env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+                              reinterpret_cast<jshort*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to get region of short primitive array elements with an object of type boolean[]");
+    env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+                             reinterpret_cast<jchar*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to get region of char primitive array elements with an object of type boolean[]");
+    env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+                            reinterpret_cast<jint*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to get region of int primitive array elements with an object of type boolean[]");
+    env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+                             reinterpret_cast<jlong*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to get region of long primitive array elements with an object of type boolean[]");
+    env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+                              reinterpret_cast<jfloat*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to get region of float primitive array elements with an object of type boolean[]");
+    env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+                               reinterpret_cast<jdouble*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to get region of double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to get region of boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "attempt to get region of boolean primitive array elements with an object of type "
+          "java.lang.String");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void SetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+    constexpr size_t kLength = 10;
+    jbooleanArray array = env_->NewBooleanArray(kLength);
+    ASSERT_TRUE(array != nullptr);
+    jboolean elements[kLength];
+    env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+                             reinterpret_cast<jbyte*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected byte[]"
+            : "attempt to set region of byte primitive array elements with an object of type boolean[]");
+    env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+                              reinterpret_cast<jshort*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected short[]"
+            : "attempt to set region of short primitive array elements with an object of type boolean[]");
+    env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+                             reinterpret_cast<jchar*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected char[]"
+            : "attempt to set region of char primitive array elements with an object of type boolean[]");
+    env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+                            reinterpret_cast<jint*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected int[]"
+            : "attempt to set region of int primitive array elements with an object of type boolean[]");
+    env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+                             reinterpret_cast<jlong*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected long[]"
+            : "attempt to set region of long primitive array elements with an object of type boolean[]");
+    env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+                              reinterpret_cast<jfloat*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected float[]"
+            : "attempt to set region of float primitive array elements with an object of type boolean[]");
+    env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+                               reinterpret_cast<jdouble*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type boolean[] expected double[]"
+            : "attempt to set region of double primitive array elements with an object of type boolean[]");
+    jbyteArray array2 = env_->NewByteArray(10);
+    env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(
+        check_jni ? "incompatible array type byte[] expected boolean[]"
+            : "attempt to set region of boolean primitive array elements with an object of type byte[]");
+    jobject object = env_->NewStringUTF("Test String");
+    env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+                                reinterpret_cast<jboolean*>(elements));
+    jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+        : "attempt to set region of boolean primitive array elements with an object of type "
+          "java.lang.String");
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
+  void NewObjectArrayBadArguments(bool check_jni) {
+    bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+    CheckJniAbortCatcher jni_abort_catcher;
+
+    jclass element_class = env_->FindClass("java/lang/String");
+    ASSERT_NE(element_class, nullptr);
+
+    env_->NewObjectArray(-1, element_class, nullptr);
+    jni_abort_catcher.Check(check_jni ? "negative jsize: -1" : "negative array length: -1");
+
+    env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
+    jni_abort_catcher.Check(check_jni ? "negative jsize: -2147483648"
+        : "negative array length: -2147483648");
+
+    EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+  }
+
   JavaVMExt* vm_;
   JNIEnv* env_;
   jclass aioobe_;
@@ -125,48 +621,8 @@
 }
 
 TEST_F(JniInternalTest, FindClass) {
-  // Reference types...
-  ExpectClassFound("java/lang/String");
-  // ...for arrays too, where you must include "L;".
-  ExpectClassFound("[Ljava/lang/String;");
-  // Primitive arrays are okay too, if the primitive type is valid.
-  ExpectClassFound("[C");
-
-  {
-    CheckJniAbortCatcher check_jni_abort_catcher;
-    env_->FindClass(nullptr);
-    check_jni_abort_catcher.Check("name == null");
-
-    // We support . as well as / for compatibility, if -Xcheck:jni is off.
-    ExpectClassFound("java.lang.String");
-    check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
-    ExpectClassNotFound("Ljava.lang.String;");
-    check_jni_abort_catcher.Check("illegal class name 'Ljava.lang.String;'");
-    ExpectClassFound("[Ljava.lang.String;");
-    check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
-    ExpectClassNotFound("[java.lang.String");
-    check_jni_abort_catcher.Check("illegal class name '[java.lang.String'");
-
-    // You can't include the "L;" in a JNI class descriptor.
-    ExpectClassNotFound("Ljava/lang/String;");
-    check_jni_abort_catcher.Check("illegal class name 'Ljava/lang/String;'");
-
-    // But you must include it for an array of any reference type.
-    ExpectClassNotFound("[java/lang/String");
-    check_jni_abort_catcher.Check("illegal class name '[java/lang/String'");
-
-    ExpectClassNotFound("[K");
-    check_jni_abort_catcher.Check("illegal class name '[K'");
-
-    // Void arrays aren't allowed.
-    ExpectClassNotFound("[V");
-    check_jni_abort_catcher.Check("illegal class name '[V'");
-  }
-
-  // But primitive types aren't allowed...
-  ExpectClassNotFound("C");
-  ExpectClassNotFound("V");
-  ExpectClassNotFound("K");
+  FindClassTest(false);
+  FindClassTest(true);
 }
 
 TEST_F(JniInternalTest, GetFieldID) {
@@ -208,16 +664,8 @@
   ExpectException(jlnsfe);
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  fid = env_->GetFieldID(nullptr, "count", "I");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("java_class == null");
-  fid = env_->GetFieldID(c, nullptr, "I");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("name == null");
-  fid = env_->GetFieldID(c, "count", nullptr);
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("sig == null");
+  GetFieldIdBadArgumentTest(false);
+  GetFieldIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, GetStaticFieldID) {
@@ -253,16 +701,8 @@
   ExpectException(jlnsfe);
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("java_class == null");
-  fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("name == null");
-  fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
-  EXPECT_EQ(nullptr, fid);
-  check_jni_abort_catcher.Check("sig == null");
+  GetStaticFieldIdBadArgumentTest(false);
+  GetStaticFieldIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, GetMethodID) {
@@ -302,16 +742,8 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("java_class == null");
-  method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("name == null");
-  method = env_->GetMethodID(jlnsme, "<init>", nullptr);
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("sig == null");
+  GetMethodIdBadArgumentTest(false);
+  GetMethodIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, CallVoidMethodNullReceiver) {
@@ -325,7 +757,6 @@
 
   // Null object to CallVoidMethod.
   CheckJniAbortCatcher check_jni_abort_catcher;
-  method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
   env_->CallVoidMethod(nullptr, method);
   check_jni_abort_catcher.Check("null");
 }
@@ -356,16 +787,8 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("java_class == null");
-  method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("name == null");
-  method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
-  EXPECT_EQ(nullptr, method);
-  check_jni_abort_catcher.Check("sig == null");
+  GetStaticMethodIdBadArgumentTest(false);
+  GetStaticMethodIdBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
@@ -386,13 +809,8 @@
   ASSERT_EQ(4, env_->GetIntField(s, fid2));
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
-  EXPECT_EQ(field, nullptr);
-  check_jni_abort_catcher.Check("fid == null");
-  fid2 = env_->FromReflectedField(nullptr);
-  ASSERT_EQ(fid2, nullptr);
-  check_jni_abort_catcher.Check("jlr_field == null");
+  GetFromReflectedField_ToReflectedFieldBadArgumentTest(false);
+  GetFromReflectedField_ToReflectedFieldBadArgumentTest(true);
 }
 
 TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
@@ -433,13 +851,8 @@
   ASSERT_EQ(4, env_->CallIntMethod(s, mid2));
 
   // Bad arguments.
-  CheckJniAbortCatcher check_jni_abort_catcher;
-  method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
-  EXPECT_EQ(method, nullptr);
-  check_jni_abort_catcher.Check("mid == null");
-  mid2 = env_->FromReflectedMethod(method);
-  ASSERT_EQ(mid2, nullptr);
-  check_jni_abort_catcher.Check("jlr_method == null");
+  GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(false);
+  GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(true);
 }
 
 static void BogusMethod() {
@@ -514,23 +927,11 @@
   }
   EXPECT_FALSE(env_->ExceptionCheck());
 
-  // Passing a class of null is a failure.
-  {
-    JNINativeMethod methods[] = { };
-    EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
-    check_jni_abort_catcher.Check("java_class == null");
-  }
-
-  // Passing methods as null is a failure.
-  EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
-  check_jni_abort_catcher.Check("methods == null");
-
-  // Unregisters null is a failure.
-  EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
-  check_jni_abort_catcher.Check("java_class == null");
-
   // Unregistering a class with no natives is a warning.
   EXPECT_EQ(env_->UnregisterNatives(jlnsme), JNI_OK);
+
+  RegisterAndUnregisterNativesBadArguments(false, &check_jni_abort_catcher);
+  RegisterAndUnregisterNativesBadArguments(true, &check_jni_abort_catcher);
 }
 
 #define EXPECT_PRIMITIVE_ARRAY(new_fn, \
@@ -544,6 +945,7 @@
   \
   { \
     CheckJniAbortCatcher jni_abort_catcher; \
+    down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(false); \
     /* Allocate an negative sized array and check it has the right failure type. */ \
     EXPECT_EQ(env_->new_fn(-1), nullptr); \
     jni_abort_catcher.Check("negative array length: -1"); \
@@ -566,6 +968,7 @@
     jni_abort_catcher.Check("buf == null"); \
     env_->set_region_fn(a, 0, size, nullptr); \
     jni_abort_catcher.Check("buf == null"); \
+    down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(true); \
   } \
   /* Allocate an array and check it has the right type and length. */ \
   scalar_type ## Array a = env_->new_fn(size); \
@@ -670,189 +1073,28 @@
 }
 
 TEST_F(JniInternalTest, GetPrimitiveArrayElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  jbooleanArray array = env_->NewBooleanArray(10);
-  jboolean is_copy;
-  EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get byte primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get short primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get char primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get int primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get long primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get float primitive array elements with an object of type boolean[]");
-  EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
-            nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
-            nullptr);
-  jni_abort_catcher.Check(
-      "attempt to get boolean primitive array elements with an object of type java.lang.String");
+  GetPrimitiveArrayElementsOfWrongType(false);
+  GetPrimitiveArrayElementsOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, ReleasePrimitiveArrayElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  jbooleanArray array = env_->NewBooleanArray(10);
-  ASSERT_TRUE(array != nullptr);
-  jboolean is_copy;
-  jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
-  ASSERT_TRUE(elements != nullptr);
-  env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
-                                 reinterpret_cast<jbyte*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release byte primitive array elements with an object of type boolean[]");
-  env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
-                                  reinterpret_cast<jshort*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release short primitive array elements with an object of type boolean[]");
-  env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
-                                 reinterpret_cast<jchar*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release char primitive array elements with an object of type boolean[]");
-  env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
-                                reinterpret_cast<jint*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release int primitive array elements with an object of type boolean[]");
-  env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
-                                 reinterpret_cast<jlong*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release long primitive array elements with an object of type boolean[]");
-  env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
-                                  reinterpret_cast<jfloat*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release float primitive array elements with an object of type boolean[]");
-  env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
-                                  reinterpret_cast<jdouble*>(elements), 0);
-  jni_abort_catcher.Check(
-      "attempt to release double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
-  jni_abort_catcher.Check(
-      "attempt to release boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
-  jni_abort_catcher.Check(
-      "attempt to release boolean primitive array elements with an object of type "
-      "java.lang.String");
+  ReleasePrimitiveArrayElementsOfWrongType(false);
+  ReleasePrimitiveArrayElementsOfWrongType(true);
 }
+
 TEST_F(JniInternalTest, GetReleasePrimitiveArrayCriticalOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  jobject object = env_->NewStringUTF("Test String");
-  jboolean is_copy;
-  void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
-  jni_abort_catcher.Check("expected primitive array, given java.lang.String");
-  env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
-  jni_abort_catcher.Check("expected primitive array, given java.lang.String");
+  GetReleasePrimitiveArrayCriticalOfWrongType(false);
+  GetReleasePrimitiveArrayCriticalOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, GetPrimitiveArrayRegionElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  constexpr size_t kLength = 10;
-  jbooleanArray array = env_->NewBooleanArray(kLength);
-  ASSERT_TRUE(array != nullptr);
-  jboolean elements[kLength];
-  env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
-                           reinterpret_cast<jbyte*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of byte primitive array elements with an object of type boolean[]");
-  env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
-                            reinterpret_cast<jshort*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of short primitive array elements with an object of type boolean[]");
-  env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
-                           reinterpret_cast<jchar*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of char primitive array elements with an object of type boolean[]");
-  env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
-                          reinterpret_cast<jint*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of int primitive array elements with an object of type boolean[]");
-  env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
-                           reinterpret_cast<jlong*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of long primitive array elements with an object of type boolean[]");
-  env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
-                            reinterpret_cast<jfloat*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of float primitive array elements with an object of type boolean[]");
-  env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
-                           reinterpret_cast<jdouble*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to get region of boolean primitive array elements with an object of type "
-      "java.lang.String");
+  GetPrimitiveArrayRegionElementsOfWrongType(false);
+  GetPrimitiveArrayRegionElementsOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, SetPrimitiveArrayRegionElementsOfWrongType) {
-  CheckJniAbortCatcher jni_abort_catcher;
-  constexpr size_t kLength = 10;
-  jbooleanArray array = env_->NewBooleanArray(kLength);
-  ASSERT_TRUE(array != nullptr);
-  jboolean elements[kLength];
-  env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
-                           reinterpret_cast<jbyte*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of byte primitive array elements with an object of type boolean[]");
-  env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
-                            reinterpret_cast<jshort*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of short primitive array elements with an object of type boolean[]");
-  env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
-                           reinterpret_cast<jchar*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of char primitive array elements with an object of type boolean[]");
-  env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
-                          reinterpret_cast<jint*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of int primitive array elements with an object of type boolean[]");
-  env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
-                           reinterpret_cast<jlong*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of long primitive array elements with an object of type boolean[]");
-  env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
-                            reinterpret_cast<jfloat*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of float primitive array elements with an object of type boolean[]");
-  env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
-                           reinterpret_cast<jdouble*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of double primitive array elements with an object of type boolean[]");
-  jbyteArray array2 = env_->NewByteArray(10);
-  env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of boolean primitive array elements with an object of type byte[]");
-  jobject object = env_->NewStringUTF("Test String");
-  env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
-                              reinterpret_cast<jboolean*>(elements));
-  jni_abort_catcher.Check(
-      "attempt to set region of boolean primitive array elements with an object of type "
-      "java.lang.String");
+  SetPrimitiveArrayRegionElementsOfWrongType(false);
+  SetPrimitiveArrayRegionElementsOfWrongType(true);
 }
 
 TEST_F(JniInternalTest, NewObjectArray) {
@@ -873,12 +1115,8 @@
   EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), nullptr));
 
   // Negative array length checks.
-  CheckJniAbortCatcher jni_abort_catcher;
-  env_->NewObjectArray(-1, element_class, nullptr);
-  jni_abort_catcher.Check("negative array length: -1");
-
-  env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
-  jni_abort_catcher.Check("negative array length: -2147483648");
+  NewObjectArrayBadArguments(false);
+  NewObjectArrayBadArguments(true);
 }
 
 TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) {
@@ -888,6 +1126,7 @@
   };
   ASSERT_EQ(strlen(primitive_descriptors), arraysize(primitive_names));
 
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   CheckJniAbortCatcher jni_abort_catcher;
   for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
     env_->NewObjectArray(0, nullptr, nullptr);
@@ -897,6 +1136,16 @@
     std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
     jni_abort_catcher.Check(error_msg.c_str());
   }
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
+    env_->NewObjectArray(0, nullptr, nullptr);
+    jni_abort_catcher.Check("NewObjectArray received NULL jclass");
+    jclass primitive_class = GetPrimitiveClass(primitive_descriptors[i]);
+    env_->NewObjectArray(1, primitive_class, nullptr);
+    std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
+    jni_abort_catcher.Check(error_msg.c_str());
+  }
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, NewObjectArrayWithInitialValue) {
@@ -956,8 +1205,13 @@
 
   // Null as class should fail.
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
   jni_abort_catcher.Check("java_class == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
+  jni_abort_catcher.Check("GetSuperclass received NULL jclass");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, IsAssignableFrom) {
@@ -991,10 +1245,17 @@
 
   // Null as either class should fail.
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
   jni_abort_catcher.Check("java_class1 == null");
   EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
   jni_abort_catcher.Check("java_class2 == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
+  jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+  EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
+  jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, GetObjectRefType) {
@@ -1008,13 +1269,14 @@
   jweak weak_global = env_->NewWeakGlobalRef(local);
   EXPECT_EQ(JNIWeakGlobalRefType, env_->GetObjectRefType(weak_global));
 
+  CheckJniAbortCatcher jni_abort_catcher;
   jobject invalid = reinterpret_cast<jobject>(this);
   EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid));
+  jni_abort_catcher.Check("use of invalid jobject");
 
   // TODO: invoke a native method and test that its arguments are considered local references.
 
   // Null as object should fail.
-  CheckJniAbortCatcher jni_abort_catcher;
   EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(nullptr));
   jni_abort_catcher.Check("java_object == null");
 }
@@ -1079,10 +1341,17 @@
 
 TEST_F(JniInternalTest, NewStringNegativeLength) {
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   env_->NewString(nullptr, -1);
   jni_abort_catcher.Check("char_count < 0: -1");
   env_->NewString(nullptr, std::numeric_limits<jint>::min());
   jni_abort_catcher.Check("char_count < 0: -2147483648");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  env_->NewString(nullptr, -1);
+  jni_abort_catcher.Check("negative jsize: -1");
+  env_->NewString(nullptr, std::numeric_limits<jint>::min());
+  jni_abort_catcher.Check("negative jsize: -2147483648");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) {
@@ -1140,10 +1409,17 @@
 
 TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
   // Passing in a nullptr jstring is ignored normally, but caught by -Xcheck:jni.
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   {
     CheckJniAbortCatcher check_jni_abort_catcher;
     EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
-    check_jni_abort_catcher.Check("GetStringUTFChars received null jstring");
+  }
+  {
+    CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+    EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
+    check_jni_abort_catcher.Check("GetStringUTFChars received NULL jstring");
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   jstring s = env_->NewStringUTF("hello");
@@ -1238,10 +1514,17 @@
 
   // Null as array should fail.
   CheckJniAbortCatcher jni_abort_catcher;
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
   EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
   jni_abort_catcher.Check("java_array == null");
   env_->SetObjectArrayElement(nullptr, 0, nullptr);
   jni_abort_catcher.Check("java_array == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
+  jni_abort_catcher.Check("jarray was NULL");
+  env_->SetObjectArrayElement(nullptr, 0, nullptr);
+  jni_abort_catcher.Check("jarray was NULL");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 #define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \
@@ -1253,15 +1536,28 @@
     env_->SetStatic ## type ## Field(c, fid, value2); \
     EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \
     \
+    bool old_check_jni = vm_->SetCheckJniEnabled(false); \
+    { \
+      CheckJniAbortCatcher jni_abort_catcher; \
+      env_->GetStatic ## type ## Field(nullptr, fid); \
+      env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+    } \
     CheckJniAbortCatcher jni_abort_catcher; \
-    env_->GetStatic ## type ## Field(nullptr, fid); \
-    jni_abort_catcher.Check("received null jclass"); \
-    env_->SetStatic ## type ## Field(nullptr, fid, value1); \
-    jni_abort_catcher.Check("received null jclass"); \
     env_->GetStatic ## type ## Field(c, nullptr); \
     jni_abort_catcher.Check("fid == null"); \
     env_->SetStatic ## type ## Field(c, nullptr, value1); \
     jni_abort_catcher.Check("fid == null"); \
+    \
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+    env_->GetStatic ## type ## Field(nullptr, fid); \
+    jni_abort_catcher.Check("received NULL jclass"); \
+    env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+    jni_abort_catcher.Check("received NULL jclass"); \
+    env_->GetStatic ## type ## Field(c, nullptr); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    env_->SetStatic ## type ## Field(c, nullptr, value1); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
   } while (false)
 
 #define EXPECT_PRIMITIVE_FIELD(instance, type, field_name, sig, value1, value2) \
@@ -1273,6 +1569,7 @@
     env_->Set ## type ## Field(instance, fid, value2); \
     EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \
     \
+    bool old_check_jni = vm_->SetCheckJniEnabled(false); \
     CheckJniAbortCatcher jni_abort_catcher; \
     env_->Get ## type ## Field(nullptr, fid); \
     jni_abort_catcher.Check("obj == null"); \
@@ -1282,6 +1579,16 @@
     jni_abort_catcher.Check("fid == null"); \
     env_->Set ## type ## Field(instance, nullptr, value1); \
     jni_abort_catcher.Check("fid == null"); \
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+    env_->Get ## type ## Field(nullptr, fid); \
+    jni_abort_catcher.Check("field operation on NULL object:"); \
+    env_->Set ## type ## Field(nullptr, fid, value1); \
+    jni_abort_catcher.Check("field operation on NULL object:"); \
+    env_->Get ## type ## Field(instance, nullptr); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    env_->Set ## type ## Field(instance, nullptr, value1); \
+    jni_abort_catcher.Check("jfieldID was NULL"); \
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
   } while (false)
 
 
@@ -1373,12 +1680,17 @@
 
   // Currently, deleting an already-deleted reference is just a CheckJNI warning.
   {
+    bool old_check_jni = vm_->SetCheckJniEnabled(false);
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      env_->DeleteLocalRef(s);
+    }
     CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
     env_->DeleteLocalRef(s);
-
-    std::string expected(StringPrintf("native code passing in reference to "
-                                      "invalid local reference: %p", s));
+    std::string expected(StringPrintf("use of deleted local reference %p", s));
     check_jni_abort_catcher.Check(expected.c_str());
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   s = env_->NewStringUTF("");
@@ -1412,7 +1724,6 @@
   jobject outer;
   jobject inner1, inner2;
   ScopedObjectAccess soa(env_);
-  mirror::Object* inner2_direct_pointer;
   {
     ASSERT_EQ(JNI_OK, env_->PushLocalFrame(4));
     outer = env_->NewLocalRef(original);
@@ -1421,24 +1732,35 @@
       ASSERT_EQ(JNI_OK, env_->PushLocalFrame(4));
       inner1 = env_->NewLocalRef(outer);
       inner2 = env_->NewStringUTF("survivor");
-      inner2_direct_pointer = soa.Decode<mirror::Object*>(inner2);
-      env_->PopLocalFrame(inner2);
+      EXPECT_NE(env_->PopLocalFrame(inner2), nullptr);
     }
 
     EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
     EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(outer));
-    EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+      check_jni_abort_catcher.Check("use of deleted local reference");
+    }
 
     // Our local reference for the survivor is invalid because the survivor
     // gets a new local reference...
-    EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+      check_jni_abort_catcher.Check("use of deleted local reference");
+    }
 
-    env_->PopLocalFrame(nullptr);
+    EXPECT_EQ(env_->PopLocalFrame(nullptr), nullptr);
   }
   EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
+  CheckJniAbortCatcher check_jni_abort_catcher;
   EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(outer));
+  check_jni_abort_catcher.Check("use of deleted local reference");
   EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+  check_jni_abort_catcher.Check("use of deleted local reference");
   EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+  check_jni_abort_catcher.Check("use of deleted local reference");
 }
 
 TEST_F(JniInternalTest, NewGlobalRef_nullptr) {
@@ -1469,12 +1791,17 @@
 
   // Currently, deleting an already-deleted reference is just a CheckJNI warning.
   {
+    bool old_check_jni = vm_->SetCheckJniEnabled(false);
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      env_->DeleteGlobalRef(o);
+    }
     CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
     env_->DeleteGlobalRef(o);
-
-    std::string expected(StringPrintf("native code passing in reference to "
-                                      "invalid global reference: %p", o));
+    std::string expected(StringPrintf("use of deleted global reference %p", o));
     check_jni_abort_catcher.Check(expected.c_str());
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   jobject o1 = env_->NewGlobalRef(s);
@@ -1514,12 +1841,17 @@
 
   // Currently, deleting an already-deleted reference is just a CheckJNI warning.
   {
+    bool old_check_jni = vm_->SetCheckJniEnabled(false);
+    {
+      CheckJniAbortCatcher check_jni_abort_catcher;
+      env_->DeleteWeakGlobalRef(o);
+    }
     CheckJniAbortCatcher check_jni_abort_catcher;
+    EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
     env_->DeleteWeakGlobalRef(o);
-
-    std::string expected(StringPrintf("native code passing in reference to "
-                                      "invalid weak global reference: %p", o));
+    std::string expected(StringPrintf("use of deleted weak global reference %p", o));
     check_jni_abort_catcher.Check(expected.c_str());
+    EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
   }
 
   jobject o1 = env_->NewWeakGlobalRef(s);
@@ -1538,8 +1870,6 @@
 }
 
 TEST_F(JniInternalTest, Throw) {
-  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
-
   jclass exception_class = env_->FindClass("java/lang/RuntimeException");
   ASSERT_TRUE(exception_class != nullptr);
   jthrowable exception = reinterpret_cast<jthrowable>(env_->AllocObject(exception_class));
@@ -1550,11 +1880,18 @@
   jthrowable thrown_exception = env_->ExceptionOccurred();
   env_->ExceptionClear();
   EXPECT_TRUE(env_->IsSameObject(exception, thrown_exception));
+
+  // Bad argument.
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
+  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  CheckJniAbortCatcher check_jni_abort_catcher;
+  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+  check_jni_abort_catcher.Check("Throw received NULL jthrowable");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, ThrowNew) {
-  EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
-
   jclass exception_class = env_->FindClass("java/lang/RuntimeException");
   ASSERT_TRUE(exception_class != nullptr);
 
@@ -1571,6 +1908,16 @@
   thrown_exception = env_->ExceptionOccurred();
   env_->ExceptionClear();
   EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class));
+
+  // Bad argument.
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
+  CheckJniAbortCatcher check_jni_abort_catcher;
+  EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+  check_jni_abort_catcher.Check("c == null");
+  EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+  EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+  check_jni_abort_catcher.Check("ThrowNew received NULL jclass");
+  EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
 }
 
 TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) {
diff --git a/runtime/leb128.h b/runtime/leb128.h
index 89de16e..dfb42b8 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -137,25 +137,26 @@
   return dest;
 }
 
-// An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format.
-class Leb128EncodingVector {
+// An encoder that pushed uint32_t data onto the given std::vector.
+class Leb128Encoder {
  public:
-  Leb128EncodingVector() {
+  explicit Leb128Encoder(std::vector<uint8_t>* data) : data_(data) {
+    DCHECK(data != nullptr);
   }
 
   void Reserve(uint32_t size) {
-    data_.reserve(size);
+    data_->reserve(size);
   }
 
   void PushBackUnsigned(uint32_t value) {
     uint8_t out = value & 0x7f;
     value >>= 7;
     while (value != 0) {
-      data_.push_back(out | 0x80);
+      data_->push_back(out | 0x80);
       out = value & 0x7f;
       value >>= 7;
     }
-    data_.push_back(out);
+    data_->push_back(out);
   }
 
   template<typename It>
@@ -169,12 +170,12 @@
     uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
     uint8_t out = value & 0x7f;
     while (extra_bits != 0u) {
-      data_.push_back(out | 0x80);
+      data_->push_back(out | 0x80);
       value >>= 7;
       out = value & 0x7f;
       extra_bits >>= 7;
     }
-    data_.push_back(out);
+    data_->push_back(out);
   }
 
   template<typename It>
@@ -185,12 +186,23 @@
   }
 
   const std::vector<uint8_t>& GetData() const {
-    return data_;
+    return *data_;
+  }
+
+ protected:
+  std::vector<uint8_t>* const data_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Leb128Encoder);
+};
+
+// An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format.
+class Leb128EncodingVector FINAL : private std::vector<uint8_t>, public Leb128Encoder {
+ public:
+  Leb128EncodingVector() : Leb128Encoder(this) {
   }
 
  private:
-  std::vector<uint8_t> data_;
-
   DISALLOW_COPY_AND_ASSIGN(Leb128EncodingVector);
 };
 
diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h
index 414b3bb..cf6f83c 100644
--- a/runtime/lock_word-inl.h
+++ b/runtime/lock_word-inl.h
@@ -50,6 +50,7 @@
 inline LockWord::LockWord(Monitor* mon)
     : value_(mon->GetMonitorId() | (kStateFat << kStateShift)) {
   DCHECK_EQ(FatLockMonitor(), mon);
+  DCHECK_LE(mon->GetMonitorId(), static_cast<uint32_t>(kMaxMonitorId));
 }
 
 inline int32_t LockWord::GetHashCode() const {
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
index e585412..13cc3b0 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -63,6 +63,7 @@
 
     kThinLockOwnerShift = 0,
     kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1,
+    kThinLockMaxOwner = kThinLockOwnerMask,
     // Count in higher bits.
     kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift,
     kThinLockCountMask = (1 << kThinLockCountSize) - 1,
@@ -80,10 +81,13 @@
     kHashShift = 0,
     kHashSize = 32 - kStateSize,
     kHashMask = (1 << kHashSize) - 1,
+    kMaxHash = kHashMask,
+    kMaxMonitorId = kMaxHash
   };
 
   static LockWord FromThinLockId(uint32_t thread_id, uint32_t count) {
-    CHECK_LE(thread_id, static_cast<uint32_t>(kThinLockOwnerMask));
+    CHECK_LE(thread_id, static_cast<uint32_t>(kThinLockMaxOwner));
+    CHECK_LE(count, static_cast<uint32_t>(kThinLockMaxCount));
     return LockWord((thread_id << kThinLockOwnerShift) | (count << kThinLockCountShift) |
                      (kStateThinOrUnlocked << kStateShift));
   }
@@ -94,7 +98,7 @@
   }
 
   static LockWord FromHashCode(uint32_t hash_code) {
-    CHECK_LE(hash_code, static_cast<uint32_t>(kHashMask));
+    CHECK_LE(hash_code, static_cast<uint32_t>(kMaxHash));
     return LockWord((hash_code << kHashShift) | (kStateHash << kStateShift));
   }
 
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 0615abd..d755cb9 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -130,7 +130,6 @@
 uintptr_t MemMap::next_mem_pos_ = GenerateNextMemPos();
 #endif
 
-#if !defined(__APPLE__)  // TODO: Reanable after b/16861075 BacktraceMap issue is addressed.
 // Return true if the address range is contained in a single /proc/self/map entry.
 static bool ContainedWithinExistingMap(uintptr_t begin,
                                        uintptr_t end,
@@ -153,7 +152,6 @@
                             begin, end, maps.c_str());
   return false;
 }
-#endif
 
 // Return true if the address range does not conflict with any /proc/self/maps entry.
 static bool CheckNonOverlapping(uintptr_t begin,
@@ -388,6 +386,8 @@
                                  std::string* error_msg) {
   CHECK_NE(0, prot);
   CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
+  uintptr_t expected = reinterpret_cast<uintptr_t>(expected_ptr);
+  uintptr_t limit = expected + byte_count;
 
   // Note that we do not allow MAP_FIXED unless reuse == true, i.e we
   // expect his mapping to be contained within an existing map.
@@ -396,11 +396,7 @@
     // Only use this if you actually made the page reservation yourself.
     CHECK(expected_ptr != nullptr);
 
-#if !defined(__APPLE__)  // TODO: Reanable after b/16861075 BacktraceMap issue is addressed.
-    uintptr_t expected = reinterpret_cast<uintptr_t>(expected_ptr);
-    uintptr_t limit = expected + byte_count;
     DCHECK(ContainedWithinExistingMap(expected, limit, error_msg));
-#endif
     flags |= MAP_FIXED;
   } else {
     CHECK_EQ(0, flags & MAP_FIXED);
@@ -489,7 +485,7 @@
     MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
     maps_.insert(std::pair<void*, MemMap*>(base_begin_, this));
   }
-};
+}
 
 MemMap* MemMap::RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
                            std::string* error_msg) {
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index 69f618c..e54d0e0 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -18,6 +18,8 @@
 
 #include <memory>
 
+#include <valgrind.h>
+
 #include "gtest/gtest.h"
 
 namespace art {
@@ -198,17 +200,20 @@
 #endif
 
 TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) {
-  uintptr_t start_addr = ART_BASE_ADDRESS + 0x1000000;
-  std::string error_msg;
-  std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
-                                             reinterpret_cast<byte*>(start_addr),
-                                             0x21000000,
-                                             PROT_READ | PROT_WRITE,
-                                             true,
-                                             &error_msg));
-  ASSERT_TRUE(map.get() != nullptr) << error_msg;
-  ASSERT_TRUE(error_msg.empty());
-  ASSERT_EQ(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), start_addr);
+  // This test may not work under valgrind.
+  if (RUNNING_ON_VALGRIND == 0) {
+    uintptr_t start_addr = ART_BASE_ADDRESS + 0x1000000;
+    std::string error_msg;
+    std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
+                                                     reinterpret_cast<byte*>(start_addr),
+                                                     0x21000000,
+                                                     PROT_READ | PROT_WRITE,
+                                                     true,
+                                                     &error_msg));
+    ASSERT_TRUE(map.get() != nullptr) << error_msg;
+    ASSERT_TRUE(error_msg.empty());
+    ASSERT_EQ(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), start_addr);
+  }
 }
 
 TEST_F(MemMapTest, MapAnonymousOverflow) {
diff --git a/runtime/method_helper-inl.h b/runtime/method_helper-inl.h
index 9af835f..143f4bc 100644
--- a/runtime/method_helper-inl.h
+++ b/runtime/method_helper-inl.h
@@ -26,7 +26,9 @@
 
 namespace art {
 
-inline bool MethodHelper::HasSameNameAndSignature(MethodHelper* other) {
+template <template <class T> class HandleKind>
+template <template <class T2> class HandleKind2>
+inline bool MethodHelperT<HandleKind>::HasSameNameAndSignature(MethodHelperT<HandleKind2>* other) {
   const DexFile* dex_file = method_->GetDexFile();
   const DexFile::MethodId& mid = dex_file->GetMethodId(GetMethod()->GetDexMethodIndex());
   if (method_->GetDexCache() == other->method_->GetDexCache()) {
@@ -43,7 +45,9 @@
   return dex_file->GetMethodSignature(mid) == other_dex_file->GetMethodSignature(other_mid);
 }
 
-inline mirror::Class* MethodHelper::GetClassFromTypeIdx(uint16_t type_idx, bool resolve) {
+template <template <class T> class HandleKind>
+inline mirror::Class* MethodHelperT<HandleKind>::GetClassFromTypeIdx(uint16_t type_idx,
+                                                                     bool resolve) {
   mirror::ArtMethod* method = GetMethod();
   mirror::Class* type = method->GetDexCacheResolvedType(type_idx);
   if (type == nullptr && resolve) {
@@ -53,7 +57,8 @@
   return type;
 }
 
-inline mirror::Class* MethodHelper::GetReturnType(bool resolve) {
+template <template <class T> class HandleKind>
+inline mirror::Class* MethodHelperT<HandleKind>::GetReturnType(bool resolve) {
   mirror::ArtMethod* method = GetMethod();
   const DexFile* dex_file = method->GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex());
@@ -62,7 +67,8 @@
   return GetClassFromTypeIdx(return_type_idx, resolve);
 }
 
-inline mirror::String* MethodHelper::ResolveString(uint32_t string_idx) {
+template <template <class T> class HandleKind>
+inline mirror::String* MethodHelperT<HandleKind>::ResolveString(uint32_t string_idx) {
   mirror::ArtMethod* method = GetMethod();
   mirror::String* s = method->GetDexCacheStrings()->Get(string_idx);
   if (UNLIKELY(s == nullptr)) {
diff --git a/runtime/method_helper.cc b/runtime/method_helper.cc
index d6f83a8..79c2b91 100644
--- a/runtime/method_helper.cc
+++ b/runtime/method_helper.cc
@@ -25,7 +25,8 @@
 
 namespace art {
 
-mirror::String* MethodHelper::GetNameAsString(Thread* self) {
+template <template <class T> class HandleKind>
+mirror::String* MethodHelperT<HandleKind>::GetNameAsString(Thread* self) {
   const DexFile* dex_file = method_->GetDexFile();
   mirror::ArtMethod* method = method_->GetInterfaceMethodIfProxy();
   uint32_t dex_method_idx = method->GetDexMethodIndex();
@@ -36,7 +37,10 @@
                                                              dex_cache);
 }
 
-bool MethodHelper::HasSameSignatureWithDifferentClassLoaders(MethodHelper* other) {
+template <template <class T> class HandleKind>
+template <template <class T2> class HandleKind2>
+bool MethodHelperT<HandleKind>::HasSameSignatureWithDifferentClassLoaders(
+    MethodHelperT<HandleKind2>* other) {
   if (UNLIKELY(GetReturnType() != other->GetReturnType())) {
     return false;
   }
@@ -62,7 +66,8 @@
   return true;
 }
 
-uint32_t MethodHelper::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile)
+template <template <class T> class HandleKind>
+uint32_t MethodHelperT<HandleKind>::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::ArtMethod* method = GetMethod();
   const DexFile* dexfile = method->GetDexFile();
@@ -102,8 +107,9 @@
   return DexFile::kDexNoIndex;
 }
 
-uint32_t MethodHelper::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile,
-                                                        uint32_t name_and_signature_idx)
+template <template <typename> class HandleKind>
+uint32_t MethodHelperT<HandleKind>::FindDexMethodIndexInOtherDexFile(
+    const DexFile& other_dexfile, uint32_t name_and_signature_idx)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::ArtMethod* method = GetMethod();
   const DexFile* dexfile = method->GetDexFile();
@@ -133,4 +139,38 @@
   return DexFile::kDexNoIndex;
 }
 
+// Instantiate methods.
+template mirror::String* MethodHelperT<Handle>::GetNameAsString(Thread* self);
+
+template mirror::String* MethodHelperT<MutableHandle>::GetNameAsString(Thread* self);
+
+template
+uint32_t MethodHelperT<Handle>::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile);
+template
+uint32_t MethodHelperT<MutableHandle>::FindDexMethodIndexInOtherDexFile(
+    const DexFile& other_dexfile);
+
+template
+uint32_t MethodHelperT<Handle>::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile,
+                                                                 uint32_t name_and_signature_idx);
+template
+uint32_t MethodHelperT<MutableHandle>::FindDexMethodIndexInOtherDexFile(
+    const DexFile& other_dexfile, uint32_t name_and_signature_idx);
+
+template
+bool MethodHelperT<Handle>::HasSameSignatureWithDifferentClassLoaders<Handle>(
+    MethodHelperT<Handle>* other);
+
+template
+bool MethodHelperT<Handle>::HasSameSignatureWithDifferentClassLoaders<MutableHandle>(
+    MethodHelperT<MutableHandle>* other);
+
+template
+bool MethodHelperT<MutableHandle>::HasSameSignatureWithDifferentClassLoaders<Handle>(
+    MethodHelperT<Handle>* other);
+
+template
+bool MethodHelperT<MutableHandle>::HasSameSignatureWithDifferentClassLoaders<MutableHandle>(
+    MethodHelperT<MutableHandle>* other);
+
 }  // namespace art
diff --git a/runtime/method_helper.h b/runtime/method_helper.h
index f71d273..fe364d3 100644
--- a/runtime/method_helper.h
+++ b/runtime/method_helper.h
@@ -24,23 +24,22 @@
 
 namespace art {
 
-class MethodHelper {
+template <template <class T> class HandleKind>
+class MethodHelperT {
  public:
-  explicit MethodHelper(Handle<mirror::ArtMethod> m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : method_(m), shorty_(nullptr), shorty_len_(0) {
-    SetMethod(m.Get());
-  }
-
-  void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK(new_m != nullptr);
-    SetMethod(new_m);
-    shorty_ = nullptr;
+  explicit MethodHelperT(HandleKind<mirror::ArtMethod> m)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : method_(m), shorty_(nullptr), shorty_len_(0) {
   }
 
   mirror::ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return method_->GetInterfaceMethodIfProxy();
   }
 
+  // GetMethod() != Get() for proxy methods.
+  mirror::ArtMethod* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return method_.Get();
+  }
+
   mirror::String* GetNameAsString(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -105,10 +104,12 @@
     return GetParamPrimitiveType(param) == Primitive::kPrimNot;
   }
 
-  ALWAYS_INLINE bool HasSameNameAndSignature(MethodHelper* other)
+  template <template <class T> class HandleKind2>
+  ALWAYS_INLINE bool HasSameNameAndSignature(MethodHelperT<HandleKind2>* other)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool HasSameSignatureWithDifferentClassLoaders(MethodHelper* other)
+  template <template <class T> class HandleKind2>
+  bool HasSameSignatureWithDifferentClassLoaders(MethodHelperT<HandleKind2>* other)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   mirror::Class* GetClassFromTypeIdx(uint16_t type_idx, bool resolve = true)
@@ -125,6 +126,33 @@
                                             uint32_t name_and_signature_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+ protected:
+  HandleKind<mirror::ArtMethod> method_;
+
+  const char* shorty_;
+  uint32_t shorty_len_;
+
+ private:
+  template <template <class T2> class HandleKind2> friend class MethodHelperT;
+
+  DISALLOW_COPY_AND_ASSIGN(MethodHelperT);
+};
+
+class MethodHelper : public MethodHelperT<Handle> {
+  using MethodHelperT<Handle>::MethodHelperT;
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MethodHelper);
+};
+
+class MutableMethodHelper : public MethodHelperT<MutableHandle> {
+  using MethodHelperT<MutableHandle>::MethodHelperT;
+ public:
+  void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK(new_m != nullptr);
+    SetMethod(new_m);
+    shorty_ = nullptr;
+  }
+
  private:
   // Set the method_ field, for proxy methods looking up the interface method via the resolved
   // methods table.
@@ -132,11 +160,7 @@
     method_.Assign(method);
   }
 
-  Handle<mirror::ArtMethod> method_;
-  const char* shorty_;
-  uint32_t shorty_len_;
-
-  DISALLOW_COPY_AND_ASSIGN(MethodHelper);
+  DISALLOW_COPY_AND_ASSIGN(MutableMethodHelper);
 };
 
 }  // namespace art
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 2c0ea36..6582226 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -29,19 +29,19 @@
 
 inline uint32_t Array::ClassSize() {
   uint32_t vtable_entries = Object::kVTableLength;
-  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
 }
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline size_t Array::SizeOf() {
   // This is safe from overflow because the array was already allocated, so we know it's sane.
-  size_t component_size =
-      GetClass<kVerifyFlags, kReadBarrierOption>()->template GetComponentSize<kReadBarrierOption>();
+  size_t component_size_shift = GetClass<kVerifyFlags, kReadBarrierOption>()->
+      template GetComponentSizeShift<kReadBarrierOption>();
   // Don't need to check this since we already check this in GetClass.
   int32_t component_count =
       GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
-  size_t header_size = DataOffset(component_size).SizeValue();
-  size_t data_size = component_count * component_size;
+  size_t header_size = DataOffset(1U << component_size_shift).SizeValue();
+  size_t data_size = component_count << component_size_shift;
   return header_size + data_size;
 }
 
@@ -56,24 +56,36 @@
 }
 
 static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t component_count,
-                                      size_t component_size)
+                                      size_t component_size_shift)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK(array_class != NULL);
   DCHECK_GE(component_count, 0);
   DCHECK(array_class->IsArrayClass());
 
+  size_t component_size = 1U << component_size_shift;
   size_t header_size = Array::DataOffset(component_size).SizeValue();
-  size_t data_size = component_count * component_size;
+  size_t data_size = static_cast<size_t>(component_count) << component_size_shift;
   size_t size = header_size + data_size;
 
-  // Check for overflow and throw OutOfMemoryError if this was an unreasonable request.
-  size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size);
-  if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) {
+  // Check for size_t overflow and throw OutOfMemoryError if this was
+  // an unreasonable request.
+#ifdef __LP64__
+  // 64-bit. No overflow as component_count is 32-bit and the maximum
+  // component size is 8.
+  DCHECK_LE((1U << component_size_shift), 8U);
+#else
+  // 32-bit.
+  DCHECK_NE(header_size, 0U);
+  DCHECK_EQ(RoundUp(header_size, component_size), header_size);
+  // The array length limit (exclusive).
+  const size_t length_limit = (0U - header_size) >> component_size_shift;
+  if (UNLIKELY(length_limit <= static_cast<size_t>(component_count))) {
     self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
                                              PrettyDescriptor(array_class).c_str(),
                                              component_count).c_str());
     return 0;  // failure
   }
+#endif
   return size;
 }
 
@@ -103,8 +115,10 @@
 // array.
 class SetLengthToUsableSizeVisitor {
  public:
-  SetLengthToUsableSizeVisitor(int32_t min_length, size_t header_size, size_t component_size) :
-      minimum_length_(min_length), header_size_(header_size), component_size_(component_size) {
+  SetLengthToUsableSizeVisitor(int32_t min_length, size_t header_size,
+                               size_t component_size_shift) :
+      minimum_length_(min_length), header_size_(header_size),
+      component_size_shift_(component_size_shift) {
   }
 
   void operator()(Object* obj, size_t usable_size) const
@@ -112,10 +126,12 @@
     // Avoid AsArray as object is not yet in live bitmap or allocation stack.
     Array* array = down_cast<Array*>(obj);
     // DCHECK(array->IsArrayInstance());
-    int32_t length = (usable_size - header_size_) / component_size_;
+    int32_t length = (usable_size - header_size_) >> component_size_shift_;
     DCHECK_GE(length, minimum_length_);
-    byte* old_end = reinterpret_cast<byte*>(array->GetRawData(component_size_, minimum_length_));
-    byte* new_end = reinterpret_cast<byte*>(array->GetRawData(component_size_, length));
+    byte* old_end = reinterpret_cast<byte*>(array->GetRawData(1U << component_size_shift_,
+                                                              minimum_length_));
+    byte* new_end = reinterpret_cast<byte*>(array->GetRawData(1U << component_size_shift_,
+                                                              length));
     // Ensure space beyond original allocation is zeroed.
     memset(old_end, 0, new_end - old_end);
     array->SetLength(length);
@@ -124,38 +140,46 @@
  private:
   const int32_t minimum_length_;
   const size_t header_size_;
-  const size_t component_size_;
+  const size_t component_size_shift_;
 
   DISALLOW_COPY_AND_ASSIGN(SetLengthToUsableSizeVisitor);
 };
 
-template <bool kIsInstrumented>
+template <bool kIsInstrumented, bool kFillUsable>
 inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
-                           size_t component_size, gc::AllocatorType allocator_type,
-                           bool fill_usable) {
+                           size_t component_size_shift, gc::AllocatorType allocator_type) {
   DCHECK(allocator_type != gc::kAllocatorTypeLOS);
-  size_t size = ComputeArraySize(self, array_class, component_count, component_size);
+  DCHECK_EQ(array_class->GetComponentSizeShift(), component_size_shift);
+  DCHECK_EQ(array_class->GetComponentSize(), (1U << component_size_shift));
+  size_t size = ComputeArraySize(self, array_class, component_count, component_size_shift);
+#ifdef __LP64__
+  // 64-bit. No size_t overflow.
+  DCHECK_NE(size, 0U);
+#else
+  // 32-bit.
   if (UNLIKELY(size == 0)) {
     return nullptr;
   }
+#endif
   gc::Heap* heap = Runtime::Current()->GetHeap();
   Array* result;
-  if (!fill_usable) {
+  if (!kFillUsable) {
     SetLengthVisitor visitor(component_count);
     result = down_cast<Array*>(
         heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
                                                               allocator_type, visitor));
   } else {
-    SetLengthToUsableSizeVisitor visitor(component_count, DataOffset(component_size).SizeValue(),
-                                         component_size);
+    SetLengthToUsableSizeVisitor visitor(component_count,
+                                         DataOffset(1U << component_size_shift).SizeValue(),
+                                         component_size_shift);
     result = down_cast<Array*>(
         heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
                                                               allocator_type, visitor));
   }
   if (kIsDebugBuild && result != nullptr && Runtime::Current()->IsStarted()) {
     array_class = result->GetClass();  // In case the array class moved.
-    CHECK_EQ(array_class->GetComponentSize(), component_size);
-    if (!fill_usable) {
+    CHECK_EQ(array_class->GetComponentSize(), 1U << component_size_shift);
+    if (!kFillUsable) {
       CHECK_EQ(result->SizeOf(), size);
     } else {
       CHECK_GE(result->SizeOf(), size);
@@ -173,7 +197,8 @@
 
 template<typename T>
 inline PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) {
-  Array* raw_array = Array::Alloc<true>(self, GetArrayClass(), length, sizeof(T),
+  Array* raw_array = Array::Alloc<true>(self, GetArrayClass(), length,
+                                        ComponentSizeShiftWidth<sizeof(T)>(),
                                         Runtime::Current()->GetHeap()->GetCurrentAllocator());
   return down_cast<PrimitiveArray<T>*>(raw_array);
 }
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index f54af85..636be33 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -48,7 +48,8 @@
   StackHandleScope<1> hs(self);
   Handle<Array> new_array(
       hs.NewHandle(
-          Array::Alloc<true>(self, array_class.Get(), array_length, array_class->GetComponentSize(),
+          Array::Alloc<true>(self, array_class.Get(), array_length,
+                             array_class->GetComponentSizeShift(),
                              Runtime::Current()->GetHeap()->GetCurrentAllocator())));
   if (UNLIKELY(new_array.Get() == nullptr)) {
     CHECK(self->IsExceptionPending());
@@ -94,7 +95,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   mirror::Class* element_class_ptr = element_class.Get();
   StackHandleScope<1> hs(self);
-  Handle<mirror::Class> array_class(
+  MutableHandle<mirror::Class> array_class(
       hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr)));
   if (UNLIKELY(array_class.Get() == nullptr)) {
     CHECK(self->IsExceptionPending());
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7af88d6..521d7e7 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -33,13 +33,12 @@
   // The size of a java.lang.Class representing an array.
   static uint32_t ClassSize();
 
-  // Allocates an array with the given properties, if fill_usable is true the array will be of at
+  // Allocates an array with the given properties, if kFillUsable is true the array will be of at
   // least component_count size, however, if there's usable space at the end of the allocation the
   // array will fill it.
-  template <bool kIsInstrumented>
-  static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
-                      size_t component_size, gc::AllocatorType allocator_type,
-                      bool fill_usable = false)
+  template <bool kIsInstrumented, bool kFillUsable = false>
+  ALWAYS_INLINE static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
+                                    size_t component_size_shift, gc::AllocatorType allocator_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static Array* CreateMultiArray(Thread* self, Handle<Class> element_class,
@@ -66,12 +65,11 @@
   }
 
   static MemberOffset DataOffset(size_t component_size) {
-    if (component_size != sizeof(int64_t)) {
-      return OFFSET_OF_OBJECT_MEMBER(Array, first_element_);
-    } else {
-      // Align longs and doubles.
-      return MemberOffset(OFFSETOF_MEMBER(Array, first_element_) + 4);
-    }
+    DCHECK(IsPowerOfTwo(component_size)) << component_size;
+    size_t data_offset = RoundUp(OFFSETOF_MEMBER(Array, first_element_), component_size);
+    DCHECK_EQ(RoundUp(data_offset, component_size), data_offset)
+        << "Array data offset isn't aligned with component size";
+    return MemberOffset(data_offset);
   }
 
   void* GetRawData(size_t component_size, int32_t index)
diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h
index 336d9ee..03425cc 100644
--- a/runtime/mirror/art_field-inl.h
+++ b/runtime/mirror/art_field-inl.h
@@ -25,6 +25,7 @@
 #include "jvalue.h"
 #include "object-inl.h"
 #include "primitive.h"
+#include "thread-inl.h"
 #include "scoped_thread_state_change.h"
 #include "well_known_classes.h"
 
@@ -33,7 +34,7 @@
 
 inline uint32_t ArtField::ClassSize() {
   uint32_t vtable_entries = Object::kVTableLength + 6;
-  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
 }
 
 inline Class* ArtField::GetDeclaringClass() {
@@ -122,50 +123,64 @@
   }
 }
 
-inline bool ArtField::GetBoolean(Object* object) {
-  DCHECK_EQ(Primitive::kPrimBoolean, GetTypeAsPrimitiveType()) << PrettyField(this);
-  return Get32(object);
+#define FIELD_GET(object, type) \
+  DCHECK_EQ(Primitive::kPrim ## type, GetTypeAsPrimitiveType()) << PrettyField(this); \
+  DCHECK(object != nullptr) << PrettyField(this); \
+  DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted()); \
+  if (UNLIKELY(IsVolatile())) { \
+    return object->GetField ## type ## Volatile(GetOffset()); \
+  } \
+  return object->GetField ## type(GetOffset());
+
+#define FIELD_SET(object, type, value) \
+  DCHECK_EQ(Primitive::kPrim ## type, GetTypeAsPrimitiveType()) << PrettyField(this); \
+  DCHECK(object != nullptr) << PrettyField(this); \
+  DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted()); \
+  if (UNLIKELY(IsVolatile())) { \
+    object->SetField ## type ## Volatile<kTransactionActive>(GetOffset(), value); \
+  } else { \
+    object->SetField ## type<kTransactionActive>(GetOffset(), value); \
+  }
+
+inline uint8_t ArtField::GetBoolean(Object* object) {
+  FIELD_GET(object, Boolean);
 }
 
 template<bool kTransactionActive>
-inline void ArtField::SetBoolean(Object* object, bool z) {
-  DCHECK_EQ(Primitive::kPrimBoolean, GetTypeAsPrimitiveType()) << PrettyField(this);
-  Set32<kTransactionActive>(object, z);
+inline void ArtField::SetBoolean(Object* object, uint8_t z) {
+  FIELD_SET(object, Boolean, z);
 }
 
 inline int8_t ArtField::GetByte(Object* object) {
-  DCHECK_EQ(Primitive::kPrimByte, GetTypeAsPrimitiveType()) << PrettyField(this);
-  return Get32(object);
+  FIELD_GET(object, Byte);
 }
 
 template<bool kTransactionActive>
 inline void ArtField::SetByte(Object* object, int8_t b) {
-  DCHECK_EQ(Primitive::kPrimByte, GetTypeAsPrimitiveType()) << PrettyField(this);
-  Set32<kTransactionActive>(object, b);
+  FIELD_SET(object, Byte, b);
 }
 
 inline uint16_t ArtField::GetChar(Object* object) {
-  DCHECK_EQ(Primitive::kPrimChar, GetTypeAsPrimitiveType()) << PrettyField(this);
-  return Get32(object);
+  FIELD_GET(object, Char);
 }
 
 template<bool kTransactionActive>
 inline void ArtField::SetChar(Object* object, uint16_t c) {
-  DCHECK_EQ(Primitive::kPrimChar, GetTypeAsPrimitiveType()) << PrettyField(this);
-  Set32<kTransactionActive>(object, c);
+  FIELD_SET(object, Char, c);
 }
 
 inline int16_t ArtField::GetShort(Object* object) {
-  DCHECK_EQ(Primitive::kPrimShort, GetTypeAsPrimitiveType()) << PrettyField(this);
-  return Get32(object);
+  FIELD_GET(object, Short);
 }
 
 template<bool kTransactionActive>
 inline void ArtField::SetShort(Object* object, int16_t s) {
-  DCHECK_EQ(Primitive::kPrimShort, GetTypeAsPrimitiveType()) << PrettyField(this);
-  Set32<kTransactionActive>(object, s);
+  FIELD_SET(object, Short, s);
 }
 
+#undef FIELD_GET
+#undef FIELD_SET
+
 inline int32_t ArtField::GetInt(Object* object) {
   if (kIsDebugBuild) {
     Primitive::Type type = GetTypeAsPrimitiveType();
@@ -275,7 +290,7 @@
 }
 
 inline size_t ArtField::FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return Primitive::FieldSize(GetTypeAsPrimitiveType());
+  return Primitive::ComponentSize(GetTypeAsPrimitiveType());
 }
 
 inline mirror::DexCache* ArtField::GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index 82d0a64..50299b6 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -95,9 +95,9 @@
   void SetOffset(MemberOffset num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // field access, null object for static fields
-  bool GetBoolean(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  uint8_t GetBoolean(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   template<bool kTransactionActive>
-  void SetBoolean(Object* object, bool z) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetBoolean(Object* object, uint8_t z) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   int8_t GetByte(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   template<bool kTransactionActive>
   void SetByte(Object* object, int8_t b) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 9782dde..8447616 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -38,7 +38,7 @@
 
 inline uint32_t ArtMethod::ClassSize() {
   uint32_t vtable_entries = Object::kVTableLength + 8;
-  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
 }
 
 template<ReadBarrierOption kReadBarrierOption>
@@ -207,25 +207,20 @@
   return PointerToLowMemUInt32(GetEntryPointFromQuickCompiledCode());
 }
 
-
-#if defined(ART_USE_PORTABLE_COMPILER)
 inline uint32_t ArtMethod::GetPortableOatCodeOffset() {
   DCHECK(!Runtime::Current()->IsStarted());
   return PointerToLowMemUInt32(GetEntryPointFromPortableCompiledCode());
 }
-#endif
 
 inline void ArtMethod::SetQuickOatCodeOffset(uint32_t code_offset) {
   DCHECK(!Runtime::Current()->IsStarted());
   SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(code_offset));
 }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
 inline void ArtMethod::SetPortableOatCodeOffset(uint32_t code_offset) {
   DCHECK(!Runtime::Current()->IsStarted());
   SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset));
 }
-#endif
 
 inline const void* ArtMethod::GetQuickOatEntryPoint() {
   if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
@@ -276,6 +271,9 @@
 }
 
 inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) {
+  if (IsOptimized()) {
+    LOG(FATAL) << "Unimplemented vmap table for optimized compiler";
+  }
   DCHECK(code_pointer != nullptr);
   DCHECK(code_pointer == GetQuickOatCodePointer());
   uint32_t offset =
@@ -286,6 +284,20 @@
   return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
 }
 
+inline StackMap ArtMethod::GetStackMap(uint32_t native_pc_offset) {
+  return GetOptimizedCodeInfo().GetStackMapForNativePcOffset(native_pc_offset);
+}
+
+inline CodeInfo ArtMethod::GetOptimizedCodeInfo() {
+  DCHECK(IsOptimized());
+  const void* code_pointer = GetQuickOatCodePointer();
+  DCHECK(code_pointer != nullptr);
+  uint32_t offset =
+      reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_;
+  const void* data = reinterpret_cast<const void*>(reinterpret_cast<const uint8_t*>(code_pointer) - offset);
+  return CodeInfo(data);
+}
+
 inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) {
   DCHECK(!Runtime::Current()->IsStarted());
   SetNativeGcMap(reinterpret_cast<uint8_t*>(gc_map_offset));
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 27499c2..787c767 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -105,9 +105,9 @@
 }
 
 size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
-  CHECK_LE(1, shorty.length());
+  CHECK_LE(1U, shorty.length());
   uint32_t num_registers = 0;
-  for (int i = 1; i < shorty.length(); ++i) {
+  for (size_t i = 1; i < shorty.length(); ++i) {
     char ch = shorty[i];
     if (ch == 'D' || ch == 'J') {
       num_registers += 2;
@@ -143,7 +143,7 @@
     } else {
       StackHandleScope<2> hs(Thread::Current());
       MethodHelper mh(hs.NewHandle(this));
-      MethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+      MutableMethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
       IfTable* iftable = GetDeclaringClass()->GetIfTable();
       for (size_t i = 0; i < iftable->Count() && result == NULL; i++) {
         Class* interface = iftable->GetInterface(i);
@@ -281,6 +281,19 @@
   return found_dex_pc;
 }
 
+bool ArtMethod::IsEntrypointInterpreter() {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this);
+  const void* oat_portable_code = class_linker->GetOatMethodPortableCodeFor(this);
+  if (!IsPortableCompiled()) {  // Quick.
+    return oat_quick_code == nullptr ||
+        oat_quick_code != GetEntryPointFromQuickCompiledCode();
+  } else {  // Portable.
+    return oat_portable_code == nullptr ||
+        oat_portable_code != GetEntryPointFromPortableCompiledCode();
+  }
+}
+
 void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
                        const char* shorty) {
   if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
@@ -310,22 +323,21 @@
   } else {
     const bool kLogInvocationStartAndReturn = false;
     bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
-#if defined(ART_USE_PORTABLE_COMPILER)
     bool have_portable_code = GetEntryPointFromPortableCompiledCode() != nullptr;
-#else
-    bool have_portable_code = false;
-#endif
     if (LIKELY(have_quick_code || have_portable_code)) {
       if (kLogInvocationStartAndReturn) {
         LOG(INFO) << StringPrintf("Invoking '%s' %s code=%p", PrettyMethod(this).c_str(),
                                   have_quick_code ? "quick" : "portable",
                                   have_quick_code ? GetEntryPointFromQuickCompiledCode()
-#if defined(ART_USE_PORTABLE_COMPILER)
                                                   : GetEntryPointFromPortableCompiledCode());
-#else
-                                                  : nullptr);
-#endif
       }
+
+      // Ensure that we won't be accidentally calling quick/portable compiled code when -Xint.
+      if (kIsDebugBuild && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()) {
+        CHECK(IsEntrypointInterpreter())
+            << "Don't call compiled code when -Xint " << PrettyMethod(this);
+      }
+
       if (!IsPortableCompiled()) {
 #ifdef __LP64__
         if (!IsStatic()) {
@@ -353,11 +365,7 @@
         LOG(INFO) << StringPrintf("Returned '%s' %s code=%p", PrettyMethod(this).c_str(),
                                   have_quick_code ? "quick" : "portable",
                                   have_quick_code ? GetEntryPointFromQuickCompiledCode()
-#if defined(ART_USE_PORTABLE_COMPILER)
                                                   : GetEntryPointFromPortableCompiledCode());
-#else
-                                                  : nullptr);
-#endif
       }
     } else {
       LOG(INFO) << "Not invoking '" << PrettyMethod(this) << "' code=null";
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index abfdd42..de6ec05 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -25,6 +25,7 @@
 #include "object_callbacks.h"
 #include "quick/quick_method_frame_info.h"
 #include "read_barrier_option.h"
+#include "stack_map.h"
 
 namespace art {
 
@@ -150,8 +151,17 @@
     SetAccessFlags(GetAccessFlags() | kAccPreverified);
   }
 
+  bool IsOptimized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Temporary solution for detecting if a method has been optimized: the compiler
+    // does not create a GC map. Instead, the vmap table contains the stack map
+    // (as in stack_map.h).
+    return (GetEntryPointFromQuickCompiledCode() != nullptr)
+        && (GetQuickOatCodePointer() != nullptr)
+        && (GetNativeGcMap() == nullptr);
+  }
+
   bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return kUsePortableCompiler && ((GetAccessFlags() & kAccPortableCompiled) != 0);
+    return (GetAccessFlags() & kAccPortableCompiled) != 0;
   }
 
   void SetIsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -257,7 +267,6 @@
         entry_point_from_interpreter);
   }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
   static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_portable_compiled_code_));
   }
@@ -274,7 +283,6 @@
     SetFieldPtr<false, true, kVerifyFlags>(
         EntryPointFromPortableCompiledCodeOffset(), entry_point_from_portable_compiled_code);
   }
-#endif
 
   static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_quick_compiled_code_));
@@ -311,12 +319,15 @@
 
   void AssertPcIsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-#if defined(ART_USE_PORTABLE_COMPILER)
-  uint32_t GetPortableOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetPortableOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#endif
+  // Returns true if the entrypoint points to the interpreter, as
+  // opposed to the compiled code, that is, this method will be
+  // interpretered on invocation.
+  bool IsEntrypointInterpreter() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   uint32_t GetQuickOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  uint32_t GetPortableOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void SetQuickOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetPortableOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static const void* EntryPointToCodePointer(const void* entry_point) ALWAYS_INLINE {
     uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
@@ -339,6 +350,9 @@
   const uint8_t* GetVmapTable(const void* code_pointer)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  StackMap GetStackMap(uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  CodeInfo GetOptimizedCodeInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_));
   }
@@ -502,9 +516,7 @@
 
   // Method dispatch from portable compiled code invokes this pointer which may cause bridging into
   // quick compiled code or the interpreter.
-#if defined(ART_USE_PORTABLE_COMPILER)
   uint64_t entry_point_from_portable_compiled_code_;
-#endif
 
   // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
   // portable compiled code or the interpreter.
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 8e44471..3d3ae16 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -510,8 +510,19 @@
 template<VerifyObjectFlags kVerifyFlags>
 inline Primitive::Type Class::GetPrimitiveType() {
   DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
-  return static_cast<Primitive::Type>(
-      GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_)));
+  int32_t v32 = GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_));
+  Primitive::Type type = static_cast<Primitive::Type>(v32 & 0xFFFF);
+  DCHECK_EQ(static_cast<size_t>(v32 >> 16), Primitive::ComponentSizeShift(type));
+  return type;
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline size_t Class::GetPrimitiveTypeSizeShift() {
+  DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
+  int32_t v32 = GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_));
+  size_t size_shift = static_cast<Primitive::Type>(v32 >> 16);
+  DCHECK_EQ(size_shift, Primitive::ComponentSizeShift(static_cast<Primitive::Type>(v32 & 0xFFFF)));
+  return size_shift;
 }
 
 inline void Class::CheckObjectAlloc() {
@@ -556,6 +567,8 @@
 
 inline uint32_t Class::ComputeClassSize(bool has_embedded_tables,
                                         uint32_t num_vtable_entries,
+                                        uint32_t num_8bit_static_fields,
+                                        uint32_t num_16bit_static_fields,
                                         uint32_t num_32bit_static_fields,
                                         uint32_t num_64bit_static_fields,
                                         uint32_t num_ref_static_fields) {
@@ -569,18 +582,33 @@
             sizeof(int32_t) /* vtable len */ +
             embedded_vtable_size;
   }
+
   // Space used by reference statics.
   size +=  num_ref_static_fields * sizeof(HeapReference<Object>);
-  // Possible pad for alignment.
-  if (((size & 7) != 0) && (num_64bit_static_fields > 0)) {
-    size += sizeof(uint32_t);
-    if (num_32bit_static_fields != 0) {
-      // Shuffle one 32 bit static field forward.
-      num_32bit_static_fields--;
+  if (!IsAligned<8>(size) && num_64bit_static_fields > 0) {
+    uint32_t gap = 8 - (size & 0x7);
+    size += gap;  // will be padded
+    // Shuffle 4-byte fields forward.
+    while (gap >= sizeof(uint32_t) && num_32bit_static_fields != 0) {
+      --num_32bit_static_fields;
+      gap -= sizeof(uint32_t);
+    }
+    // Shuffle 2-byte fields forward.
+    while (gap >= sizeof(uint16_t) && num_16bit_static_fields != 0) {
+      --num_16bit_static_fields;
+      gap -= sizeof(uint16_t);
+    }
+    // Shuffle byte fields forward.
+    while (gap >= sizeof(uint8_t) && num_8bit_static_fields != 0) {
+      --num_8bit_static_fields;
+      gap -= sizeof(uint8_t);
     }
   }
+  // Guaranteed to be at least 4 byte aligned. No need for further alignments.
   // Space used for primitive static fields.
-  size += (num_32bit_static_fields * sizeof(uint32_t)) +
+  size += (num_8bit_static_fields * sizeof(uint8_t)) +
+      (num_16bit_static_fields * sizeof(uint16_t)) +
+      (num_32bit_static_fields * sizeof(uint32_t)) +
       (num_64bit_static_fields * sizeof(uint64_t));
   return size;
 }
@@ -705,11 +733,11 @@
 }
 
 inline bool Class::GetSlowPathEnabled() {
-  return GetField32(GetSlowPathFlagOffset());
+  return GetFieldBoolean(GetSlowPathFlagOffset());
 }
 
 inline void Class::SetSlowPath(bool enabled) {
-  SetField32<false>(GetSlowPathFlagOffset(), enabled);
+  SetFieldBoolean<false>(GetSlowPathFlagOffset(), enabled);
 }
 
 inline void Class::InitializeClassVisitor::operator()(
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 5b8eb82..3fcb188 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -292,21 +292,10 @@
                     new_reference_offsets);
 }
 
-void Class::SetReferenceStaticOffsets(uint32_t new_reference_offsets) {
-  if (new_reference_offsets != CLASS_WALK_SUPER) {
-    // Sanity check that the number of bits set in the reference offset bitmap
-    // agrees with the number of references
-    CHECK_EQ((size_t)POPCOUNT(new_reference_offsets),
-             NumReferenceStaticFieldsDuringLinking());
-  }
-  // Not called within a transaction.
-  SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
-                    new_reference_offsets);
-}
-
 bool Class::IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2) {
   size_t i = 0;
-  while (descriptor1[i] != '\0' && descriptor1[i] == descriptor2[i]) {
+  size_t min_length = std::min(descriptor1.size(), descriptor2.size());
+  while (i < min_length && descriptor1[i] == descriptor2[i]) {
     ++i;
   }
   if (descriptor1.find('/', i) != StringPiece::npos ||
@@ -772,7 +761,8 @@
   return GetInterfaceTypeList()->GetTypeItem(idx).type_idx_;
 }
 
-mirror::Class* Class::GetDirectInterface(Thread* self, Handle<mirror::Class> klass, uint32_t idx) {
+mirror::Class* Class::GetDirectInterface(Thread* self, Handle<mirror::Class> klass,
+                                         uint32_t idx) {
   DCHECK(klass.Get() != nullptr);
   DCHECK(!klass->IsPrimitive());
   if (klass->IsArrayClass()) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 43ac98d..0acf695 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -65,6 +65,8 @@
 namespace art {
 
 struct ClassOffsets;
+template<class T> class Handle;
+template<class T> class Handle;
 class Signature;
 class StringPiece;
 
@@ -96,6 +98,12 @@
 
   // Class Status
   //
+  // kStatusRetired: Class that's temporarily used till class linking time
+  // has its (vtable) size figured out and has been cloned to one with the
+  // right size which will be the one used later. The old one is retired and
+  // will be gc'ed once all refs to the class point to the newly
+  // cloned version.
+  //
   // kStatusNotReady: If a Class cannot be found in the class table by
   // FindClass, it allocates an new one with AllocClass in the
   // kStatusNotReady and calls LoadClass. Note if it does find a
@@ -131,7 +139,7 @@
   //
   // TODO: Explain the other states
   enum Status {
-    kStatusRetired = -2,
+    kStatusRetired = -2,  // Retired, should not be used. Use the newly cloned one instead.
     kStatusError = -1,
     kStatusNotReady = 0,
     kStatusIdx = 1,  // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
@@ -258,6 +266,16 @@
     return (GetAccessFlags() & kAccSynthetic) != 0;
   }
 
+  // Returns true if the class can avoid access checks.
+  bool IsPreverified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return (GetAccessFlags() & kAccPreverified) != 0;
+  }
+
+  void SetPreverified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+    SetAccessFlags(flags | kAccPreverified);
+  }
+
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsTypeOfReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags<kVerifyFlags>() & kAccClassIsReference) != 0;
@@ -327,9 +345,16 @@
 
   void SetPrimitiveType(Primitive::Type new_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
-    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type);
+    int32_t v32 = static_cast<int32_t>(new_type);
+    DCHECK_EQ(v32 & 0xFFFF, v32) << "upper 16 bits aren't zero";
+    // Store the component size shift in the upper 16 bits.
+    v32 |= Primitive::ComponentSizeShift(new_type) << 16;
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), v32);
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  size_t GetPrimitiveTypeSizeShift() ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Returns true if the class is a primitive type.
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitive() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -439,15 +464,25 @@
 
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   size_t GetComponentSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return Primitive::ComponentSize(
-        GetComponentType<kDefaultVerifyFlags, kReadBarrierOption>()->GetPrimitiveType());
+    return 1U << GetComponentSizeShift();
+  }
+
+  template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  size_t GetComponentSizeShift() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetComponentType<kDefaultVerifyFlags, kReadBarrierOption>()->GetPrimitiveTypeSizeShift();
   }
 
   bool IsObjectClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return !IsPrimitive() && GetSuperClass() == NULL;
   }
+
+  bool IsInstantiableNonArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return !IsPrimitive() && !IsInterface() && !IsAbstract() && !IsArrayClass();
+  }
+
   bool IsInstantiable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return (!IsPrimitive() && !IsInterface() && !IsAbstract()) || ((IsAbstract()) && IsArrayClass());
+    return (!IsPrimitive() && !IsInterface() && !IsAbstract()) ||
+        ((IsAbstract()) && IsArrayClass());
   }
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -491,6 +526,8 @@
   // Compute how many bytes would be used a class with the given elements.
   static uint32_t ComputeClassSize(bool has_embedded_tables,
                                    uint32_t num_vtable_entries,
+                                   uint32_t num_8bit_static_fields,
+                                   uint32_t num_16bit_static_fields,
                                    uint32_t num_32bit_static_fields,
                                    uint32_t num_64bit_static_fields,
                                    uint32_t num_ref_static_fields);
@@ -499,12 +536,12 @@
   static uint32_t ClassClassSize() {
     // The number of vtable entries in java.lang.Class.
     uint32_t vtable_entries = Object::kVTableLength + 64;
-    return ComputeClassSize(true, vtable_entries, 0, 1, 0);
+    return ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 0);
   }
 
   // The size of a java.lang.Class representing a primitive such as int.class.
   static uint32_t PrimitiveClassSize() {
-    return ComputeClassSize(false, 0, 0, 0, 0);
+    return ComputeClassSize(false, 0, 0, 0, 0, 0, 0);
   }
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -858,14 +895,6 @@
   // TODO: uint16_t
   void SetStaticField(uint32_t i, ArtField* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  uint32_t GetReferenceStaticOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_));
-  }
-
-  void SetReferenceStaticOffsets(uint32_t new_reference_offsets)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // Find a static or instance field using the JLS resolution order
   static ArtField* FindField(Thread* self, Handle<Class> klass, const StringPiece& name,
                              const StringPiece& type)
@@ -972,7 +1001,8 @@
 
   uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static mirror::Class* GetDirectInterface(Thread* self, Handle<mirror::Class> klass, uint32_t idx)
+  static mirror::Class* GetDirectInterface(Thread* self, Handle<mirror::Class> klass,
+                                           uint32_t idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   const char* GetSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -1130,15 +1160,13 @@
   // See also class_size_.
   uint32_t object_size_;
 
-  // Primitive type value, or Primitive::kPrimNot (0); set for generated primitive classes.
-  Primitive::Type primitive_type_;
+  // The lower 16 bits contains a Primitive::Type value. The upper 16
+  // bits contains the size shift of the primitive type.
+  uint32_t primitive_type_;
 
   // Bitmap of offsets of ifields.
   uint32_t reference_instance_offsets_;
 
-  // Bitmap of offsets of sfields.
-  uint32_t reference_static_offsets_;
-
   // State of class initialization.
   Status status_;
 
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index d3fcb55..288e88e 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -28,7 +28,7 @@
 
 inline uint32_t DexCache::ClassSize() {
   uint32_t vtable_entries = Object::kVTableLength + 1;
-  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
 }
 
 inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx)
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 9dbfb56..166ea9c 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -37,7 +37,7 @@
 
 inline uint32_t Object::ClassSize() {
   uint32_t vtable_entries = kVTableLength;
-  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+  return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
 }
 
 template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
@@ -408,17 +408,157 @@
 }
 
 template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline uint8_t Object::GetFieldBoolean(MemberOffset field_offset) {
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  return GetField<uint8_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline int8_t Object::GetFieldByte(MemberOffset field_offset) {
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  return GetField<int8_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline uint8_t Object::GetFieldBooleanVolatile(MemberOffset field_offset) {
+  return GetFieldBoolean<kVerifyFlags, true>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline int8_t Object::GetFieldByteVolatile(MemberOffset field_offset) {
+  return GetFieldByte<kVerifyFlags, true>(field_offset);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+    bool kIsVolatile>
+inline void Object::SetFieldBoolean(MemberOffset field_offset, uint8_t new_value)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteFieldBoolean(this, field_offset,
+                                           GetFieldBoolean<kVerifyFlags, kIsVolatile>(field_offset),
+                                           kIsVolatile);
+  }
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  SetField<uint8_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+    bool kIsVolatile>
+inline void Object::SetFieldByte(MemberOffset field_offset, int8_t new_value)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteFieldByte(this, field_offset,
+                                           GetFieldByte<kVerifyFlags, kIsVolatile>(field_offset),
+                                           kIsVolatile);
+  }
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  SetField<int8_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldBooleanVolatile(MemberOffset field_offset, uint8_t new_value) {
+  return SetFieldBoolean<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+      field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldByteVolatile(MemberOffset field_offset, int8_t new_value) {
+  return SetFieldByte<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+      field_offset, new_value);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline uint16_t Object::GetFieldChar(MemberOffset field_offset) {
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  return GetField<uint16_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline int16_t Object::GetFieldShort(MemberOffset field_offset) {
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  return GetField<int16_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline uint16_t Object::GetFieldCharVolatile(MemberOffset field_offset) {
+  return GetFieldChar<kVerifyFlags, true>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline int16_t Object::GetFieldShortVolatile(MemberOffset field_offset) {
+  return GetFieldShort<kVerifyFlags, true>(field_offset);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+    bool kIsVolatile>
+inline void Object::SetFieldChar(MemberOffset field_offset, uint16_t new_value) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteFieldChar(this, field_offset,
+                                           GetFieldChar<kVerifyFlags, kIsVolatile>(field_offset),
+                                           kIsVolatile);
+  }
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  SetField<uint16_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+    bool kIsVolatile>
+inline void Object::SetFieldShort(MemberOffset field_offset, int16_t new_value) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteFieldChar(this, field_offset,
+                                           GetFieldShort<kVerifyFlags, kIsVolatile>(field_offset),
+                                           kIsVolatile);
+  }
+  if (kVerifyFlags & kVerifyThis) {
+    VerifyObject(this);
+  }
+  SetField<int16_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldCharVolatile(MemberOffset field_offset, uint16_t new_value) {
+  return SetFieldChar<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+      field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldShortVolatile(MemberOffset field_offset, int16_t new_value) {
+  return SetFieldShort<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+      field_offset, new_value);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
 inline int32_t Object::GetField32(MemberOffset field_offset) {
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
-  const int32_t* word_addr = reinterpret_cast<const int32_t*>(raw_addr);
-  if (UNLIKELY(kIsVolatile)) {
-    return reinterpret_cast<const Atomic<int32_t>*>(word_addr)->LoadSequentiallyConsistent();
-  } else {
-    return reinterpret_cast<const Atomic<int32_t>*>(word_addr)->LoadJavaData();
-  }
+  return GetField<int32_t, kIsVolatile>(field_offset);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
@@ -440,13 +580,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
-  int32_t* word_addr = reinterpret_cast<int32_t*>(raw_addr);
-  if (kIsVolatile) {
-    reinterpret_cast<Atomic<int32_t>*>(word_addr)->StoreSequentiallyConsistent(new_value);
-  } else {
-    reinterpret_cast<Atomic<int32_t>*>(word_addr)->StoreJavaData(new_value);
-  }
+  SetField<int32_t, kIsVolatile>(field_offset, new_value);
 }
 
 template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
@@ -515,13 +649,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
-  const int64_t* addr = reinterpret_cast<const int64_t*>(raw_addr);
-  if (kIsVolatile) {
-    return reinterpret_cast<const Atomic<int64_t>*>(addr)->LoadSequentiallyConsistent();
-  } else {
-    return reinterpret_cast<const Atomic<int64_t>*>(addr)->LoadJavaData();
-  }
+  return GetField<int64_t, kIsVolatile>(field_offset);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
@@ -543,13 +671,7 @@
   if (kVerifyFlags & kVerifyThis) {
     VerifyObject(this);
   }
-  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
-  int64_t* addr = reinterpret_cast<int64_t*>(raw_addr);
-  if (kIsVolatile) {
-    reinterpret_cast<Atomic<int64_t>*>(addr)->StoreSequentiallyConsistent(new_value);
-  } else {
-    reinterpret_cast<Atomic<int64_t>*>(addr)->StoreJavaData(new_value);
-  }
+  SetField<int64_t, kIsVolatile>(field_offset, new_value);
 }
 
 template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
@@ -558,6 +680,28 @@
                                                                                new_value);
 }
 
+template<typename kSize, bool kIsVolatile>
+inline void Object::SetField(MemberOffset field_offset, kSize new_value) {
+  byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+  kSize* addr = reinterpret_cast<kSize*>(raw_addr);
+  if (kIsVolatile) {
+    reinterpret_cast<Atomic<kSize>*>(addr)->StoreSequentiallyConsistent(new_value);
+  } else {
+    reinterpret_cast<Atomic<kSize>*>(addr)->StoreJavaData(new_value);
+  }
+}
+
+template<typename kSize, bool kIsVolatile>
+inline kSize Object::GetField(MemberOffset field_offset) {
+  const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
+  const kSize* addr = reinterpret_cast<const kSize*>(raw_addr);
+  if (kIsVolatile) {
+    return reinterpret_cast<const Atomic<kSize>*>(addr)->LoadSequentiallyConsistent();
+  } else {
+    return reinterpret_cast<const Atomic<kSize>*>(addr)->LoadJavaData();
+  }
+}
+
 template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset,
                                                          int64_t old_value, int64_t new_value) {
@@ -744,9 +888,9 @@
 
 template<bool kVisitClass, bool kIsStatic, typename Visitor>
 inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) {
-  if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) {
+  if (!kIsStatic && LIKELY(ref_offsets != CLASS_WALK_SUPER)) {
     if (!kVisitClass) {
-     // Mask out the class from the reference offsets.
+      // Mask out the class from the reference offsets.
       ref_offsets ^= kWordHighBitMask;
     }
     DCHECK_EQ(ClassOffset().Uint32Value(), 0U);
@@ -758,7 +902,7 @@
       ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
     }
   } else {
-    // There is no reference offset bitmap.  In the non-static case, walk up the class
+    // There is no reference offset bitmap. In the non-static case, walk up the class
     // inheritance hierarchy and find reference offsets the hard way. In the static case, just
     // consider this class.
     for (mirror::Class* klass = kIsStatic ? AsClass() : GetClass(); klass != nullptr;
@@ -786,8 +930,7 @@
 template<bool kVisitClass, typename Visitor>
 inline void Object::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) {
   DCHECK(!klass->IsTemp());
-  klass->VisitFieldsReferences<kVisitClass, true>(
-      klass->GetReferenceStaticOffsets<kVerifyNone>(), visitor);
+  klass->VisitFieldsReferences<kVisitClass, true>(0, visitor);
 }
 
 template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor,
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index a1177d6..57069ab 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -187,8 +187,7 @@
       }
     }
   }
-  LOG(FATAL) << "Unreachable";
-  return 0;
+  UNREACHABLE();
 }
 
 void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index a6b6227..6cd230b 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -19,6 +19,7 @@
 
 #include "object_reference.h"
 #include "offsets.h"
+#include "runtime.h"
 #include "verify_object.h"
 
 namespace art {
@@ -247,6 +248,78 @@
   HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE uint8_t GetFieldBoolean(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE int8_t GetFieldByte(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE uint8_t GetFieldBooleanVolatile(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE int8_t GetFieldByteVolatile(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE void SetFieldBoolean(MemberOffset field_offset, uint8_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE void SetFieldByte(MemberOffset field_offset, int8_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE void SetFieldBooleanVolatile(MemberOffset field_offset, uint8_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE void SetFieldByteVolatile(MemberOffset field_offset, int8_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE uint16_t GetFieldChar(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE int16_t GetFieldShort(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE uint16_t GetFieldCharVolatile(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE int16_t GetFieldShortVolatile(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE void SetFieldChar(MemberOffset field_offset, uint16_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+  ALWAYS_INLINE void SetFieldShort(MemberOffset field_offset, int16_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE void SetFieldCharVolatile(MemberOffset field_offset, uint16_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  ALWAYS_INLINE void SetFieldShortVolatile(MemberOffset field_offset, int16_t new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
   ALWAYS_INLINE int32_t GetField32(MemberOffset field_offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -356,6 +429,13 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
+  template<typename kSize, bool kIsVolatile>
+  ALWAYS_INLINE void SetField(MemberOffset field_offset, kSize new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<typename kSize, bool kIsVolatile>
+  ALWAYS_INLINE kSize GetField(MemberOffset field_offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Verify the type correctness of stores to fields.
   // TODO: This can cause thread suspension and isn't moving GC safe.
   void CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value)
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index c7540dc..0ca44f8 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -35,10 +35,13 @@
 inline ObjectArray<T>* ObjectArray<T>::Alloc(Thread* self, Class* object_array_class,
                                              int32_t length, gc::AllocatorType allocator_type) {
   Array* array = Array::Alloc<true>(self, object_array_class, length,
-                                    sizeof(HeapReference<Object>), allocator_type);
+                                    ComponentSizeShiftWidth<sizeof(HeapReference<Object>)>(),
+                                    allocator_type);
   if (UNLIKELY(array == nullptr)) {
     return nullptr;
   } else {
+    DCHECK_EQ(array->GetClass()->GetComponentSizeShift(),
+              ComponentSizeShiftWidth<sizeof(HeapReference<Object>)>());
     return array->AsObjectArray<T>();
   }
 }
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index ede3b64..7fa664d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -74,7 +74,12 @@
   }
 };
 
-// Keep the assembly code in sync
+// Keep constants in sync.
+TEST_F(ObjectTest, Constants) {
+  EXPECT_EQ(kObjectReferenceSize, sizeof(mirror::HeapReference<mirror::Object>));
+}
+
+// Keep the assembly code constats in sync.
 TEST_F(ObjectTest, AsmConstants) {
   EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value());
   EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value());
@@ -90,9 +95,7 @@
   EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value());
 
   EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
-#if defined(ART_USE_PORTABLE_COMPILER)
   EXPECT_EQ(METHOD_PORTABLE_CODE_OFFSET, ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value());
-#endif
   EXPECT_EQ(METHOD_QUICK_CODE_OFFSET, ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
 }
 
@@ -158,20 +161,20 @@
   ScopedObjectAccess soa(Thread::Current());
   Class* c = class_linker_->FindSystemClass(soa.Self(), "[I");
   StackHandleScope<1> hs(soa.Self());
-  Handle<Array> a(
-      hs.NewHandle(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+  MutableHandle<Array> a(
+      hs.NewHandle(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
                                       Runtime::Current()->GetHeap()->GetCurrentAllocator())));
   EXPECT_TRUE(c == a->GetClass());
   EXPECT_EQ(1, a->GetLength());
 
   c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
-  a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+  a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
                               Runtime::Current()->GetHeap()->GetCurrentAllocator()));
   EXPECT_TRUE(c == a->GetClass());
   EXPECT_EQ(1, a->GetLength());
 
   c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
-  a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+  a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
                               Runtime::Current()->GetHeap()->GetCurrentAllocator()));
   EXPECT_TRUE(c == a->GetClass());
   EXPECT_EQ(1, a->GetLength());
@@ -181,27 +184,27 @@
   ScopedObjectAccess soa(Thread::Current());
   Class* c = class_linker_->FindSystemClass(soa.Self(), "[B");
   StackHandleScope<1> hs(soa.Self());
-  Handle<Array> a(
-      hs.NewHandle(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
-                                      Runtime::Current()->GetHeap()->GetCurrentAllocator(), true)));
+  MutableHandle<Array> a(
+      hs.NewHandle(Array::Alloc<true, true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
+                                            Runtime::Current()->GetHeap()->GetCurrentAllocator())));
   EXPECT_TRUE(c == a->GetClass());
   EXPECT_LE(1, a->GetLength());
 
   c = class_linker_->FindSystemClass(soa.Self(), "[I");
-  a.Assign(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
-                              Runtime::Current()->GetHeap()->GetCurrentAllocator(), true));
+  a.Assign(Array::Alloc<true, true>(soa.Self(), c, 2, c->GetComponentSizeShift(),
+                                    Runtime::Current()->GetHeap()->GetCurrentAllocator()));
   EXPECT_TRUE(c == a->GetClass());
   EXPECT_LE(2, a->GetLength());
 
   c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
-  a.Assign(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
-                              Runtime::Current()->GetHeap()->GetCurrentAllocator(), true));
+  a.Assign(Array::Alloc<true, true>(soa.Self(), c, 2, c->GetComponentSizeShift(),
+                                    Runtime::Current()->GetHeap()->GetCurrentAllocator()));
   EXPECT_TRUE(c == a->GetClass());
   EXPECT_LE(2, a->GetLength());
 
   c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
-  a.Assign(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
-                             Runtime::Current()->GetHeap()->GetCurrentAllocator(), true));
+  a.Assign(Array::Alloc<true, true>(soa.Self(), c, 2, c->GetComponentSizeShift(),
+                                    Runtime::Current()->GetHeap()->GetCurrentAllocator()));
   EXPECT_TRUE(c == a->GetClass());
   EXPECT_LE(2, a->GetLength());
 }
@@ -284,7 +287,7 @@
 
   StackHandleScope<2> hs(soa.Self());
   Handle<Class> c(hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "I")));
-  Handle<IntArray> dims(hs.NewHandle(IntArray::Alloc(soa.Self(), 1)));
+  MutableHandle<IntArray> dims(hs.NewHandle(IntArray::Alloc(soa.Self(), 1)));
   dims->Set<false>(0, 1);
   Array* multi = Array::CreateMultiArray(soa.Self(), c, dims);
   EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
@@ -482,8 +485,8 @@
   ArtMethod* m4_2 = klass2->GetVirtualMethod(3);
   EXPECT_STREQ(m4_2->GetName(), "m4");
 
-  MethodHelper mh(hs.NewHandle(m1_1));
-  MethodHelper mh2(hs.NewHandle(m1_2));
+  MutableMethodHelper mh(hs.NewHandle(m1_1));
+  MutableMethodHelper mh2(hs.NewHandle(m1_2));
   EXPECT_TRUE(mh.HasSameNameAndSignature(&mh2));
   EXPECT_TRUE(mh2.HasSameNameAndSignature(&mh));
 
diff --git a/runtime/mirror/reference-inl.h b/runtime/mirror/reference-inl.h
index b353402..d1d2a3a 100644
--- a/runtime/mirror/reference-inl.h
+++ b/runtime/mirror/reference-inl.h
@@ -24,7 +24,7 @@
 
 inline uint32_t Reference::ClassSize() {
   uint32_t vtable_entries = Object::kVTableLength + 5;
-  return Class::ComputeClassSize(false, vtable_entries, 2, 0, 0);
+  return Class::ComputeClassSize(false, vtable_entries, 2, 0, 0, 0, 0);
 }
 
 inline bool Reference::IsEnqueuable() {
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index d924141..14d7de2 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -30,7 +30,7 @@
 
 inline uint32_t String::ClassSize() {
   uint32_t vtable_entries = Object::kVTableLength + 51;
-  return Class::ComputeClassSize(true, vtable_entries, 1, 1, 2);
+  return Class::ComputeClassSize(true, vtable_entries, 0, 1, 0, 1, 2);
 }
 
 inline CharArray* String::GetCharArray() {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 03f8687..6123934 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -176,10 +176,6 @@
   // Deflated monitors have a null object.
 }
 
-/*
- * Links a thread into a monitor's wait set.  The monitor lock must be
- * held by the caller of this routine.
- */
 void Monitor::AppendToWaitSet(Thread* thread) {
   DCHECK(owner_ == Thread::Current());
   DCHECK(thread != NULL);
@@ -197,10 +193,6 @@
   t->SetWaitNext(thread);
 }
 
-/*
- * Unlinks a thread from a monitor's wait set.  The monitor lock must
- * be held by the caller of this routine.
- */
 void Monitor::RemoveFromWaitSet(Thread *thread) {
   DCHECK(owner_ == Thread::Current());
   DCHECK(thread != NULL);
@@ -395,29 +387,6 @@
   return true;
 }
 
-/*
- * Wait on a monitor until timeout, interrupt, or notification.  Used for
- * Object.wait() and (somewhat indirectly) Thread.sleep() and Thread.join().
- *
- * If another thread calls Thread.interrupt(), we throw InterruptedException
- * and return immediately if one of the following are true:
- *  - blocked in wait(), wait(long), or wait(long, int) methods of Object
- *  - blocked in join(), join(long), or join(long, int) methods of Thread
- *  - blocked in sleep(long), or sleep(long, int) methods of Thread
- * Otherwise, we set the "interrupted" flag.
- *
- * Checks to make sure that "ns" is in the range 0-999999
- * (i.e. fractions of a millisecond) and throws the appropriate
- * exception if it isn't.
- *
- * The spec allows "spurious wakeups", and recommends that all code using
- * Object.wait() do so in a loop.  This appears to derive from concerns
- * about pthread_cond_wait() on multiprocessor systems.  Some commentary
- * on the web casts doubt on whether these can/should occur.
- *
- * Since we're allowed to wake up "early", we clamp extremely long durations
- * to return at the end of the 32-bit time epoch.
- */
 void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,
                    bool interruptShouldThrow, ThreadState why) {
   DCHECK(self != NULL);
@@ -818,9 +787,6 @@
   }
 }
 
-/*
- * Object.wait().  Also called for class init.
- */
 void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns,
                    bool interruptShouldThrow, ThreadState why) {
   DCHECK(self != nullptr);
diff --git a/runtime/monitor.h b/runtime/monitor.h
index be9e6f9..8f97a40 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -75,6 +75,8 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DoNotify(self, obj, true);
   }
+
+  // Object.wait().  Also called for class init.
   static void Wait(Thread* self, mirror::Object* obj, int64_t ms, int32_t ns,
                    bool interruptShouldThrow, ThreadState why)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -139,15 +141,18 @@
       LOCKS_EXCLUDED(monitor_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Links a thread into a monitor's wait set.  The monitor lock must be held by the caller of this
+  // routine.
   void AppendToWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
+
+  // Unlinks a thread from a monitor's wait set.  The monitor lock must be held by the caller of
+  // this routine.
   void RemoveFromWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
 
-  /*
-   * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The
-   * calling thread must own the lock or the owner must be suspended. There's a race with other
-   * threads inflating the lock, installing hash codes and spurious failures. The caller should
-   * re-read the lock word following the call.
-   */
+  // Changes the shape of a monitor from thin to fat, preserving the internal lock state. The
+  // calling thread must own the lock or the owner must be suspended. There's a race with other
+  // threads inflating the lock, installing hash codes and spurious failures. The caller should
+  // re-read the lock word following the call.
   static void Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -178,6 +183,25 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 
+  // Wait on a monitor until timeout, interrupt, or notification.  Used for Object.wait() and
+  // (somewhat indirectly) Thread.sleep() and Thread.join().
+  //
+  // If another thread calls Thread.interrupt(), we throw InterruptedException and return
+  // immediately if one of the following are true:
+  //  - blocked in wait(), wait(long), or wait(long, int) methods of Object
+  //  - blocked in join(), join(long), or join(long, int) methods of Thread
+  //  - blocked in sleep(long), or sleep(long, int) methods of Thread
+  // Otherwise, we set the "interrupted" flag.
+  //
+  // Checks to make sure that "ns" is in the range 0-999999 (i.e. fractions of a millisecond) and
+  // throws the appropriate exception if it isn't.
+  //
+  // The spec allows "spurious wakeups", and recommends that all code using Object.wait() do so in
+  // a loop.  This appears to derive from concerns about pthread_cond_wait() on multiprocessor
+  // systems.  Some commentary on the web casts doubt on whether these can/should occur.
+  //
+  // Since we're allowed to wake up "early", we clamp extremely long durations to return at the end
+  // of the 32-bit time epoch.
   void Wait(Thread* self, int64_t msec, int32_t nsec, bool interruptShouldThrow, ThreadState why)
       LOCKS_EXCLUDED(monitor_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index af24368..704e041 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -58,7 +58,7 @@
 static const size_t kMaxHandles = 1000000;  // Use arbitrary large amount for now.
 static void FillHeap(Thread* self, ClassLinker* class_linker,
                      std::unique_ptr<StackHandleScope<kMaxHandles>>* hsp,
-                     std::vector<Handle<mirror::Object>>* handles)
+                     std::vector<MutableHandle<mirror::Object>>* handles)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Runtime::Current()->GetHeap()->SetIdealFootprint(1 * GB);
 
@@ -73,7 +73,7 @@
   // Start allocating with 128K
   size_t length = 128 * KB / 4;
   while (length > 10) {
-    Handle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>(
+    MutableHandle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>(
         mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4)));
     if (self->IsExceptionPending() || h.Get() == nullptr) {
       self->ClearException();
@@ -92,7 +92,7 @@
 
   // Allocate simple objects till it fails.
   while (!self->IsExceptionPending()) {
-    Handle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self));
+    MutableHandle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self));
     if (!self->IsExceptionPending() && h.Get() != nullptr) {
       handles->push_back(h);
     }
@@ -307,7 +307,7 @@
 
   // Fill the heap.
   std::unique_ptr<StackHandleScope<kMaxHandles>> hsp;
-  std::vector<Handle<mirror::Object>> handles;
+  std::vector<MutableHandle<mirror::Object>> handles;
   {
     Thread* self = Thread::Current();
     ScopedObjectAccess soa(self);
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 01c8978..65a7919 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -189,8 +189,8 @@
       StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ClassLoader> class_loader(
           hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
-      mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file,
-                                                        *dex_class_def);
+      mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(),
+                                                        class_loader, *dex_file, *dex_class_def);
       if (result != nullptr) {
         VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
         return soa.AddLocalReference<jclass>(result);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index fc1018a..23f46f4 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -76,7 +76,8 @@
   }
   gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator();
   mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
-                                                     array_class->GetComponentSize(), allocator);
+                                                     array_class->GetComponentSizeShift(),
+                                                     allocator);
   return soa.AddLocalReference<jobject>(result);
 }
 
@@ -99,9 +100,9 @@
     return nullptr;
   }
   gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
-  mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
-                                                     array_class->GetComponentSize(), allocator,
-                                                     true);
+  mirror::Array* result = mirror::Array::Alloc<true, true>(soa.Self(), array_class, length,
+                                                           array_class->GetComponentSizeShift(),
+                                                           allocator);
   return soa.AddLocalReference<jobject>(result);
 }
 
@@ -171,7 +172,7 @@
 }
 
 static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) {
-  return Runtime::Current()->GetJavaVM()->check_jni ? JNI_TRUE : JNI_FALSE;
+  return Runtime::Current()->GetJavaVM()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
 }
 
 static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) {
@@ -242,7 +243,7 @@
 }
 
 // Based on ClassLinker::ResolveType.
-static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t type_idx)
+static void PreloadDexCachesResolveType(Thread* self, mirror::DexCache* dex_cache, uint32_t type_idx)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
   if (klass != NULL) {
@@ -254,7 +255,7 @@
   if (class_name[1] == '\0') {
     klass = linker->FindPrimitiveClass(class_name[0]);
   } else {
-    klass = linker->LookupClass(class_name, NULL);
+    klass = linker->LookupClass(self, class_name, NULL);
   }
   if (klass == NULL) {
     return;
@@ -431,7 +432,6 @@
 
   Runtime* runtime = Runtime::Current();
   ClassLinker* linker = runtime->GetClassLinker();
-  Thread* self = ThreadForEnv(env);
 
   // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
   StringTable strings;
@@ -444,7 +444,7 @@
   for (size_t i = 0; i< boot_class_path.size(); i++) {
     const DexFile* dex_file = boot_class_path[i];
     CHECK(dex_file != NULL);
-    StackHandleScope<1> hs(self);
+    StackHandleScope<1> hs(soa.Self());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache(*dex_file)));
 
     if (kPreloadDexCachesStrings) {
@@ -455,7 +455,7 @@
 
     if (kPreloadDexCachesTypes) {
       for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-        PreloadDexCachesResolveType(dex_cache.Get(), i);
+        PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), i);
       }
     }
 
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a4ef839..e469126 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -18,6 +18,7 @@
 
 #include "debugger.h"
 #include "instruction_set.h"
+#include "java_vm_ext.h"
 #include "jni_internal.h"
 #include "JNIHelp.h"
 #include "ScopedUtfChars.h"
@@ -49,7 +50,7 @@
 }
 
 static void EnableDebugFeatures(uint32_t debug_flags) {
-  // Must match values in dalvik.system.Zygote.
+  // Must match values in com.android.internal.os.Zygote.
   enum {
     DEBUG_ENABLE_DEBUGGER           = 1,
     DEBUG_ENABLE_CHECKJNI           = 1 << 1,
@@ -61,7 +62,7 @@
   if ((debug_flags & DEBUG_ENABLE_CHECKJNI) != 0) {
     Runtime* runtime = Runtime::Current();
     JavaVMExt* vm = runtime->GetJavaVM();
-    if (!vm->check_jni) {
+    if (!vm->IsCheckJniEnabled()) {
       LOG(INFO) << "Late-enabling -Xcheck:jni";
       vm->SetCheckJniEnabled(true);
       // There's only one thread running at this point, so only one JNIEnv to fix up.
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 124bdf5..b11cbdf 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -78,7 +78,7 @@
     return nullptr;
   }
   if (initialize) {
-    class_linker->EnsureInitialized(c, true, true);
+    class_linker->EnsureInitialized(soa.Self(), c, true, true);
   }
   return soa.AddLocalReference<jclass>(c.Get());
 }
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index 51cd5b8..c1c6c26 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -15,6 +15,7 @@
  */
 
 #include "dex_file.h"
+#include "jni_internal.h"
 #include "mirror/dex_cache.h"
 #include "mirror/object-inl.h"
 #include "scoped_fast_native_object_access.h"
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index fb708a2..a85eec7 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -43,7 +43,10 @@
   exit(status);
 }
 
-static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) {
+static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader,
+                                  jstring javaLdLibraryPath) {
+  // TODO: returns NULL on success or an error message describing the failure on failure. This
+  // should be refactored in terms of suppressed exceptions.
   ScopedUtfChars filename(env, javaFilename);
   if (filename.c_str() == NULL) {
     return NULL;
@@ -64,14 +67,10 @@
     }
   }
 
-  std::string detail;
+  std::string error_msg;
   {
-    ScopedObjectAccess soa(env);
-    StackHandleScope<1> hs(soa.Self());
-    Handle<mirror::ClassLoader> classLoader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
     JavaVMExt* vm = Runtime::Current()->GetJavaVM();
-    bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, &detail);
+    bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, &error_msg);
     if (success) {
       return nullptr;
     }
@@ -79,7 +78,7 @@
 
   // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
   env->ExceptionClear();
-  return env->NewStringUTF(detail.c_str());
+  return env->NewStringUTF(error_msg.c_str());
 }
 
 static jlong Runtime_maxMemory(JNIEnv*, jclass) {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 761e800..f6a46bd 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -24,17 +24,18 @@
 
 namespace art {
 
-static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) {
+static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader,
+                                            jstring javaName) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
   ScopedUtfChars name(env, javaName);
-  if (name.c_str() == NULL) {
-    return NULL;
+  if (name.c_str() == nullptr) {
+    return nullptr;
   }
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   std::string descriptor(DotToDescriptor(name.c_str()));
-  mirror::Class* c = cl->LookupClass(descriptor.c_str(), loader);
-  if (c != NULL && c->IsResolved()) {
+  mirror::Class* c = cl->LookupClass(soa.Self(), descriptor.c_str(), loader);
+  if (c != nullptr && c->IsResolved()) {
     return soa.AddLocalReference<jclass>(c);
   }
   if (loader != nullptr) {
@@ -47,7 +48,7 @@
   }
   // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
   // the regular loadClass code.
-  return NULL;
+  return nullptr;
 }
 
 static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) {
@@ -67,13 +68,15 @@
  * with '/'); if it's not we'd need to make it absolute as part of forming
  * the URL string.
  */
-static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName, jint index) {
+static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName,
+                                                      jint index) {
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == nullptr) {
     return nullptr;
   }
 
-  const std::vector<const DexFile*>& path = Runtime::Current()->GetClassLinker()->GetBootClassPath();
+  const std::vector<const DexFile*>& path =
+      Runtime::Current()->GetClassLinker()->GetBootClassPath();
   if (index < 0 || size_t(index) >= path.size()) {
     return nullptr;
   }
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 058458f..763a664 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -59,9 +59,10 @@
     return NULL;
   }
   DCHECK(array_class->IsObjectArrayClass());
-  mirror::Array* new_array = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
-                                                        sizeof(mirror::HeapReference<mirror::Object>),
-                                                        runtime->GetHeap()->GetCurrentAllocator());
+  mirror::Array* new_array = mirror::Array::Alloc<true>(
+      soa.Self(), array_class, length,
+      ComponentSizeShiftWidth<sizeof(mirror::HeapReference<mirror::Object>)>(),
+      runtime->GetHeap()->GetCurrentAllocator());
   return soa.AddLocalReference<jobject>(new_array);
 }
 
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 34cb93a..0542aeb 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -48,7 +48,7 @@
     return nullptr;
   }
 
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), c, true, true)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 8850df2..d166be0 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -33,13 +33,24 @@
                                                    mirror::Object* obj)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (kIsSet && field->IsFinal()) {
-    ThrowIllegalAccessException(nullptr, StringPrintf("Cannot set final field: %s",
-                                                      PrettyField(field).c_str()).c_str());
+    ThrowIllegalAccessException(nullptr,
+            StringPrintf("Cannot set %s field %s of class %s",
+                PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(),
+                PrettyField(field).c_str(),
+                field->GetDeclaringClass() == nullptr ? "null" :
+                    PrettyClass(field->GetDeclaringClass()).c_str()).c_str());
     return false;
   }
-  if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags())) {
-    ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access field: %s",
-                                                      PrettyField(field).c_str()).c_str());
+  mirror::Class* calling_class = nullptr;
+  if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags(),
+                    &calling_class)) {
+    ThrowIllegalAccessException(nullptr,
+            StringPrintf("Class %s cannot access %s field %s of class %s",
+                calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
+                PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(),
+                PrettyField(field).c_str(),
+                field->GetDeclaringClass() == nullptr ? "null" :
+                    PrettyClass(field->GetDeclaringClass()).c_str()).c_str());
     return false;
   }
   return true;
@@ -104,7 +115,7 @@
       StackHandleScope<2> hs(soa.Self());
       HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(f));
       HandleWrapper<mirror::Class> h_klass(hs.NewHandleWrapper(&declaringClass));
-      if (UNLIKELY(!class_linker->EnsureInitialized(h_klass, true, true))) {
+      if (UNLIKELY(!class_linker->EnsureInitialized(soa.Self(), h_klass, true, true))) {
         DCHECK(soa.Self()->IsExceptionPending());
         return false;
       }
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index 163ae20..8b2aecb 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -16,6 +16,7 @@
 
 #include "base/logging.h"
 #include "debugger.h"
+#include "jni_internal.h"
 #include "scoped_fast_native_object_access.h"
 #include "ScopedPrimitiveArray.h"
 
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index cc44615..bc191b4 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -132,4 +132,4 @@
   android::UnloadNativeBridge();
 }
 
-};  // namespace art
+}  // namespace art
diff --git a/runtime/native_bridge_art_interface.h b/runtime/native_bridge_art_interface.h
index 42f0ed2..026cd82 100644
--- a/runtime/native_bridge_art_interface.h
+++ b/runtime/native_bridge_art_interface.h
@@ -35,6 +35,6 @@
 
 void UnloadNativeBridge();
 
-};  // namespace art
+}  // namespace art
 
 #endif  // ART_RUNTIME_NATIVE_BRIDGE_ART_INTERFACE_H_
diff --git a/runtime/oat.cc b/runtime/oat.cc
index ede108c..6810d73 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -23,7 +23,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '9', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '4', '2', '\0' };
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
diff --git a/runtime/oat.h b/runtime/oat.h
index 6d5fefe..6a32e3e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -152,7 +152,7 @@
 enum OatClassType {
   kOatClassAllCompiled = 0,   // OatClass is followed by an OatMethodOffsets for each method.
   kOatClassSomeCompiled = 1,  // A bitmap of which OatMethodOffsets are present follows the OatClass.
-  kOatClassNoneCompiled = 2,  // All methods are interpretted so no OatMethodOffsets are necessary.
+  kOatClassNoneCompiled = 2,  // All methods are interpreted so no OatMethodOffsets are necessary.
   kOatClassMax = 3,
 };
 
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 025f87d..a896f3e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -481,15 +481,17 @@
   uint32_t bitmap_size = 0;
   const byte* bitmap_pointer = nullptr;
   const byte* methods_pointer = nullptr;
-  if (type == kOatClassSomeCompiled) {
-    bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(after_type_pointer));
-    bitmap_pointer = after_type_pointer + sizeof(bitmap_size);
-    CHECK_LE(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
-    methods_pointer = bitmap_pointer + bitmap_size;
-  } else {
-    methods_pointer = after_type_pointer;
+  if (type != kOatClassNoneCompiled) {
+    if (type == kOatClassSomeCompiled) {
+      bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(after_type_pointer));
+      bitmap_pointer = after_type_pointer + sizeof(bitmap_size);
+      CHECK_LE(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
+      methods_pointer = bitmap_pointer + bitmap_size;
+    } else {
+      methods_pointer = after_type_pointer;
+    }
+    CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();
   }
-  CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();
 
   return OatClass(oat_file_,
                   status,
@@ -507,22 +509,23 @@
                             const OatMethodOffsets* methods_pointer)
     : oat_file_(oat_file), status_(status), type_(type),
       bitmap_(bitmap_pointer), methods_pointer_(methods_pointer) {
-    CHECK(methods_pointer != nullptr);
     switch (type_) {
       case kOatClassAllCompiled: {
         CHECK_EQ(0U, bitmap_size);
         CHECK(bitmap_pointer == nullptr);
+        CHECK(methods_pointer != nullptr);
         break;
       }
       case kOatClassSomeCompiled: {
         CHECK_NE(0U, bitmap_size);
         CHECK(bitmap_pointer != nullptr);
+        CHECK(methods_pointer != nullptr);
         break;
       }
       case kOatClassNoneCompiled: {
         CHECK_EQ(0U, bitmap_size);
         CHECK(bitmap_pointer == nullptr);
-        methods_pointer_ = nullptr;
+        CHECK(methods_pointer_ == nullptr);
         break;
       }
       case kOatClassMax: {
@@ -580,23 +583,11 @@
   }
 }
 
-OatFile::OatMethod::OatMethod(const byte* base,
-                              const uint32_t code_offset,
-                              const uint32_t gc_map_offset)
-  : begin_(base),
-    code_offset_(code_offset),
-    native_gc_map_offset_(gc_map_offset) {
-}
-
-OatFile::OatMethod::~OatMethod() {}
-
 void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
   CHECK(method != NULL);
-#if defined(ART_USE_PORTABLE_COMPILER)
   method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
-#endif
   method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
-  method->SetNativeGcMap(GetNativeGcMap());
+  method->SetNativeGcMap(GetNativeGcMap());  // Used by native methods in work around JNI mode.
 }
 
 }  // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index e5cd6ec..b9d5702 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -147,14 +147,19 @@
     uint32_t GetVmapTableOffset() const;
     uint32_t GetVmapTableOffsetOffset() const;
 
-    ~OatMethod();
-
     // Create an OatMethod with offsets relative to the given base address
-    OatMethod(const byte* base,
-              const uint32_t code_offset,
-              const uint32_t gc_map_offset);
+    OatMethod(const byte* base, const uint32_t code_offset, const uint32_t gc_map_offset)
+      : begin_(base),
+        code_offset_(code_offset),
+        native_gc_map_offset_(gc_map_offset) {
+    }
+    ~OatMethod() {}
 
-    OatMethod() {}
+    // A representation of an invalid OatMethod, used when an OatMethod or OatClass can't be found.
+    // See ClassLinker::FindOatMethodFor.
+    static const OatMethod Invalid() {
+      return OatMethod(nullptr, -1, -1);
+    }
 
    private:
     template<class T>
@@ -165,10 +170,10 @@
       return reinterpret_cast<T>(begin_ + offset);
     }
 
-    const byte* begin_;
+    const byte* const begin_;
 
-    uint32_t code_offset_;
-    uint32_t native_gc_map_offset_;
+    const uint32_t code_offset_;
+    const uint32_t native_gc_map_offset_;
 
     friend class OatClass;
   };
@@ -199,7 +204,12 @@
     // is present. Note that most callers should use GetOatMethod.
     uint32_t GetOatMethodOffsetsOffset(uint32_t method_index) const;
 
-    OatClass() {}
+    // A representation of an invalid OatClass, used when an OatClass can't be found.
+    // See ClassLinker::FindOatClass.
+    static OatClass Invalid() {
+      return OatClass(nullptr, mirror::Class::kStatusError, kOatClassNoneCompiled, 0, nullptr,
+                      nullptr);
+    }
 
    private:
     OatClass(const OatFile* oat_file,
@@ -209,15 +219,15 @@
              const uint32_t* bitmap_pointer,
              const OatMethodOffsets* methods_pointer);
 
-    const OatFile* oat_file_;
+    const OatFile* const oat_file_;
 
-    mirror::Class::Status status_;
+    const mirror::Class::Status status_;
 
-    OatClassType type_;
+    const OatClassType type_;
 
-    const uint32_t* bitmap_;
+    const uint32_t* const bitmap_;
 
-    const OatMethodOffsets* methods_pointer_;
+    const OatMethodOffsets* const methods_pointer_;
 
     friend class OatDexFile;
   };
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index bb2ad44..d820026 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -30,6 +30,87 @@
 
 namespace art {
 
+ParsedOptions::ParsedOptions()
+    :
+    boot_class_path_(nullptr),
+    check_jni_(kIsDebugBuild),                      // -Xcheck:jni is off by default for regular
+                                                    // builds but on by default in debug builds.
+    force_copy_(false),
+    compiler_callbacks_(nullptr),
+    is_zygote_(false),
+    must_relocate_(kDefaultMustRelocate),
+    dex2oat_enabled_(true),
+    image_dex2oat_enabled_(true),
+    interpreter_only_(kPoisonHeapReferences),       // kPoisonHeapReferences currently works with
+                                                    // the interpreter only.
+                                                    // TODO: make it work with the compiler.
+    is_explicit_gc_disabled_(false),
+    use_tlab_(false),
+    verify_pre_gc_heap_(false),
+    verify_pre_sweeping_heap_(kIsDebugBuild),       // Pre sweeping is the one that usually fails
+                                                    // if the GC corrupted the heap.
+    verify_post_gc_heap_(false),
+    verify_pre_gc_rosalloc_(kIsDebugBuild),
+    verify_pre_sweeping_rosalloc_(false),
+    verify_post_gc_rosalloc_(false),
+    long_pause_log_threshold_(gc::Heap::kDefaultLongPauseLogThreshold),
+    long_gc_log_threshold_(gc::Heap::kDefaultLongGCLogThreshold),
+    dump_gc_performance_on_shutdown_(false),
+    ignore_max_footprint_(false),
+    heap_initial_size_(gc::Heap::kDefaultInitialSize),
+    heap_maximum_size_(gc::Heap::kDefaultMaximumSize),
+    heap_growth_limit_(0),                          // 0 means no growth limit.
+    heap_min_free_(gc::Heap::kDefaultMinFree),
+    heap_max_free_(gc::Heap::kDefaultMaxFree),
+    heap_non_moving_space_capacity_(gc::Heap::kDefaultNonMovingSpaceCapacity),
+    large_object_space_type_(gc::Heap::kDefaultLargeObjectSpaceType),
+    large_object_threshold_(gc::Heap::kDefaultLargeObjectThreshold),
+    heap_target_utilization_(gc::Heap::kDefaultTargetUtilization),
+    foreground_heap_growth_multiplier_(gc::Heap::kDefaultHeapGrowthMultiplier),
+    parallel_gc_threads_(1),
+    conc_gc_threads_(0),                            // Only the main GC thread, no workers.
+    collector_type_(                                // The default GC type is set in makefiles.
+#if ART_DEFAULT_GC_TYPE_IS_CMS
+        gc::kCollectorTypeCMS),
+#elif ART_DEFAULT_GC_TYPE_IS_SS
+        gc::kCollectorTypeSS),
+#elif ART_DEFAULT_GC_TYPE_IS_GSS
+    gc::kCollectorTypeGSS),
+#else
+    gc::kCollectorTypeCMS),
+#error "ART default GC type must be set"
+#endif
+    background_collector_type_(gc::kCollectorTypeNone),
+                                                    // If background_collector_type_ is
+                                                    // kCollectorTypeNone, it defaults to the
+                                                    // collector_type_ after parsing options. If
+                                                    // you set this to kCollectorTypeHSpaceCompact
+                                                    // then we will do an hspace compaction when
+                                                    // we transition to background instead of a
+                                                    // normal collector transition.
+    stack_size_(0),                                 // 0 means default.
+    max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
+    low_memory_mode_(false),
+    lock_profiling_threshold_(0),
+    method_trace_(false),
+    method_trace_file_("/data/method-trace-file.bin"),
+    method_trace_file_size_(10 * MB),
+    hook_is_sensitive_thread_(nullptr),
+    hook_vfprintf_(vfprintf),
+    hook_exit_(exit),
+    hook_abort_(nullptr),                           // We don't call abort(3) by default; see
+                                                    // Runtime::Abort.
+    profile_clock_source_(kDefaultTraceClockSource),
+    verify_(true),
+    image_isa_(kRuntimeISA),
+    use_homogeneous_space_compaction_for_oom_(false),  // If we are using homogeneous space
+                                                       // compaction then default background
+                                                       // compaction to off since homogeneous
+                                                       // space compactions when we transition
+                                                       // to not jank perceptible.
+    min_interval_homogeneous_space_compaction_by_oom_(MsToNs(100 * 1000))  // 100s.
+    {}
+
 ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized) {
   std::unique_ptr<ParsedOptions> parsed(new ParsedOptions());
   if (parsed->Parse(options, ignore_unrecognized)) {
@@ -175,82 +256,9 @@
   if (class_path_string != NULL) {
     class_path_string_ = class_path_string;
   }
-  // -Xcheck:jni is off by default for regular builds but on by default in debug builds.
-  check_jni_ = kIsDebugBuild;
 
-  heap_initial_size_ = gc::Heap::kDefaultInitialSize;
-  heap_maximum_size_ = gc::Heap::kDefaultMaximumSize;
-  heap_min_free_ = gc::Heap::kDefaultMinFree;
-  heap_max_free_ = gc::Heap::kDefaultMaxFree;
-  heap_non_moving_space_capacity_ = gc::Heap::kDefaultNonMovingSpaceCapacity;
-  heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization;
-  foreground_heap_growth_multiplier_ = gc::Heap::kDefaultHeapGrowthMultiplier;
-  heap_growth_limit_ = 0;  // 0 means no growth limit .
   // Default to number of processors minus one since the main GC thread also does work.
   parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
-  // Only the main GC thread, no workers.
-  conc_gc_threads_ = 0;
-  // The default GC type is set in makefiles.
-#if ART_DEFAULT_GC_TYPE_IS_CMS
-  collector_type_ = gc::kCollectorTypeCMS;
-#elif ART_DEFAULT_GC_TYPE_IS_SS
-  collector_type_ = gc::kCollectorTypeSS;
-#elif ART_DEFAULT_GC_TYPE_IS_GSS
-  collector_type_ = gc::kCollectorTypeGSS;
-#else
-#error "ART default GC type must be set"
-#endif
-  // If we are using homogeneous space compaction then default background compaction to off since
-  // homogeneous space compactions when we transition to not jank perceptible.
-  use_homogeneous_space_compaction_for_oom_ = false;
-  // If background_collector_type_ is kCollectorTypeNone, it defaults to the collector_type_ after
-  // parsing options. If you set this to kCollectorTypeHSpaceCompact then we will do an hspace
-  // compaction when we transition to background instead of a normal collector transition.
-  background_collector_type_ = gc::kCollectorTypeNone;
-#ifdef ART_USE_HSPACE_COMPACT
-  background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
-#endif
-#ifdef ART_USE_BACKGROUND_COMPACT
-  background_collector_type_ = gc::kCollectorTypeSS;
-#endif
-  stack_size_ = 0;  // 0 means default.
-  max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation;
-  low_memory_mode_ = false;
-  use_tlab_ = false;
-  min_interval_homogeneous_space_compaction_by_oom_ = MsToNs(100 * 1000);  // 100s.
-  verify_pre_gc_heap_ = false;
-  // Pre sweeping is the one that usually fails if the GC corrupted the heap.
-  verify_pre_sweeping_heap_ = kIsDebugBuild;
-  verify_post_gc_heap_ = false;
-  verify_pre_gc_rosalloc_ = kIsDebugBuild;
-  verify_pre_sweeping_rosalloc_ = false;
-  verify_post_gc_rosalloc_ = false;
-
-  compiler_callbacks_ = nullptr;
-  is_zygote_ = false;
-  must_relocate_ = kDefaultMustRelocate;
-  dex2oat_enabled_ = true;
-  image_dex2oat_enabled_ = true;
-  if (kPoisonHeapReferences) {
-    // kPoisonHeapReferences currently works only with the interpreter only.
-    // TODO: make it work with the compiler.
-    interpreter_only_ = true;
-  } else {
-    interpreter_only_ = false;
-  }
-  is_explicit_gc_disabled_ = false;
-
-  long_pause_log_threshold_ = gc::Heap::kDefaultLongPauseLogThreshold;
-  long_gc_log_threshold_ = gc::Heap::kDefaultLongGCLogThreshold;
-  dump_gc_performance_on_shutdown_ = false;
-  ignore_max_footprint_ = false;
-
-  lock_profiling_threshold_ = 0;
-  hook_is_sensitive_thread_ = NULL;
-
-  hook_vfprintf_ = vfprintf;
-  hook_exit_ = exit;
-  hook_abort_ = NULL;  // We don't call abort(3) by default; see Runtime::Abort.
 
 //  gLogVerbosity.class_linker = true;  // TODO: don't check this in!
 //  gLogVerbosity.compiler = true;  // TODO: don't check this in!
@@ -266,15 +274,6 @@
 //  gLogVerbosity.threads = true;  // TODO: don't check this in!
 //  gLogVerbosity.verifier = true;  // TODO: don't check this in!
 
-  method_trace_ = false;
-  method_trace_file_ = "/data/method-trace-file.bin";
-  method_trace_file_size_ = 10 * MB;
-
-  profile_clock_source_ = kDefaultTraceClockSource;
-
-  verify_ = true;
-  image_isa_ = kRuntimeISA;
-
   for (size_t i = 0; i < options.size(); ++i) {
     if (true && options[0].first == "-Xzygote") {
       LOG(INFO) << "option[" << i << "]=" << options[i].first;
@@ -309,6 +308,8 @@
       }
     } else if (StartsWith(option, "-Xcheck:jni")) {
       check_jni_ = true;
+    } else if (StartsWith(option, "-Xjniopts:forcecopy")) {
+      force_copy_ = true;
     } else if (StartsWith(option, "-Xrunjdwp:") || StartsWith(option, "-agentlib:jdwp=")) {
       std::string tail(option.substr(option[1] == 'X' ? 10 : 15));
       // TODO: move parsing logic out of Dbg
@@ -453,6 +454,32 @@
       if (!ParseXGcOption(option)) {
         return false;
       }
+    } else if (StartsWith(option, "-XX:LargeObjectSpace=")) {
+      std::string substring;
+      if (!ParseStringAfterChar(option, '=', &substring)) {
+        return false;
+      }
+      if (substring == "disabled") {
+        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeDisabled;
+      } else if (substring == "freelist") {
+        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeFreeList;
+      } else if (substring == "map") {
+        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeMap;
+      } else {
+        Usage("Unknown -XX:LargeObjectSpace= option %s\n", substring.c_str());
+        return false;
+      }
+    } else if (StartsWith(option, "-XX:LargeObjectThreshold=")) {
+      std::string substring;
+      if (!ParseStringAfterChar(option, '=', &substring)) {
+        return false;
+      }
+      size_t size = ParseMemoryOption(substring.c_str(), 1);
+      if (size == 0) {
+        Usage("Failed to parse memory option %s\n", option.c_str());
+        return false;
+      }
+      large_object_threshold_ = size;
     } else if (StartsWith(option, "-XX:BackgroundGC=")) {
       std::string substring;
       if (!ParseStringAfterChar(option, '=', &substring)) {
@@ -642,7 +669,6 @@
                StartsWith(option, "-Xint:") ||
                StartsWith(option, "-Xdexopt:") ||
                (option == "-Xnoquithandler") ||
-               StartsWith(option, "-Xjniopts:") ||
                StartsWith(option, "-Xjnigreflimit:") ||
                (option == "-Xgenregmap") ||
                (option == "-Xnogenregmap") ||
@@ -765,7 +791,6 @@
   UsageMessage(stream, "  -Xstacktracefile:<filename>\n");
   UsageMessage(stream, "  -Xgc:[no]preverify\n");
   UsageMessage(stream, "  -Xgc:[no]postverify\n");
-  UsageMessage(stream, "  -XX:+DisableExplicitGC\n");
   UsageMessage(stream, "  -XX:HeapGrowthLimit=N\n");
   UsageMessage(stream, "  -XX:HeapMinFree=N\n");
   UsageMessage(stream, "  -XX:HeapMaxFree=N\n");
@@ -782,6 +807,7 @@
   UsageMessage(stream, "  -Xgc:[no]postverify_rosalloc\n");
   UsageMessage(stream, "  -Xgc:[no]presweepingverify\n");
   UsageMessage(stream, "  -Ximage:filename\n");
+  UsageMessage(stream, "  -XX:+DisableExplicitGC\n");
   UsageMessage(stream, "  -XX:ParallelGCThreads=integervalue\n");
   UsageMessage(stream, "  -XX:ConcGCThreads=integervalue\n");
   UsageMessage(stream, "  -XX:MaxSpinsBeforeThinLockInflation=integervalue\n");
@@ -791,6 +817,8 @@
   UsageMessage(stream, "  -XX:IgnoreMaxFootprint\n");
   UsageMessage(stream, "  -XX:UseTLAB\n");
   UsageMessage(stream, "  -XX:BackgroundGC=none\n");
+  UsageMessage(stream, "  -XX:LargeObjectSpace={disabled,map,freelist}\n");
+  UsageMessage(stream, "  -XX:LargeObjectThreshold=N\n");
   UsageMessage(stream, "  -Xmethod-trace\n");
   UsageMessage(stream, "  -Xmethod-trace-file:filename");
   UsageMessage(stream, "  -Xmethod-trace-file-size:integervalue\n");
@@ -904,7 +932,7 @@
   }
   bool sane_val = true;
   double value;
-  if (false) {
+  if ((false)) {
     // TODO: this doesn't seem to work on the emulator.  b/15114595
     std::stringstream iss(substring);
     iss >> value;
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 86056c5..26a2f31 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -24,6 +24,7 @@
 
 #include "globals.h"
 #include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
 #include "instruction_set.h"
 #include "profiler_options.h"
 
@@ -44,6 +45,7 @@
   std::string class_path_string_;
   std::string image_;
   bool check_jni_;
+  bool force_copy_;
   std::string jni_trace_;
   std::string native_bridge_library_filename_;
   CompilerCallbacks* compiler_callbacks_;
@@ -71,6 +73,8 @@
   size_t heap_min_free_;
   size_t heap_max_free_;
   size_t heap_non_moving_space_capacity_;
+  gc::space::LargeObjectSpaceType large_object_space_type_;
+  size_t large_object_threshold_;
   double heap_target_utilization_;
   double foreground_heap_growth_multiplier_;
   unsigned int parallel_gc_threads_;
@@ -107,7 +111,7 @@
   uint64_t min_interval_homogeneous_space_compaction_by_oom_;
 
  private:
-  ParsedOptions() {}
+  ParsedOptions();
 
   void Usage(const char* fmt, ...);
   void UsageMessage(FILE* stream, const char* fmt, ...);
diff --git a/runtime/primitive.cc b/runtime/primitive.cc
index 16ca0fe..a639f93 100644
--- a/runtime/primitive.cc
+++ b/runtime/primitive.cc
@@ -30,6 +30,7 @@
   "PrimDouble",
   "PrimVoid",
 };
+
 std::ostream& operator<<(std::ostream& os, const Primitive::Type& type) {
   int32_t int_type = static_cast<int32_t>(type);
   if (type >= Primitive::kPrimNot && type <= Primitive::kPrimVoid) {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index b436bd2..afcc64d 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -21,12 +21,28 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "mirror/object_reference.h"
 
 namespace art {
-namespace mirror {
-class Object;
-}  // namespace mirror
+
+static constexpr size_t kObjectReferenceSize = 4;
+
+
+template<size_t kComponentSize>
+size_t ComponentSizeShiftWidth() {
+  switch (kComponentSize) {
+    case 1:
+      return 0U;
+    case 2:
+      return 1U;
+    case 4:
+      return 2U;
+    case 8:
+      return 3U;
+    default:
+      LOG(FATAL) << "Unexpected component size : " << kComponentSize;
+      return 0U;
+  }
+}
 
 class Primitive {
  public:
@@ -68,6 +84,24 @@
     }
   }
 
+  static size_t ComponentSizeShift(Type type) {
+    switch (type) {
+      case kPrimVoid:
+      case kPrimBoolean:
+      case kPrimByte:    return 0;
+      case kPrimChar:
+      case kPrimShort:   return 1;
+      case kPrimInt:
+      case kPrimFloat:   return 2;
+      case kPrimLong:
+      case kPrimDouble:  return 3;
+      case kPrimNot:     return ComponentSizeShiftWidth<kObjectReferenceSize>();
+      default:
+        LOG(FATAL) << "Invalid type " << static_cast<int>(type);
+        return 0;
+    }
+  }
+
   static size_t ComponentSize(Type type) {
     switch (type) {
       case kPrimVoid:    return 0;
@@ -79,17 +113,13 @@
       case kPrimFloat:   return 4;
       case kPrimLong:
       case kPrimDouble:  return 8;
-      case kPrimNot:     return sizeof(mirror::HeapReference<mirror::Object>);
+      case kPrimNot:     return kObjectReferenceSize;
       default:
         LOG(FATAL) << "Invalid type " << static_cast<int>(type);
         return 0;
     }
   }
 
-  static size_t FieldSize(Type type) {
-    return ComponentSize(type) <= 4 ? 4 : 8;
-  }
-
   static const char* Descriptor(Type type) {
     switch (type) {
       case kPrimBoolean:
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index a6a2475..cde4177 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -119,12 +119,12 @@
 }
 
 // A closure that is called by the thread checkpoint code.
-class SampleCheckpoint : public Closure {
+class SampleCheckpoint FINAL : public Closure {
  public:
   explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) :
     profiler_(profiler) {}
 
-  virtual void Run(Thread* thread) NO_THREAD_SAFETY_ANALYSIS {
+  void Run(Thread* thread) OVERRIDE {
     Thread* self = Thread::Current();
     if (thread == nullptr) {
       LOG(ERROR) << "Checkpoint with nullptr thread";
@@ -192,6 +192,7 @@
       VLOG(profiler) << "Delaying profile start for " << delay_secs << " secs";
       MutexLock mu(self, profiler->wait_lock_);
       profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0);
+      // We were either signaled by Stop or timedout, in either case ignore the timed out result.
 
       // Expand the backoff by its coefficient, but don't go beyond the max.
       backoff = std::min(backoff * profiler->options_.GetBackoffCoefficient(), kMaxBackoffSecs);
@@ -238,17 +239,13 @@
       // is done with a timeout so that we can detect problems with the checkpoint
       // running code.  We should never see this.
       const uint32_t kWaitTimeoutMs = 10000;
-      const uint32_t kWaitTimeoutUs = kWaitTimeoutMs * 1000;
 
-      uint64_t waitstart_us = MicroTime();
       // Wait for all threads to pass the barrier.
-      profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
-      uint64_t waitend_us = MicroTime();
-      uint64_t waitdiff_us = waitend_us - waitstart_us;
+      bool timed_out =  profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
 
       // We should never get a timeout.  If we do, it suggests a problem with the checkpoint
       // code.  Crash the process in this case.
-      CHECK_LT(waitdiff_us, kWaitTimeoutUs);
+      CHECK(!timed_out);
 
       // Update the current time.
       now_us = MicroTime();
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index d977ce9..1eded62 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -183,7 +183,8 @@
   ASSERT_TRUE(throwsFieldClass.Get() != nullptr);
 
   // Test "Class[] interfaces" field.
-  FieldHelper fh(hs.NewHandle(static_fields->Get(0)));
+  MutableHandle<mirror::ArtField> fhandle = hs.NewHandle(static_fields->Get(0));
+  FieldHelper fh(fhandle);
   EXPECT_EQ("interfaces", std::string(fh.GetField()->GetName()));
   EXPECT_EQ("[Ljava/lang/Class;", std::string(fh.GetField()->GetTypeDescriptor()));
   EXPECT_EQ(interfacesFieldClass.Get(), fh.GetType());
@@ -191,12 +192,13 @@
   EXPECT_FALSE(fh.GetField()->IsPrimitiveType());
 
   // Test "Class[][] throws" field.
-  fh.ChangeField(static_fields->Get(1));
-  EXPECT_EQ("throws", std::string(fh.GetField()->GetName()));
-  EXPECT_EQ("[[Ljava/lang/Class;", std::string(fh.GetField()->GetTypeDescriptor()));
-  EXPECT_EQ(throwsFieldClass.Get(), fh.GetType());
-  EXPECT_EQ("L$Proxy1234;", std::string(fh.GetDeclaringClassDescriptor()));
-  EXPECT_FALSE(fh.GetField()->IsPrimitiveType());
+  fhandle.Assign(static_fields->Get(1));
+  FieldHelper fh2(fhandle);
+  EXPECT_EQ("throws", std::string(fh2.GetField()->GetName()));
+  EXPECT_EQ("[[Ljava/lang/Class;", std::string(fh2.GetField()->GetTypeDescriptor()));
+  EXPECT_EQ(throwsFieldClass.Get(), fh2.GetType());
+  EXPECT_EQ("L$Proxy1234;", std::string(fh2.GetDeclaringClassDescriptor()));
+  EXPECT_FALSE(fh2.GetField()->IsPrimitiveType());
 }
 
 }  // namespace art
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 98eeda7..2c158ba 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -206,13 +206,14 @@
     const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
     uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
     ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
-    StackHandleScope<2> hs(self_);
+    StackHandleScope<3> hs(self_);
     mirror::Class* declaring_class = m->GetDeclaringClass();
     Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
     Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
-    verifier::MethodVerifier verifier(h_dex_cache->GetDexFile(), &h_dex_cache, &h_class_loader,
-                                      &m->GetClassDef(), code_item, m->GetDexMethodIndex(), m,
-                                      m->GetAccessFlags(), false, true, true);
+    Handle<mirror::ArtMethod> h_method(hs.NewHandle(m));
+    verifier::MethodVerifier verifier(self_, h_dex_cache->GetDexFile(), h_dex_cache, h_class_loader,
+                                      &m->GetClassDef(), code_item, m->GetDexMethodIndex(),
+                                      h_method, m->GetAccessFlags(), false, true, true);
     verifier.Verify();
     const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
     for (uint16_t reg = 0; reg < num_regs; ++reg) {
@@ -229,7 +230,7 @@
                                       reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
           break;
         case kLongLoVReg:
-          if (GetVRegKind(reg + 1, kinds), kLongHiVReg) {
+          if (GetVRegKind(reg + 1, kinds) == kLongHiVReg) {
             // Treat it as a "long" register pair.
             new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg));
           } else {
@@ -237,14 +238,14 @@
           }
           break;
         case kLongHiVReg:
-          if (GetVRegKind(reg - 1, kinds), kLongLoVReg) {
+          if (GetVRegKind(reg - 1, kinds) == kLongLoVReg) {
             // Nothing to do: we treated it as a "long" register pair.
           } else {
             new_frame->SetVReg(reg, GetVReg(m, reg, kind));
           }
           break;
         case kDoubleLoVReg:
-          if (GetVRegKind(reg + 1, kinds), kDoubleHiVReg) {
+          if (GetVRegKind(reg + 1, kinds) == kDoubleHiVReg) {
             // Treat it as a "double" register pair.
             new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg));
           } else {
@@ -252,7 +253,7 @@
           }
           break;
         case kDoubleHiVReg:
-          if (GetVRegKind(reg - 1, kinds), kDoubleLoVReg) {
+          if (GetVRegKind(reg - 1, kinds) == kDoubleLoVReg) {
             // Nothing to do: we treated it as a "double" register pair.
           } else {
             new_frame->SetVReg(reg, GetVReg(m, reg, kind));
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 70aba9b..01c5070 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -24,6 +24,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/string-inl.h"
+#include "runtime-inl.h"
 #include "thread.h"
 #include "utils.h"
 
@@ -62,7 +63,9 @@
 // If "obj" is an array, return the number of elements in the array.
 // Otherwise, return zero.
 static size_t GetElementCount(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (obj == NULL || obj == kClearedJniWeakGlobal || !obj->IsArrayInstance()) {
+  // We assume the special cleared value isn't an array in the if statement below.
+  DCHECK(!Runtime::Current()->GetClearedJniWeakGlobal()->IsArrayInstance());
+  if (obj == nullptr || !obj->IsArrayInstance()) {
     return 0;
   }
   return obj->AsArray()->GetLength();
@@ -81,9 +84,10 @@
     } else if (obj2 == NULL) {
       return false;
     }
-    if (obj1 == kClearedJniWeakGlobal) {
+    Runtime* runtime = Runtime::Current();
+    if (runtime->IsClearedJniWeakGlobal(obj1)) {
       return true;
-    } else if (obj2 == kClearedJniWeakGlobal) {
+    } else if (runtime->IsClearedJniWeakGlobal(obj2)) {
       return false;
     }
 
@@ -116,7 +120,7 @@
     os << "    NULL reference (count=" << equiv << ")\n";
     return;
   }
-  if (obj == kClearedJniWeakGlobal) {
+  if (Runtime::Current()->IsClearedJniWeakGlobal(obj)) {
     os << "    cleared jweak (count=" << equiv << ")\n";
     return;
   }
@@ -167,7 +171,7 @@
     if (ref == NULL) {
       continue;
     }
-    if (ref == kClearedJniWeakGlobal) {
+    if (Runtime::Current()->IsClearedJniWeakGlobal(ref)) {
       os << StringPrintf("    %5d: cleared jweak\n", idx);
       continue;
     }
@@ -209,7 +213,8 @@
     sorted_entries.pop_back();
   }
   while (!sorted_entries.empty() &&
-         sorted_entries.back().Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal) {
+         Runtime::Current()->IsClearedJniWeakGlobal(
+             sorted_entries.back().Read<kWithoutReadBarrier>())) {
     sorted_entries.pop_back();
   }
   if (sorted_entries.empty()) {
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 18fcee4..23f8076 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -348,7 +348,7 @@
   std::unique_ptr<uint32_t[]> large_arg_array_;
 };
 
-static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
+static void CheckMethodArguments(JavaVMExt* vm, mirror::ArtMethod* m, uint32_t* args)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   const DexFile::TypeList* params = m->GetParameterTypeList();
   if (params == nullptr) {
@@ -376,11 +376,11 @@
       self->ClearException();
       ++error_count;
     } else if (!param_type->IsPrimitive()) {
-      // TODO: check primitives are in range.
       // TODO: There is a compaction bug here since GetClassFromTypeIdx can cause thread suspension,
       // this is a hard to fix problem since the args can contain Object*, we need to save and
       // restore them by using a visitor similar to the ones used in the trampoline entrypoints.
-      mirror::Object* argument = reinterpret_cast<mirror::Object*>(args[i + offset]);
+      mirror::Object* argument =
+          (reinterpret_cast<StackReference<mirror::Object>*>(&args[i + offset]))->AsMirrorPtr();
       if (argument != nullptr && !argument->InstanceOf(param_type)) {
         LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
                    << PrettyTypeOf(argument) << " as argument " << (i + 1)
@@ -389,13 +389,40 @@
       }
     } else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) {
       offset++;
+    } else {
+      int32_t arg = static_cast<int32_t>(args[i + offset]);
+      if (param_type->IsPrimitiveBoolean()) {
+        if (arg != JNI_TRUE && arg != JNI_FALSE) {
+          LOG(ERROR) << "JNI ERROR (app bug): expected jboolean (0/1) but got value of "
+              << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+          ++error_count;
+        }
+      } else if (param_type->IsPrimitiveByte()) {
+        if (arg < -128 || arg > 127) {
+          LOG(ERROR) << "JNI ERROR (app bug): expected jbyte but got value of "
+              << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+          ++error_count;
+        }
+      } else if (param_type->IsPrimitiveChar()) {
+        if (args[i + offset] > 0xFFFF) {
+          LOG(ERROR) << "JNI ERROR (app bug): expected jchar but got value of "
+              << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+          ++error_count;
+        }
+      } else if (param_type->IsPrimitiveShort()) {
+        if (arg < -32768 || arg > 0x7FFF) {
+          LOG(ERROR) << "JNI ERROR (app bug): expected jshort but got value of "
+              << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+          ++error_count;
+        }
+      }
     }
   }
-  if (error_count > 0) {
+  if (UNLIKELY(error_count > 0)) {
     // TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort
     // with an argument.
-    JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
-              PrettyMethod(h_m.Get()).c_str());
+    vm->JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
+                  PrettyMethod(h_m.Get()).c_str());
   }
 }
 
@@ -412,7 +439,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   uint32_t* args = arg_array->GetArray();
   if (UNLIKELY(soa.Env()->check_jni)) {
-    CheckMethodArguments(method, args);
+    CheckMethodArguments(soa.Vm(), method, args);
   }
   method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
 }
@@ -533,7 +560,7 @@
   if (UNLIKELY(!declaring_class->IsInitialized())) {
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), h_class, true, true)) {
       return nullptr;
     }
     declaring_class = h_class.Get();
@@ -565,9 +592,16 @@
   }
 
   // If method is not set to be accessible, verify it can be accessed by the caller.
-  if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags())) {
-    ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access method: %s",
-                                                      PrettyMethod(m).c_str()).c_str());
+  mirror::Class* calling_class = nullptr;
+  if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags(),
+                                   &calling_class)) {
+    ThrowIllegalAccessException(nullptr,
+        StringPrintf("Class %s cannot access %s method %s of class %s",
+            calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
+            PrettyJavaAccessFlags(m->GetAccessFlags()).c_str(),
+            PrettyMethod(m).c_str(),
+            m->GetDeclaringClass() == nullptr ? "null" :
+                PrettyClass(m->GetDeclaringClass()).c_str()).c_str());
     return nullptr;
   }
 
@@ -788,7 +822,8 @@
   return UnboxPrimitive(&throw_location, o, dst_class, nullptr, unboxed_value);
 }
 
-bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags) {
+bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
+                  uint32_t access_flags, mirror::Class** calling_class) {
   if ((access_flags & kAccPublic) != 0) {
     return true;
   }
@@ -802,6 +837,8 @@
   if (caller_class == declaring_class) {
     return true;
   }
+  ScopedAssertNoThreadSuspension sants(self, "verify-access");
+  *calling_class = caller_class;
   if ((access_flags & kAccPrivate) != 0) {
     return false;
   }
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 0f41aca..23d8e05 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_REFLECTION_H_
 #define ART_RUNTIME_REFLECTION_H_
 
+#include "base/mutex.h"
 #include "jni.h"
 #include "primitive.h"
 
@@ -75,7 +76,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
-                  uint32_t access_flags)
+                  uint32_t access_flags, mirror::Class** calling_class)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 }  // namespace art
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index 9d10daa..75211e0 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -117,7 +117,7 @@
       // Ensure class is initialized before allocating object
       StackHandleScope<1> hs(self);
       Handle<mirror::Class> h_class(hs.NewHandle(c));
-      bool initialized = class_linker_->EnsureInitialized(h_class, true, true);
+      bool initialized = class_linker_->EnsureInitialized(self, h_class, true, true);
       CHECK(initialized);
       *receiver = c->AllocObject(self);
     }
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index ac9026b..fe05073 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -23,6 +23,16 @@
 
 namespace art {
 
+inline bool Runtime::IsClearedJniWeakGlobal(mirror::Object* obj) {
+  return obj == GetClearedJniWeakGlobal();
+}
+
+inline mirror::Object* Runtime::GetClearedJniWeakGlobal() {
+  mirror::Object* obj = sentinel_.Read();
+  DCHECK(obj != nullptr);
+  return obj;
+}
+
 inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(mirror::ArtMethod* method) {
   DCHECK(method != nullptr);
   // Cannot be imt-conflict-method or resolution-method.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1544aa3..48439b6 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -72,6 +72,7 @@
 #include "reflection.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
+#include "sigchain.h"
 #include "signal_catcher.h"
 #include "signal_set.h"
 #include "handle_scope-inl.h"
@@ -139,9 +140,6 @@
       system_class_loader_(nullptr),
       dump_gc_performance_on_shutdown_(false),
       preinitialization_transaction_(nullptr),
-      null_pointer_handler_(nullptr),
-      suspend_handler_(nullptr),
-      stack_overflow_handler_(nullptr),
       verify_(false),
       target_sdk_version_(0),
       implicit_null_checks_(false),
@@ -198,14 +196,10 @@
   // TODO: acquire a static mutex on Runtime to avoid racing.
   CHECK(instance_ == nullptr || instance_ == this);
   instance_ = nullptr;
-
-  delete null_pointer_handler_;
-  delete suspend_handler_;
-  delete stack_overflow_handler_;
 }
 
 struct AbortState {
-  void Dump(std::ostream& os) NO_THREAD_SAFETY_ANALYSIS {
+  void Dump(std::ostream& os) {
     if (gAborting > 1) {
       os << "Runtime aborting --- recursively, so no thread-specific detail!\n";
       return;
@@ -235,7 +229,9 @@
     DumpAllThreads(os, self);
   }
 
-  void DumpThread(std::ostream& os, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // No thread-safety analysis as we do explicitly test for holding the mutator lock.
+  void DumpThread(std::ostream& os, Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+    DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self) || Locks::mutator_lock_->IsSharedHeld(self));
     self->Dump(os);
     if (self->IsExceptionPending()) {
       ThrowLocation throw_location;
@@ -246,7 +242,7 @@
     }
   }
 
-  void DumpAllThreads(std::ostream& os, Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+  void DumpAllThreads(std::ostream& os, Thread* self) {
     Runtime* runtime = Runtime::Current();
     if (runtime != nullptr) {
       ThreadList* thread_list = runtime->GetThreadList();
@@ -260,7 +256,7 @@
               << "\n";
         }
         os << "All threads:\n";
-        thread_list->DumpLocked(os);
+        thread_list->Dump(os);
       }
     }
   }
@@ -346,36 +342,34 @@
   ScopedObjectAccess soa(Thread::Current());
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
 
-  StackHandleScope<3> hs(soa.Self());
+  StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> class_loader_class(
       hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader)));
-  CHECK(cl->EnsureInitialized(class_loader_class, true, true));
+  CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true));
 
   mirror::ArtMethod* getSystemClassLoader =
       class_loader_class->FindDirectMethod("getSystemClassLoader", "()Ljava/lang/ClassLoader;");
   CHECK(getSystemClassLoader != NULL);
 
   JValue result = InvokeWithJValues(soa, nullptr, soa.EncodeMethod(getSystemClassLoader), nullptr);
-  Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(down_cast<mirror::ClassLoader*>(result.GetL())));
-  CHECK(class_loader.Get() != nullptr);
   JNIEnv* env = soa.Self()->GetJniEnv();
   ScopedLocalRef<jobject> system_class_loader(env,
-                                              soa.AddLocalReference<jobject>(class_loader.Get()));
+                                              soa.AddLocalReference<jobject>(result.GetL()));
   CHECK(system_class_loader.get() != nullptr);
 
-  soa.Self()->SetClassLoaderOverride(class_loader.Get());
+  soa.Self()->SetClassLoaderOverride(system_class_loader.get());
 
   Handle<mirror::Class> thread_class(
       hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread)));
-  CHECK(cl->EnsureInitialized(thread_class, true, true));
+  CHECK(cl->EnsureInitialized(soa.Self(), thread_class, true, true));
 
   mirror::ArtField* contextClassLoader =
       thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
   CHECK(contextClassLoader != NULL);
 
   // We can't run in a transaction yet.
-  contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), class_loader.Get());
+  contextClassLoader->SetObject<false>(soa.Self()->GetPeer(),
+                                       soa.Decode<mirror::ClassLoader*>(system_class_loader.get()));
 
   return env->NewGlobalRef(system_class_loader.get());
 }
@@ -412,7 +406,7 @@
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
-    class_linker_->EnsureInitialized(klass, true, true);
+    class_linker_->EnsureInitialized(soa.Self(), klass, true, true);
   }
 
   // InitNativeMethods needs to be after started_ so that the classes
@@ -711,6 +705,8 @@
                        options->image_isa_,
                        options->collector_type_,
                        options->background_collector_type_,
+                       options->large_object_space_type_,
+                       options->large_object_threshold_,
                        options->parallel_gc_threads_,
                        options->conc_gc_threads_,
                        options->low_memory_mode_,
@@ -740,28 +736,37 @@
     case kArm64:
     case kX86_64:
       implicit_null_checks_ = true;
-      implicit_so_checks_ = true;
+      // Installing stack protection does not play well with valgrind.
+      implicit_so_checks_ = (RUNNING_ON_VALGRIND == 0);
       break;
     default:
       // Keep the defaults.
       break;
   }
 
+  // Always initialize the signal chain so that any calls to sigaction get
+  // correctly routed to the next in the chain regardless of whether we
+  // have claimed the signal or not.
+  InitializeSignalChain();
+
   if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {
     fault_manager.Init();
 
     // These need to be in a specific order.  The null point check handler must be
     // after the suspend check and stack overflow check handlers.
+    //
+    // Note: the instances attach themselves to the fault manager and are handled by it. The manager
+    //       will delete the instance on Shutdown().
     if (implicit_suspend_checks_) {
-      suspend_handler_ = new SuspensionHandler(&fault_manager);
+      new SuspensionHandler(&fault_manager);
     }
 
     if (implicit_so_checks_) {
-      stack_overflow_handler_ = new StackOverflowHandler(&fault_manager);
+      new StackOverflowHandler(&fault_manager);
     }
 
     if (implicit_null_checks_) {
-      null_pointer_handler_ = new NullPointerHandler(&fault_manager);
+      new NullPointerHandler(&fault_manager);
     }
 
     if (kEnableJavaStackTraceHandler) {
@@ -813,6 +818,11 @@
     class_linker_->InitWithoutImage(*options->boot_class_path_);
   }
   CHECK(class_linker_ != nullptr);
+
+  // Initialize the special sentinel_ value early.
+  sentinel_ = GcRoot<mirror::Object>(class_linker_->AllocObject(self));
+  CHECK(sentinel_.Read() != nullptr);
+
   verifier::MethodVerifier::Init();
 
   method_trace_ = options->method_trace_;
@@ -901,13 +911,9 @@
   {
     std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
     std::string reason;
-    self->TransitionFromSuspendedToRunnable();
-    StackHandleScope<1> hs(self);
-    auto class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
-    if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, class_loader, &reason)) {
+    if (!instance_->java_vm_->LoadNativeLibrary(env, mapped_name, nullptr, &reason)) {
       LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
     }
-    self->TransitionFromRunnableToSuspended(kNative);
   }
 
   // Initialize well known classes that may invoke runtime native methods.
@@ -1133,6 +1139,10 @@
 
 void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
   java_vm_->VisitRoots(callback, arg);
+  if (!sentinel_.IsNull()) {
+    sentinel_.VisitRoot(callback, arg, 0, kRootVMInternal);
+    DCHECK(!sentinel_.IsNull());
+  }
   if (!pre_allocated_OutOfMemoryError_.IsNull()) {
     pre_allocated_OutOfMemoryError_.VisitRoot(callback, arg, 0, kRootVMInternal);
     DCHECK(!pre_allocated_OutOfMemoryError_.IsNull());
@@ -1200,14 +1210,10 @@
   method->SetDexMethodIndex(DexFile::kDexNoIndex);
   // When compiling, the code pointer will get set later when the image is loaded.
   if (runtime->IsCompiler()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(nullptr);
-#endif
     method->SetEntryPointFromQuickCompiledCode(nullptr);
   } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableImtConflictTrampoline());
-#endif
     method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickImtConflictTrampoline());
   }
   return method.Get();
@@ -1224,14 +1230,10 @@
   method->SetDexMethodIndex(DexFile::kDexNoIndex);
   // When compiling, the code pointer will get set later when the image is loaded.
   if (runtime->IsCompiler()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(nullptr);
-#endif
     method->SetEntryPointFromQuickCompiledCode(nullptr);
   } else {
-#if defined(ART_USE_PORTABLE_COMPILER)
     method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableResolutionTrampoline());
-#endif
     method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickResolutionTrampoline());
   }
   return method.Get();
@@ -1246,9 +1248,7 @@
   method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod());
   // TODO: use a special method for callee saves
   method->SetDexMethodIndex(DexFile::kDexNoIndex);
-#if defined(ART_USE_PORTABLE_COMPILER)
   method->SetEntryPointFromPortableCompiledCode(nullptr);
-#endif
   method->SetEntryPointFromQuickCompiledCode(nullptr);
   DCHECK_NE(instruction_set_, kNone);
   return method.Get();
@@ -1354,6 +1354,34 @@
   preinitialization_transaction_ = nullptr;
 }
 
+void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
+                                      uint8_t value, bool is_volatile) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction_->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset,
+                                   int8_t value, bool is_volatile) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction_->RecordWriteFieldByte(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset,
+                                   uint16_t value, bool is_volatile) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction_->RecordWriteFieldChar(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset,
+                                    int16_t value, bool is_volatile) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction_->RecordWriteFieldShort(obj, field_offset, value, is_volatile);
+}
+
 void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset,
                                  uint32_t value, bool is_volatile) const {
   DCHECK(IsCompiler());
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8cfa8aa..1a6c6e0 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -200,8 +200,7 @@
   // Detaches the current native thread from the runtime.
   void DetachCurrentThread() LOCKS_EXCLUDED(Locks::mutator_lock_);
 
-  void DumpForSigQuit(std::ostream& os)
-      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DumpForSigQuit(std::ostream& os);
   void DumpLockHolders(std::ostream& os);
 
   ~Runtime();
@@ -247,6 +246,12 @@
     return monitor_pool_;
   }
 
+  // Is the given object the special object used to mark a cleared JNI weak global?
+  bool IsClearedJniWeakGlobal(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Get the special object used to mark a cleared JNI weak global.
+  mirror::Object* GetClearedJniWeakGlobal() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   mirror::Throwable* GetPreAllocatedOutOfMemoryError() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   mirror::Throwable* GetPreAllocatedNoClassDefFoundError()
@@ -427,6 +432,14 @@
   }
   void EnterTransactionMode(Transaction* transaction);
   void ExitTransactionMode();
+  void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
+                               bool is_volatile) const;
+  void RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset, int8_t value,
+                            bool is_volatile) const;
+  void RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset, uint16_t value,
+                            bool is_volatile) const;
+  void RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset, int16_t value,
+                          bool is_volatile) const;
   void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
                           bool is_volatile) const;
   void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
@@ -453,16 +466,8 @@
 
   void AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::string>* arg_vector) const;
 
-  bool ExplicitNullChecks() const {
-    return null_pointer_handler_ == nullptr;
-  }
-
-  bool ExplicitSuspendChecks() const {
-    return suspend_handler_ == nullptr;
-  }
-
   bool ExplicitStackOverflowChecks() const {
-    return stack_overflow_handler_ == nullptr;
+    return !implicit_so_checks_;
   }
 
   bool IsVerificationEnabled() const {
@@ -517,6 +522,10 @@
   GcRoot<mirror::ArtMethod> imt_conflict_method_;
   GcRoot<mirror::ObjectArray<mirror::ArtMethod>> default_imt_;
 
+  // Special sentinel object used to invalid conditions in JNI (cleared weak references) and
+  // JDWP (invalid references).
+  GcRoot<mirror::Object> sentinel_;
+
   InstructionSet instruction_set_;
   QuickMethodFrameInfo callee_save_method_frame_infos_[kLastCalleeSaveType];
 
@@ -623,9 +632,6 @@
 
   // Transaction used for pre-initializing classes at compilation time.
   Transaction* preinitialization_transaction_;
-  NullPointerHandler* null_pointer_handler_;
-  SuspensionHandler* suspend_handler_;
-  StackOverflowHandler* stack_overflow_handler_;
 
   // If false, verification is disabled. True by default.
   bool verify_;
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 23aca45..ae3eaf2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -18,7 +18,8 @@
 #define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
 
 #include "base/casts.h"
-#include "jni_internal-inl.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext-inl.h"
 #include "read_barrier.h"
 #include "thread-inl.h"
 #include "verify_object.h"
@@ -114,6 +115,10 @@
     return vm_;
   }
 
+  bool ForceCopy() const {
+    return vm_->ForceCopy();
+  }
+
   /*
    * Add a local reference for an object to the indirect reference table associated with the
    * current stack frame.  When the native function returns, the reference will be discarded.
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 11e06fe..336340e 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -118,17 +118,6 @@
 
 void SignalCatcher::HandleSigQuit() {
   Runtime* runtime = Runtime::Current();
-  ThreadList* thread_list = runtime->GetThreadList();
-
-  // Grab exclusively the mutator lock, set state to Runnable without checking for a pending
-  // suspend request as we're going to suspend soon anyway. We set the state to Runnable to avoid
-  // giving away the mutator lock.
-  thread_list->SuspendAll();
-  Thread* self = Thread::Current();
-  Locks::mutator_lock_->AssertExclusiveHeld(self);
-  const char* old_cause = self->StartAssertNoThreadSuspension("Handling SIGQUIT");
-  ThreadState old_state = self->SetStateUnsafe(kRunnable);
-
   std::ostringstream os;
   os << "\n"
       << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
@@ -149,14 +138,6 @@
     }
   }
   os << "----- end " << getpid() << " -----\n";
-  CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
-  self->EndAssertNoThreadSuspension(old_cause);
-  thread_list->ResumeAll();
-  // Run the checkpoints after resuming the threads to prevent deadlocks if the checkpoint function
-  // acquires the mutator lock.
-  if (self->ReadFlag(kCheckpointRequest)) {
-    self->RunCheckpointFunction();
-  }
   Output(os.str());
 }
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2d0060e..b8b10d2 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -115,18 +115,22 @@
 mirror::Object* StackVisitor::GetThisObject() const {
   mirror::ArtMethod* m = GetMethod();
   if (m->IsStatic()) {
-    return NULL;
+    return nullptr;
   } else if (m->IsNative()) {
-    if (cur_quick_frame_ != NULL) {
+    if (cur_quick_frame_ != nullptr) {
       HandleScope* hs = reinterpret_cast<HandleScope*>(
           reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffsetInBytes());
       return hs->GetReference(0);
     } else {
       return cur_shadow_frame_->GetVRegReference(0);
     }
+  } else if (m->IsOptimized()) {
+    // TODO: Implement, currently only used for exceptions when jdwp is enabled.
+    LOG(WARNING) << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
+    return nullptr;
   } else {
     const DexFile::CodeItem* code_item = m->GetCodeItem();
-    if (code_item == NULL) {
+    if (code_item == nullptr) {
       UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: "
           << PrettyMethod(m);
       return nullptr;
diff --git a/runtime/stack.h b/runtime/stack.h
index e58caee..44e36c4 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -54,50 +54,6 @@
   kUndefined,
 };
 
-/**
- * @brief Represents the virtual register numbers that denote special meaning.
- * @details This is used to make some virtual register numbers to have specific
- * semantic meaning. This is done so that the compiler can treat all virtual
- * registers the same way and only special case when needed. For example,
- * calculating SSA does not care whether a virtual register is a normal one or
- * a compiler temporary, so it can deal with them in a consistent manner. But,
- * for example if backend cares about temporaries because it has custom spill
- * location, then it can special case them only then.
- */
-enum VRegBaseRegNum : int {
-  /**
-   * @brief Virtual registers originating from dex have number >= 0.
-   */
-  kVRegBaseReg = 0,
-
-  /**
-   * @brief Invalid virtual register number.
-   */
-  kVRegInvalid = -1,
-
-  /**
-   * @brief Used to denote the base register for compiler temporaries.
-   * @details Compiler temporaries are virtual registers not originating
-   * from dex but that are created by compiler.  All virtual register numbers
-   * that are <= kVRegTempBaseReg are categorized as compiler temporaries.
-   */
-  kVRegTempBaseReg = -2,
-
-  /**
-   * @brief Base register of temporary that holds the method pointer.
-   * @details This is a special compiler temporary because it has a specific
-   * location on stack.
-   */
-  kVRegMethodPtrBaseReg = kVRegTempBaseReg,
-
-  /**
-   * @brief Base register of non-special compiler temporary.
-   * @details A non-special compiler temporary is one whose spill location
-   * is flexible.
-   */
-  kVRegNonSpecialTempBaseReg = -3,
-};
-
 // A reference from the shadow stack to a MirrorType object within the Java heap.
 template<class MirrorType>
 class MANAGED StackReference : public mirror::ObjectReference<false, MirrorType> {
@@ -612,75 +568,76 @@
   /*
    * Return sp-relative offset for a Dalvik virtual register, compiler
    * spill or Method* in bytes using Method*.
-   * Note that (reg >= 0) refers to a Dalvik register, (reg == -1)
-   * denotes an invalid Dalvik register, (reg == -2) denotes Method*
-   * and (reg <= -3) denotes a compiler temporary. A compiler temporary
-   * can be thought of as a virtual register that does not exist in the
-   * dex but holds intermediate values to help optimizations and code
-   * generation. A special compiler temporary is one whose location
-   * in frame is well known while non-special ones do not have a requirement
-   * on location in frame as long as code generator itself knows how
-   * to access them.
+   * Note that (reg == -1) denotes an invalid Dalvik register. For the
+   * positive values, the Dalvik registers come first, followed by the
+   * Method*, followed by other special temporaries if any, followed by
+   * regular compiler temporary. As of now we only have the Method* as
+   * as a special compiler temporary.
+   * A compiler temporary can be thought of as a virtual register that
+   * does not exist in the dex but holds intermediate values to help
+   * optimizations and code generation. A special compiler temporary is
+   * one whose location in frame is well known while non-special ones
+   * do not have a requirement on location in frame as long as code
+   * generator itself knows how to access them.
    *
-   *     +---------------------------+
-   *     | IN[ins-1]                 |  {Note: resides in caller's frame}
-   *     |       .                   |
-   *     | IN[0]                     |
-   *     | caller's ArtMethod        |  ... StackReference<ArtMethod>
-   *     +===========================+  {Note: start of callee's frame}
-   *     | core callee-save spill    |  {variable sized}
-   *     +---------------------------+
-   *     | fp callee-save spill      |
-   *     +---------------------------+
-   *     | filler word               |  {For compatibility, if V[locals-1] used as wide
-   *     +---------------------------+
-   *     | V[locals-1]               |
-   *     | V[locals-2]               |
-   *     |      .                    |
-   *     |      .                    |  ... (reg == 2)
-   *     | V[1]                      |  ... (reg == 1)
-   *     | V[0]                      |  ... (reg == 0) <---- "locals_start"
-   *     +---------------------------+
-   *     | Compiler temp region      |  ... (reg <= -3)
-   *     |                           |
-   *     |                           |
-   *     +---------------------------+
-   *     | stack alignment padding   |  {0 to (kStackAlignWords-1) of padding}
-   *     +---------------------------+
-   *     | OUT[outs-1]               |
-   *     | OUT[outs-2]               |
-   *     |       .                   |
-   *     | OUT[0]                    |
-   *     | StackReference<ArtMethod> |  ... (reg == -2) <<== sp, 16-byte aligned
-   *     +===========================+
+   *     +-------------------------------+
+   *     | IN[ins-1]                     |  {Note: resides in caller's frame}
+   *     |       .                       |
+   *     | IN[0]                         |
+   *     | caller's ArtMethod            |  ... StackReference<ArtMethod>
+   *     +===============================+  {Note: start of callee's frame}
+   *     | core callee-save spill        |  {variable sized}
+   *     +-------------------------------+
+   *     | fp callee-save spill          |
+   *     +-------------------------------+
+   *     | filler word                   |  {For compatibility, if V[locals-1] used as wide
+   *     +-------------------------------+
+   *     | V[locals-1]                   |
+   *     | V[locals-2]                   |
+   *     |      .                        |
+   *     |      .                        |  ... (reg == 2)
+   *     | V[1]                          |  ... (reg == 1)
+   *     | V[0]                          |  ... (reg == 0) <---- "locals_start"
+   *     +-------------------------------+
+   *     | stack alignment padding       |  {0 to (kStackAlignWords-1) of padding}
+   *     +-------------------------------+
+   *     | Compiler temp region          |  ... (reg >= max_num_special_temps)
+   *     |      .                        |
+   *     |      .                        |
+   *     | V[max_num_special_temps + 1]  |
+   *     | V[max_num_special_temps + 0]  |
+   *     +-------------------------------+
+   *     | OUT[outs-1]                   |
+   *     | OUT[outs-2]                   |
+   *     |       .                       |
+   *     | OUT[0]                        |
+   *     | StackReference<ArtMethod>     |  ... (reg == num_total_code_regs == special_temp_value) <<== sp, 16-byte aligned
+   *     +===============================+
    */
   static int GetVRegOffset(const DexFile::CodeItem* code_item,
                            uint32_t core_spills, uint32_t fp_spills,
                            size_t frame_size, int reg, InstructionSet isa) {
     DCHECK_EQ(frame_size & (kStackAlignment - 1), 0U);
-    DCHECK_NE(reg, static_cast<int>(kVRegInvalid));
+    DCHECK_NE(reg, -1);
     int spill_size = POPCOUNT(core_spills) * GetBytesPerGprSpillLocation(isa)
         + POPCOUNT(fp_spills) * GetBytesPerFprSpillLocation(isa)
         + sizeof(uint32_t);  // Filler.
-    int num_ins = code_item->ins_size_;
-    int num_regs = code_item->registers_size_ - num_ins;
-    int locals_start = frame_size - spill_size - num_regs * sizeof(uint32_t);
-    if (reg == static_cast<int>(kVRegMethodPtrBaseReg)) {
+    int num_regs = code_item->registers_size_ - code_item->ins_size_;
+    int temp_threshold = code_item->registers_size_;
+    const int max_num_special_temps = 1;
+    if (reg == temp_threshold) {
       // The current method pointer corresponds to special location on stack.
       return 0;
-    } else if (reg <= static_cast<int>(kVRegNonSpecialTempBaseReg)) {
+    } else if (reg >= temp_threshold + max_num_special_temps) {
       /*
        * Special temporaries may have custom locations and the logic above deals with that.
-       * However, non-special temporaries are placed relative to the locals. Since the
-       * virtual register numbers for temporaries "grow" in negative direction, reg number
-       * will always be <= to the temp base reg. Thus, the logic ensures that the first
-       * temp is at offset -4 bytes from locals, the second is at -8 bytes from locals,
-       * and so on.
+       * However, non-special temporaries are placed relative to the outs.
        */
-      int relative_offset =
-          (reg + std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg)) - 1) * sizeof(uint32_t);
-      return locals_start + relative_offset;
+      int temps_start = sizeof(StackReference<mirror::ArtMethod>) + code_item->outs_size_ * sizeof(uint32_t);
+      int relative_offset = (reg - (temp_threshold + max_num_special_temps)) * sizeof(uint32_t);
+      return temps_start + relative_offset;
     }  else if (reg < num_regs) {
+      int locals_start = frame_size - spill_size - num_regs * sizeof(uint32_t);
       return locals_start + (reg * sizeof(uint32_t));
     } else {
       // Handle ins.
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7d3a48f..9b49d31 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -64,9 +64,9 @@
 
   MemoryRegion region_;
 
-  template<typename T> friend class CodeInfo;
-  template<typename T> friend class StackMap;
-  template<typename T> friend class StackMapStream;
+  friend class CodeInfo;
+  friend class StackMap;
+  friend class StackMapStream;
 };
 
 /**
@@ -77,13 +77,15 @@
  * The location_kind for a Dex register can either be:
  * - Constant: register_value holds the constant,
  * - Stack: register_value holds the stack offset,
- * - Register: register_value holds the register number.
+ * - Register: register_value holds the physical register number.
+ * - None: the register has no location yet, meaning it has not been set.
  */
 class DexRegisterMap {
  public:
   explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
 
   enum LocationKind {
+    kNone,
     kInStack,
     kInRegister,
     kConstant
@@ -114,8 +116,8 @@
 
   MemoryRegion region_;
 
-  template <typename T> friend class CodeInfo;
-  template <typename T> friend class StackMapStream;
+  friend class CodeInfo;
+  friend class StackMapStream;
 };
 
 /**
@@ -127,12 +129,11 @@
  * - Knowing the values of dex registers.
  *
  * The information is of the form:
- * [dex_pc, native_pc, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
+ * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
  *
  * Note that register_mask is fixed size, but stack_mask is variable size, depending on the
  * stack size of a method.
  */
-template <typename T>
 class StackMap {
  public:
   explicit StackMap(MemoryRegion region) : region_(region) {}
@@ -145,12 +146,12 @@
     region_.Store<uint32_t>(kDexPcOffset, dex_pc);
   }
 
-  T GetNativePc() const {
-    return region_.Load<T>(kNativePcOffset);
+  uint32_t GetNativePcOffset() const {
+    return region_.Load<uint32_t>(kNativePcOffsetOffset);
   }
 
-  void SetNativePc(T native_pc) {
-    return region_.Store<T>(kNativePcOffset, native_pc);
+  void SetNativePcOffset(uint32_t native_pc_offset) {
+    return region_.Store<uint32_t>(kNativePcOffsetOffset, native_pc_offset);
   }
 
   uint32_t GetDexRegisterMapOffset() const {
@@ -199,8 +200,8 @@
 
  private:
   static constexpr int kDexPcOffset = 0;
-  static constexpr int kNativePcOffset = kDexPcOffset + sizeof(uint32_t);
-  static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffset + sizeof(T);
+  static constexpr int kNativePcOffsetOffset = kDexPcOffset + sizeof(uint32_t);
+  static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffsetOffset + sizeof(uint32_t);
   static constexpr int kInlineDescriptorOffsetOffset =
       kDexRegisterMapOffsetOffset + sizeof(uint32_t);
   static constexpr int kRegisterMaskOffset = kInlineDescriptorOffsetOffset + sizeof(uint32_t);
@@ -211,24 +212,36 @@
 
   MemoryRegion region_;
 
-  template <typename U> friend class CodeInfo;
-  template <typename U> friend class StackMapStream;
+  friend class CodeInfo;
+  friend class StackMapStream;
 };
 
 
 /**
  * Wrapper around all compiler information collected for a method.
  * The information is of the form:
- * [number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
+ * [overall_size, number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
  */
-template <typename T>
 class CodeInfo {
  public:
   explicit CodeInfo(MemoryRegion region) : region_(region) {}
 
-  StackMap<T> GetStackMapAt(size_t i) const {
+  explicit CodeInfo(const void* data) {
+    uint32_t size = reinterpret_cast<const uint32_t*>(data)[0];
+    region_ = MemoryRegion(const_cast<void*>(data), size);
+  }
+
+  StackMap GetStackMapAt(size_t i) const {
     size_t size = StackMapSize();
-    return StackMap<T>(GetStackMaps().Subregion(i * size, size));
+    return StackMap(GetStackMaps().Subregion(i * size, size));
+  }
+
+  uint32_t GetOverallSize() const {
+    return region_.Load<uint32_t>(kOverallSizeOffset);
+  }
+
+  void SetOverallSize(uint32_t size) {
+    region_.Store<uint32_t>(kOverallSizeOffset, size);
   }
 
   uint32_t GetStackMaskSize() const {
@@ -248,47 +261,48 @@
   }
 
   size_t StackMapSize() const {
-    return StackMap<T>::kFixedSize + GetStackMaskSize();
+    return StackMap::kFixedSize + GetStackMaskSize();
   }
 
-  DexRegisterMap GetDexRegisterMapOf(StackMap<T> stack_map, uint32_t number_of_dex_registers) {
+  DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) {
     uint32_t offset = stack_map.GetDexRegisterMapOffset();
     return DexRegisterMap(region_.Subregion(offset,
         DexRegisterMap::kFixedSize + number_of_dex_registers * DexRegisterMap::SingleEntrySize()));
   }
 
-  InlineInfo GetInlineInfoOf(StackMap<T> stack_map) {
+  InlineInfo GetInlineInfoOf(StackMap stack_map) {
     uint32_t offset = stack_map.GetInlineDescriptorOffset();
     uint8_t depth = region_.Load<uint8_t>(offset);
     return InlineInfo(region_.Subregion(offset,
         InlineInfo::kFixedSize + depth * InlineInfo::SingleEntrySize()));
   }
 
-  StackMap<T> GetStackMapForDexPc(uint32_t dex_pc) {
+  StackMap GetStackMapForDexPc(uint32_t dex_pc) {
     for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
-      StackMap<T> stack_map = GetStackMapAt(i);
+      StackMap stack_map = GetStackMapAt(i);
       if (stack_map.GetDexPc() == dex_pc) {
         return stack_map;
       }
     }
     LOG(FATAL) << "Unreachable";
-    return StackMap<T>(MemoryRegion());
+    return StackMap(MemoryRegion());
   }
 
-  StackMap<T> GetStackMapForNativePc(T native_pc) {
+  StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) {
     // TODO: stack maps are sorted by native pc, we can do a binary search.
     for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
-      StackMap<T> stack_map = GetStackMapAt(i);
-      if (stack_map.GetNativePc() == native_pc) {
+      StackMap stack_map = GetStackMapAt(i);
+      if (stack_map.GetNativePcOffset() == native_pc_offset) {
         return stack_map;
       }
     }
     LOG(FATAL) << "Unreachable";
-    return StackMap<T>(MemoryRegion());
+    return StackMap(MemoryRegion());
   }
 
  private:
-  static constexpr int kNumberOfStackMapsOffset = 0;
+  static constexpr int kOverallSizeOffset = 0;
+  static constexpr int kNumberOfStackMapsOffset = kOverallSizeOffset + sizeof(uint32_t);
   static constexpr int kStackMaskSizeOffset = kNumberOfStackMapsOffset + sizeof(uint32_t);
   static constexpr int kFixedSize = kStackMaskSizeOffset + sizeof(uint32_t);
 
@@ -299,7 +313,7 @@
   }
 
   MemoryRegion region_;
-  template<typename U> friend class StackMapStream;
+  friend class StackMapStream;
 };
 
 }  // namespace art
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index a5caa07..170cec6 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -24,7 +24,7 @@
 #include "base/casts.h"
 #include "base/mutex-inl.h"
 #include "gc/heap.h"
-#include "jni_internal.h"
+#include "jni_env_ext.h"
 
 namespace art {
 
@@ -45,6 +45,26 @@
   }
 }
 
+inline void Thread::AllowThreadSuspension() {
+  DCHECK_EQ(Thread::Current(), this);
+  if (UNLIKELY(TestAllFlags())) {
+    CheckSuspend();
+  }
+}
+
+inline void Thread::CheckSuspend() {
+  DCHECK_EQ(Thread::Current(), this);
+  for (;;) {
+    if (ReadFlag(kCheckpointRequest)) {
+      RunCheckpointFunction();
+    } else if (ReadFlag(kSuspendRequest)) {
+      FullSuspendCheck();
+    } else {
+      break;
+    }
+  }
+}
+
 inline ThreadState Thread::SetState(ThreadState new_state) {
   // Cannot use this code to change into Runnable as changing to Runnable should fail if
   // old_state_and_flags.suspend_request is true.
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 6b65f12..07657d1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -446,7 +446,7 @@
 
   ScopedObjectAccess soa(self);
   StackHandleScope<1> hs(self);
-  Handle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName(soa)));
+  MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName(soa)));
   if (peer_thread_name.Get() == nullptr) {
     // The Thread constructor should have set the Thread.name to a
     // non-null value. However, because we can run without code
@@ -507,40 +507,9 @@
     + 4 * KB;
   if (read_stack_size <= min_stack) {
     LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size
-        << " bytes)";
+               << " bytes)";
   }
 
-  // TODO: move this into the Linux GetThreadStack implementation.
-#if !defined(__APPLE__)
-  // If we're the main thread, check whether we were run with an unlimited stack. In that case,
-  // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection
-  // will be broken because we'll die long before we get close to 2GB.
-  bool is_main_thread = (::art::GetTid() == getpid());
-  if (is_main_thread) {
-    rlimit stack_limit;
-    if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) {
-      PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed";
-    }
-    if (stack_limit.rlim_cur == RLIM_INFINITY) {
-      // Find the default stack size for new threads...
-      pthread_attr_t default_attributes;
-      size_t default_stack_size;
-      CHECK_PTHREAD_CALL(pthread_attr_init, (&default_attributes), "default stack size query");
-      CHECK_PTHREAD_CALL(pthread_attr_getstacksize, (&default_attributes, &default_stack_size),
-                         "default stack size query");
-      CHECK_PTHREAD_CALL(pthread_attr_destroy, (&default_attributes), "default stack size query");
-
-      // ...and use that as our limit.
-      size_t old_stack_size = read_stack_size;
-      tlsPtr_.stack_size = default_stack_size;
-      tlsPtr_.stack_begin += (old_stack_size - default_stack_size);
-      VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")"
-                    << " to " << PrettySize(default_stack_size)
-                    << " with base " << reinterpret_cast<void*>(tlsPtr_.stack_begin);
-    }
-  }
-#endif
-
   // Set stack_end_ to the bottom of the stack saving space of stack overflows
 
   Runtime* runtime = Runtime::Current();
@@ -623,7 +592,7 @@
     }
   }
   std::ostringstream ss;
-  Runtime::Current()->GetThreadList()->DumpLocked(ss);
+  Runtime::Current()->GetThreadList()->Dump(ss);
   LOG(FATAL) << ss.str();
 }
 
@@ -1098,6 +1067,12 @@
       (*tlsPtr_.name == kThreadNameDuringStartup);
 }
 
+void Thread::AssertPendingException() const {
+  if (UNLIKELY(!IsExceptionPending())) {
+    LOG(FATAL) << "Pending exception expected.";
+  }
+}
+
 void Thread::AssertNoPendingException() const {
   if (UNLIKELY(IsExceptionPending())) {
     ScopedObjectAccess soa(Thread::Current());
@@ -1133,6 +1108,21 @@
   Thread* self = this;
   DCHECK_EQ(self, Thread::Current());
 
+  if (tlsPtr_.jni_env != nullptr) {
+    // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
+    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
+    // Release locally held global references which releasing may require the mutator lock.
+    if (tlsPtr_.jpeer != nullptr) {
+      // If pthread_create fails we don't have a jni env here.
+      tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
+      tlsPtr_.jpeer = nullptr;
+    }
+    if (tlsPtr_.class_loader_override != nullptr) {
+      tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.class_loader_override);
+      tlsPtr_.class_loader_override = nullptr;
+    }
+  }
+
   if (tlsPtr_.opeer != nullptr) {
     ScopedObjectAccess soa(self);
     // We may need to call user-supplied managed code, do this before final clean-up.
@@ -1160,22 +1150,16 @@
       ObjectLock<mirror::Object> locker(self, h_obj);
       locker.NotifyAll();
     }
+    tlsPtr_.opeer = nullptr;
   }
 
-  // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
-  if (tlsPtr_.jni_env != nullptr) {
-    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
-  }
+  Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
 }
 
 Thread::~Thread() {
-  if (tlsPtr_.jni_env != nullptr && tlsPtr_.jpeer != nullptr) {
-    // If pthread_create fails we don't have a jni env here.
-    tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
-    tlsPtr_.jpeer = nullptr;
-  }
-  tlsPtr_.opeer = nullptr;
-
+  CHECK(tlsPtr_.class_loader_override == nullptr);
+  CHECK(tlsPtr_.jpeer == nullptr);
+  CHECK(tlsPtr_.opeer == nullptr);
   bool initialized = (tlsPtr_.jni_env != nullptr);  // Did Thread::Init run?
   if (initialized) {
     delete tlsPtr_.jni_env;
@@ -1208,7 +1192,7 @@
   delete tlsPtr_.stack_trace_sample;
   free(tlsPtr_.nested_signal_state);
 
-  Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+  Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
 
   TearDownAlternateSignalStack();
 }
@@ -1236,7 +1220,7 @@
 
   // Call the handler.
   tlsPtr_.jni_env->CallVoidMethod(handler.get(),
-      WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler_uncaughtException,
+      WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler_uncaughtException,
       peer.get(), exception.get());
 
   // If the handler threw, clear that exception too.
@@ -1260,7 +1244,7 @@
 
 size_t Thread::NumHandleReferences() {
   size_t count = 0;
-  for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
+  for (HandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) {
     count += cur->NumberOfReferences();
   }
   return count;
@@ -1269,7 +1253,7 @@
 bool Thread::HandleScopeContains(jobject obj) const {
   StackReference<mirror::Object>* hs_entry =
       reinterpret_cast<StackReference<mirror::Object>*>(obj);
-  for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
+  for (HandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) {
     if (cur->Contains(hs_entry)) {
       return true;
     }
@@ -1302,6 +1286,7 @@
   IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
   IndirectRefKind kind = GetIndirectRefKind(ref);
   mirror::Object* result;
+  bool expect_null = false;
   // The "kinds" below are sorted by the frequency we expect to encounter them.
   if (kind == kLocal) {
     IndirectReferenceTable& locals = tlsPtr_.jni_env->locals;
@@ -1315,22 +1300,25 @@
       result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr();
       VerifyObject(result);
     } else {
-      result = kInvalidIndirectRefObject;
+      tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj);
+      expect_null = true;
+      result = nullptr;
     }
   } else if (kind == kGlobal) {
-    JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
-    result = vm->globals.SynchronizedGet(const_cast<Thread*>(this), &vm->globals_lock, ref);
+    result = tlsPtr_.jni_env->vm->DecodeGlobal(const_cast<Thread*>(this), ref);
   } else {
     DCHECK_EQ(kind, kWeakGlobal);
-    result = Runtime::Current()->GetJavaVM()->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
-    if (result == kClearedJniWeakGlobal) {
+    result = tlsPtr_.jni_env->vm->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
+    if (Runtime::Current()->IsClearedJniWeakGlobal(result)) {
       // This is a special case where it's okay to return nullptr.
-      return nullptr;
+      expect_null = true;
+      result = nullptr;
     }
   }
 
-  if (UNLIKELY(result == nullptr)) {
-    JniAbortF(nullptr, "use of deleted %s %p", ToStr<IndirectRefKind>(kind).c_str(), obj);
+  if (UNLIKELY(!expect_null && result == nullptr)) {
+    tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p",
+                                   ToStr<IndirectRefKind>(kind).c_str(), obj);
   }
   return result;
 }
@@ -1370,6 +1358,13 @@
   }
 }
 
+void Thread::SetClassLoaderOverride(jobject class_loader_override) {
+  if (tlsPtr_.class_loader_override != nullptr) {
+    GetJniEnv()->DeleteGlobalRef(tlsPtr_.class_loader_override);
+  }
+  tlsPtr_.class_loader_override = GetJniEnv()->NewGlobalRef(class_loader_override);
+}
+
 class CountStackDepthVisitor : public StackVisitor {
  public:
   explicit CountStackDepthVisitor(Thread* thread)
@@ -1614,7 +1609,8 @@
   ThrowNewException(throw_location, exception_class_descriptor, msg.c_str());
 }
 
-void Thread::ThrowNewException(const ThrowLocation& throw_location, const char* exception_class_descriptor,
+void Thread::ThrowNewException(const ThrowLocation& throw_location,
+                               const char* exception_class_descriptor,
                                const char* msg) {
   // Callers should either clear or call ThrowNewWrappedException.
   AssertNoPendingExceptionForNewException(msg);
@@ -1650,7 +1646,8 @@
     return;
   }
 
-  if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(exception_class, true, true))) {
+  if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(soa.Self(), exception_class, true,
+                                                             true))) {
     DCHECK(IsExceptionPending());
     return;
   }
@@ -1842,12 +1839,24 @@
   QUICK_ENTRY_POINT_INFO(pInitializeTypeAndVerifyAccess)
   QUICK_ENTRY_POINT_INFO(pInitializeType)
   QUICK_ENTRY_POINT_INFO(pResolveString)
+  QUICK_ENTRY_POINT_INFO(pSet8Instance)
+  QUICK_ENTRY_POINT_INFO(pSet8Static)
+  QUICK_ENTRY_POINT_INFO(pSet16Instance)
+  QUICK_ENTRY_POINT_INFO(pSet16Static)
   QUICK_ENTRY_POINT_INFO(pSet32Instance)
   QUICK_ENTRY_POINT_INFO(pSet32Static)
   QUICK_ENTRY_POINT_INFO(pSet64Instance)
   QUICK_ENTRY_POINT_INFO(pSet64Static)
   QUICK_ENTRY_POINT_INFO(pSetObjInstance)
   QUICK_ENTRY_POINT_INFO(pSetObjStatic)
+  QUICK_ENTRY_POINT_INFO(pGetByteInstance)
+  QUICK_ENTRY_POINT_INFO(pGetBooleanInstance)
+  QUICK_ENTRY_POINT_INFO(pGetByteStatic)
+  QUICK_ENTRY_POINT_INFO(pGetBooleanStatic)
+  QUICK_ENTRY_POINT_INFO(pGetShortInstance)
+  QUICK_ENTRY_POINT_INFO(pGetCharInstance)
+  QUICK_ENTRY_POINT_INFO(pGetShortStatic)
+  QUICK_ENTRY_POINT_INFO(pGetCharStatic)
   QUICK_ENTRY_POINT_INFO(pGet32Instance)
   QUICK_ENTRY_POINT_INFO(pGet32Static)
   QUICK_ENTRY_POINT_INFO(pGet64Instance)
@@ -2070,48 +2079,72 @@
 
     // Process register map (which native and runtime methods don't have)
     if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
-      const uint8_t* native_gc_map = m->GetNativeGcMap();
-      CHECK(native_gc_map != nullptr) << PrettyMethod(m);
-      const DexFile::CodeItem* code_item = m->GetCodeItem();
-      DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be nullptr or how would we compile its instructions?
-      NativePcOffsetToReferenceMap map(native_gc_map);
-      size_t num_regs = std::min(map.RegWidth() * 8,
-                                 static_cast<size_t>(code_item->registers_size_));
-      if (num_regs > 0) {
+      if (m->IsOptimized()) {
         Runtime* runtime = Runtime::Current();
         const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
         uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
-        const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
-        DCHECK(reg_bitmap != nullptr);
-        const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
-        const VmapTable vmap_table(m->GetVmapTable(code_pointer));
-        QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
-        // For all dex registers in the bitmap
-        StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
-        DCHECK(cur_quick_frame != nullptr);
-        for (size_t reg = 0; reg < num_regs; ++reg) {
-          // Does this register hold a reference?
-          if (TestBitmap(reg, reg_bitmap)) {
-            uint32_t vmap_offset;
-            if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
-              int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
-                                                        kReferenceVReg);
-              // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
-              mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
-              if (*ref_addr != nullptr) {
-                visitor_(ref_addr, reg, this);
+        StackMap map = m->GetStackMap(native_pc_offset);
+        MemoryRegion mask = map.GetStackMask();
+        for (size_t i = 0; i < mask.size_in_bits(); ++i) {
+          if (mask.LoadBit(i)) {
+            StackReference<mirror::Object>* ref_addr =
+                  reinterpret_cast<StackReference<mirror::Object>*>(cur_quick_frame) + i;
+            mirror::Object* ref = ref_addr->AsMirrorPtr();
+            if (ref != nullptr) {
+              mirror::Object* new_ref = ref;
+              visitor_(&new_ref, -1, this);
+              if (ref != new_ref) {
+                ref_addr->Assign(new_ref);
               }
-            } else {
-              StackReference<mirror::Object>* ref_addr =
-                  reinterpret_cast<StackReference<mirror::Object>*>(
-                      GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
-                                  frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
-              mirror::Object* ref = ref_addr->AsMirrorPtr();
-              if (ref != nullptr) {
-                mirror::Object* new_ref = ref;
-                visitor_(&new_ref, reg, this);
-                if (ref != new_ref) {
-                  ref_addr->Assign(new_ref);
+            }
+          }
+        }
+      } else {
+        const uint8_t* native_gc_map = m->GetNativeGcMap();
+        CHECK(native_gc_map != nullptr) << PrettyMethod(m);
+        const DexFile::CodeItem* code_item = m->GetCodeItem();
+        // Can't be nullptr or how would we compile its instructions?
+        DCHECK(code_item != nullptr) << PrettyMethod(m);
+        NativePcOffsetToReferenceMap map(native_gc_map);
+        size_t num_regs = std::min(map.RegWidth() * 8,
+                                   static_cast<size_t>(code_item->registers_size_));
+        if (num_regs > 0) {
+          Runtime* runtime = Runtime::Current();
+          const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
+          uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
+          const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
+          DCHECK(reg_bitmap != nullptr);
+          const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
+          const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+          QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+          // For all dex registers in the bitmap
+          StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
+          DCHECK(cur_quick_frame != nullptr);
+          for (size_t reg = 0; reg < num_regs; ++reg) {
+            // Does this register hold a reference?
+            if (TestBitmap(reg, reg_bitmap)) {
+              uint32_t vmap_offset;
+              if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
+                int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
+                                                          kReferenceVReg);
+                // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
+                mirror::Object** ref_addr =
+                    reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
+                if (*ref_addr != nullptr) {
+                  visitor_(ref_addr, reg, this);
+                }
+              } else {
+                StackReference<mirror::Object>* ref_addr =
+                    reinterpret_cast<StackReference<mirror::Object>*>(
+                        GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
+                                    frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
+                mirror::Object* ref = ref_addr->AsMirrorPtr();
+                if (ref != nullptr) {
+                  mirror::Object* new_ref = ref;
+                  visitor_(&new_ref, reg, this);
+                  if (ref != new_ref) {
+                    ref_addr->Assign(new_ref);
+                  }
                 }
               }
             }
@@ -2144,11 +2177,6 @@
   const uint32_t tid_;
 };
 
-void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) {
-  VerifyObject(class_loader_override);
-  tlsPtr_.class_loader_override = class_loader_override;
-}
-
 void Thread::VisitRoots(RootCallback* visitor, void* arg) {
   uint32_t thread_id = GetThreadId();
   if (tlsPtr_.opeer != nullptr) {
@@ -2158,10 +2186,6 @@
     visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
   }
   tlsPtr_.throw_location.VisitRoots(visitor, arg);
-  if (tlsPtr_.class_loader_override != nullptr) {
-    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.class_loader_override), arg, thread_id,
-            kRootNativeStack);
-  }
   if (tlsPtr_.monitor_enter_object != nullptr) {
     visitor(&tlsPtr_.monitor_enter_object, arg, thread_id, kRootNativeStack);
   }
diff --git a/runtime/thread.h b/runtime/thread.h
index 0c64f1f..6c427b8 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -146,6 +146,12 @@
 
   static Thread* Current();
 
+  // On a runnable thread, check for pending thread suspension request and handle if pending.
+  void AllowThreadSuspension() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Process pending thread suspension request and handle if pending.
+  void CheckSuspend() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts,
                                    mirror::Object* thread_peer)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
@@ -322,6 +328,7 @@
     return tlsPtr_.exception;
   }
 
+  void AssertPendingException() const;
   void AssertNoPendingException() const;
   void AssertNoPendingExceptionForNewException(const char* msg) const;
 
@@ -457,12 +464,11 @@
     tlsPtr_.wait_next = next;
   }
 
-  mirror::ClassLoader* GetClassLoaderOverride() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  jobject GetClassLoaderOverride() {
     return tlsPtr_.class_loader_override;
   }
 
-  void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetClassLoaderOverride(jobject class_loader_override);
 
   // Create the internal representation of a stack trace, that is more time
   // and space efficient to compute than the StackTraceElement[].
@@ -673,7 +679,7 @@
   // Number of references allocated in handle scopes & JNI shadow frames on this thread.
   size_t NumStackReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return NumHandleReferences() + NumJniShadowFrameReferences();
-  };
+  }
 
   // Is the given obj in this thread's stack indirect reference table?
   bool HandleScopeContains(jobject obj) const;
@@ -1029,7 +1035,11 @@
       deoptimization_shadow_frame(nullptr), shadow_frame_under_construction(nullptr), name(nullptr),
       pthread_self(0), last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
       thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
-      thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr) {
+      thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
+      nested_signal_state(nullptr) {
+        for (size_t i = 0; i < kLockLevelCount; ++i) {
+          held_mutexes[i] = nullptr;
+        }
     }
 
     // The biased card table, see CardTable for details.
@@ -1085,7 +1095,7 @@
 
     // Needed to get the right ClassLoader in JNI_OnLoad, but also
     // useful for testing.
-    mirror::ClassLoader* class_loader_override;
+    jobject class_loader_override;
 
     // Thread local, lazily allocated, long jump context. Used to deliver exceptions.
     Context* long_jump_context;
@@ -1162,7 +1172,6 @@
   friend class Runtime;  // For CreatePeer.
   friend class QuickExceptionHandler;  // For dumping the stack.
   friend class ScopedThreadStateChange;
-  friend class SignalCatcher;  // For SetStateUnsafe.
   friend class StubTest;  // For accessing entrypoints.
   friend class ThreadList;  // For ~Thread and Destroy.
 
@@ -1171,6 +1180,23 @@
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };
 
+class ScopedAssertNoThreadSuspension {
+ public:
+  ScopedAssertNoThreadSuspension(Thread* self, const char* cause)
+      : self_(self), old_cause_(self->StartAssertNoThreadSuspension(cause)) {
+  }
+  ~ScopedAssertNoThreadSuspension() {
+    self_->EndAssertNoThreadSuspension(old_cause_);
+  }
+  Thread* Self() {
+    return self_;
+  }
+
+ private:
+  Thread* const self_;
+  const char* old_cause_;
+};
+
 std::ostream& operator<<(std::ostream& os, const Thread& thread);
 std::ostream& operator<<(std::ostream& os, const ThreadState& state);
 
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index ceed254..ec5b775 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -88,10 +88,7 @@
 }
 
 void ThreadList::DumpForSigQuit(std::ostream& os) {
-  {
-    MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
-    DumpLocked(os);
-  }
+  Dump(os);
   DumpUnattachedThreads(os);
 }
 
@@ -133,12 +130,54 @@
   closedir(d);
 }
 
-void ThreadList::DumpLocked(std::ostream& os) {
-  os << "DALVIK THREADS (" << list_.size() << "):\n";
-  for (const auto& thread : list_) {
-    thread->Dump(os);
-    os << "\n";
+// A closure used by Thread::Dump.
+class DumpCheckpoint FINAL : public Closure {
+ public:
+  explicit DumpCheckpoint(std::ostream* os) : os_(os), barrier_(0) {}
+
+  void Run(Thread* thread) OVERRIDE {
+    // Note thread and self may not be equal if thread was already suspended at the point of the
+    // request.
+    Thread* self = Thread::Current();
+    std::ostringstream local_os;
+    {
+      ScopedObjectAccess soa(self);
+      thread->Dump(local_os);
+    }
+    local_os << "\n";
+    {
+      // Use the logging lock to ensure serialization when writing to the common ostream.
+      MutexLock mu(self, *Locks::logging_lock_);
+      *os_ << local_os.str();
+    }
+    barrier_.Pass(self);
   }
+
+  void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
+    Thread* self = Thread::Current();
+    ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+    const uint32_t kWaitTimeoutMs = 10000;
+    bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kWaitTimeoutMs);
+    if (timed_out) {
+      LOG(kIsDebugBuild ? FATAL : ERROR) << "Unexpected time out during dump checkpoint.";
+    }
+  }
+
+ private:
+  // The common stream that will accumulate all the dumps.
+  std::ostream* const os_;
+  // The barrier to be passed through and for the requestor to wait upon.
+  Barrier barrier_;
+};
+
+void ThreadList::Dump(std::ostream& os) {
+  {
+    MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+    os << "DALVIK THREADS (" << list_.size() << "):\n";
+  }
+  DumpCheckpoint checkpoint(&os);
+  size_t threads_running_checkpoint = RunCheckpoint(&checkpoint);
+  checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
 }
 
 void ThreadList::AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread* ignore2) {
@@ -155,12 +194,12 @@
 
 #if HAVE_TIMED_RWLOCK
 // Attempt to rectify locks so that we dump thread list with required locks before exiting.
-static void UnsafeLogFatalForThreadSuspendAllTimeout() NO_THREAD_SAFETY_ANALYSIS __attribute__((noreturn));
+static void UnsafeLogFatalForThreadSuspendAllTimeout() __attribute__((noreturn));
 static void UnsafeLogFatalForThreadSuspendAllTimeout() {
   Runtime* runtime = Runtime::Current();
   std::ostringstream ss;
   ss << "Thread suspend timeout\n";
-  runtime->GetThreadList()->DumpLocked(ss);
+  runtime->GetThreadList()->Dump(ss);
   LOG(FATAL) << ss.str();
   exit(0);
 }
@@ -266,12 +305,10 @@
 // threads.  Returns the number of successful requests.
 size_t ThreadList::RunCheckpointOnRunnableThreads(Closure* checkpoint_function) {
   Thread* self = Thread::Current();
-  if (kIsDebugBuild) {
-    Locks::mutator_lock_->AssertNotExclusiveHeld(self);
-    Locks::thread_list_lock_->AssertNotHeld(self);
-    Locks::thread_suspend_count_lock_->AssertNotHeld(self);
-    CHECK_NE(self->GetState(), kRunnable);
-  }
+  Locks::mutator_lock_->AssertNotExclusiveHeld(self);
+  Locks::thread_list_lock_->AssertNotHeld(self);
+  Locks::thread_suspend_count_lock_->AssertNotHeld(self);
+  CHECK_NE(self->GetState(), kRunnable);
 
   size_t count = 0;
   {
@@ -823,6 +860,8 @@
 
 void ThreadList::Unregister(Thread* self) {
   DCHECK_EQ(self, Thread::Current());
+  CHECK_NE(self->GetState(), kRunnable);
+  Locks::mutator_lock_->AssertNotHeld(self);
 
   VLOG(threads) << "ThreadList::Unregister() " << *self;
 
@@ -836,21 +875,26 @@
     // thread_suspend_count_lock_ so that the unregistering thread cannot be suspended.
     // Note: deliberately not using MutexLock that could hold a stale self pointer.
     Locks::thread_list_lock_->ExclusiveLock(self);
+    bool removed = true;
     if (!Contains(self)) {
       std::ostringstream os;
       DumpNativeStack(os, GetTid(), "  native: ", nullptr);
       LOG(ERROR) << "Request to unregister unattached thread\n" << os.str();
-      self = nullptr;
     } else {
-      // Note: we don't take the thread_suspend_count_lock_ here as to be suspending a thread other
-      // than yourself you need to hold the thread_list_lock_ (see Thread::ModifySuspendCount).
+      Locks::thread_suspend_count_lock_->ExclusiveLock(self);
       if (!self->IsSuspended()) {
         list_.remove(self);
-        delete self;
-        self = nullptr;
+      } else {
+        // We failed to remove the thread due to a suspend request, loop and try again.
+        removed = false;
       }
+      Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
     }
     Locks::thread_list_lock_->ExclusiveUnlock(self);
+    if (removed) {
+      delete self;
+      self = nullptr;
+    }
   }
   // Release the thread ID after the thread is finished and deleted to avoid cases where we can
   // temporarily have multiple threads with the same thread id. When this occurs, it causes
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index bb4f775..9f47f9f 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -39,11 +39,11 @@
   ~ThreadList();
 
   void DumpForSigQuit(std::ostream& os)
-      LOCKS_EXCLUDED(Locks::thread_list_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void DumpLocked(std::ostream& os)  // For thread suspend timeout dumps.
-      EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+      LOCKS_EXCLUDED(Locks::thread_list_lock_);
+  // For thread suspend timeout dumps.
+  void Dump(std::ostream& os)
+      LOCKS_EXCLUDED(Locks::thread_list_lock_,
+                     Locks::thread_suspend_count_lock_);
   pid_t GetLockOwner();  // For SignalCatcher.
 
   // Thread suspension support.
@@ -93,7 +93,8 @@
                      Locks::thread_suspend_count_lock_);
 
   size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function)
-      LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_);
+  LOCKS_EXCLUDED(Locks::thread_list_lock_,
+                 Locks::thread_suspend_count_lock_);
 
   // Suspends all threads
   void SuspendAllForDebugger()
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4bb388f..027f62d 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -560,7 +560,7 @@
                        mirror::ArtMethod* method, uint32_t new_dex_pc) {
   // We're not recorded to listen to this kind of event, so complain.
   LOG(ERROR) << "Unexpected dex PC event in tracing " << PrettyMethod(method) << " " << new_dex_pc;
-};
+}
 
 void Trace::FieldRead(Thread* /*thread*/, mirror::Object* this_object,
                        mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field)
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 50f1eca..b496f25 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -57,6 +57,40 @@
   }
 }
 
+void Transaction::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
+                                          uint8_t value, bool is_volatile) {
+  DCHECK(obj != nullptr);
+  MutexLock mu(Thread::Current(), log_lock_);
+  ObjectLog& object_log = object_logs_[obj];
+  object_log.LogBooleanValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset,
+                                       int8_t value, bool is_volatile) {
+  DCHECK(obj != nullptr);
+  MutexLock mu(Thread::Current(), log_lock_);
+  ObjectLog& object_log = object_logs_[obj];
+  object_log.LogByteValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset,
+                                       uint16_t value, bool is_volatile) {
+  DCHECK(obj != nullptr);
+  MutexLock mu(Thread::Current(), log_lock_);
+  ObjectLog& object_log = object_logs_[obj];
+  object_log.LogCharValue(field_offset, value, is_volatile);
+}
+
+
+void Transaction::RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset,
+                                        int16_t value, bool is_volatile) {
+  DCHECK(obj != nullptr);
+  MutexLock mu(Thread::Current(), log_lock_);
+  ObjectLog& object_log = object_logs_[obj];
+  object_log.LogShortValue(field_offset, value, is_volatile);
+}
+
+
 void Transaction::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
                                      bool is_volatile) {
   DCHECK(obj != nullptr);
@@ -223,35 +257,42 @@
   }
 }
 
+void Transaction::ObjectLog::LogBooleanValue(MemberOffset offset, uint8_t value, bool is_volatile) {
+  LogValue(ObjectLog::kBoolean, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogByteValue(MemberOffset offset, int8_t value, bool is_volatile) {
+  LogValue(ObjectLog::kByte, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogCharValue(MemberOffset offset, uint16_t value, bool is_volatile) {
+  LogValue(ObjectLog::kChar, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogShortValue(MemberOffset offset, int16_t value, bool is_volatile) {
+  LogValue(ObjectLog::kShort, offset, value, is_volatile);
+}
+
 void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
-  auto it = field_values_.find(offset.Uint32Value());
-  if (it == field_values_.end()) {
-    ObjectLog::FieldValue field_value;
-    field_value.value = value;
-    field_value.is_volatile = is_volatile;
-    field_value.kind = ObjectLog::k32Bits;
-    field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
-  }
+  LogValue(ObjectLog::k32Bits, offset, value, is_volatile);
 }
 
 void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
+  LogValue(ObjectLog::k64Bits, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
+  LogValue(ObjectLog::kReference, offset, reinterpret_cast<uintptr_t>(obj), is_volatile);
+}
+
+void Transaction::ObjectLog::LogValue(ObjectLog::FieldValueKind kind,
+                                      MemberOffset offset, uint64_t value, bool is_volatile) {
   auto it = field_values_.find(offset.Uint32Value());
   if (it == field_values_.end()) {
     ObjectLog::FieldValue field_value;
     field_value.value = value;
     field_value.is_volatile = is_volatile;
-    field_value.kind = ObjectLog::k64Bits;
-    field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
-  }
-}
-
-void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
-  auto it = field_values_.find(offset.Uint32Value());
-  if (it == field_values_.end()) {
-    ObjectLog::FieldValue field_value;
-    field_value.value = reinterpret_cast<uintptr_t>(obj);
-    field_value.is_volatile = is_volatile;
-    field_value.kind = ObjectLog::kReference;
+    field_value.kind = kind;
     field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
   }
 }
@@ -281,6 +322,42 @@
   // we'd need to disable the check.
   constexpr bool kCheckTransaction = true;
   switch (field_value.kind) {
+    case kBoolean:
+      if (UNLIKELY(field_value.is_volatile)) {
+        obj->SetFieldBooleanVolatile<false, kCheckTransaction>(field_offset,
+                                                         static_cast<bool>(field_value.value));
+      } else {
+        obj->SetFieldBoolean<false, kCheckTransaction>(field_offset,
+                                                 static_cast<bool>(field_value.value));
+      }
+      break;
+    case kByte:
+      if (UNLIKELY(field_value.is_volatile)) {
+        obj->SetFieldByteVolatile<false, kCheckTransaction>(field_offset,
+                                                         static_cast<int8_t>(field_value.value));
+      } else {
+        obj->SetFieldByte<false, kCheckTransaction>(field_offset,
+                                                 static_cast<int8_t>(field_value.value));
+      }
+      break;
+    case kChar:
+      if (UNLIKELY(field_value.is_volatile)) {
+        obj->SetFieldCharVolatile<false, kCheckTransaction>(field_offset,
+                                                          static_cast<uint16_t>(field_value.value));
+      } else {
+        obj->SetFieldChar<false, kCheckTransaction>(field_offset,
+                                                  static_cast<uint16_t>(field_value.value));
+      }
+      break;
+    case kShort:
+      if (UNLIKELY(field_value.is_volatile)) {
+        obj->SetFieldShortVolatile<false, kCheckTransaction>(field_offset,
+                                                          static_cast<int16_t>(field_value.value));
+      } else {
+        obj->SetFieldShort<false, kCheckTransaction>(field_offset,
+                                                  static_cast<int16_t>(field_value.value));
+      }
+      break;
     case k32Bits:
       if (UNLIKELY(field_value.is_volatile)) {
         obj->SetField32Volatile<false, kCheckTransaction>(field_offset,
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 6625390..21d3c98 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -41,6 +41,18 @@
   ~Transaction();
 
   // Record object field changes.
+  void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
+                               bool is_volatile)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset, int8_t value,
+                               bool is_volatile)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset, uint16_t value,
+                            bool is_volatile)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset, int16_t value,
+                             bool is_volatile)
+      LOCKS_EXCLUDED(log_lock_);
   void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
                           bool is_volatile)
       LOCKS_EXCLUDED(log_lock_);
@@ -82,6 +94,10 @@
  private:
   class ObjectLog {
    public:
+    void LogBooleanValue(MemberOffset offset, uint8_t value, bool is_volatile);
+    void LogByteValue(MemberOffset offset, int8_t value, bool is_volatile);
+    void LogCharValue(MemberOffset offset, uint16_t value, bool is_volatile);
+    void LogShortValue(MemberOffset offset, int16_t value, bool is_volatile);
     void Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile);
     void Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile);
     void LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile);
@@ -95,6 +111,10 @@
 
    private:
     enum FieldValueKind {
+      kBoolean,
+      kByte,
+      kChar,
+      kShort,
       k32Bits,
       k64Bits,
       kReference
@@ -106,6 +126,7 @@
       bool is_volatile;
     };
 
+    void LogValue(FieldValueKind kind, MemberOffset offset, uint64_t value, bool is_volatile);
     void UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
                         const FieldValue& field_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 691aec4..a14889c 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -89,7 +89,7 @@
   Handle<mirror::Array> h_obj(
       hs.NewHandle(
           mirror::Array::Alloc<true>(soa.Self(), h_klass.Get(), kArraySize,
-                                     h_klass->GetComponentSize(),
+                                     h_klass->GetComponentSizeShift(),
                                      Runtime::Current()->GetHeap()->GetCurrentAllocator())));
   ASSERT_TRUE(h_obj.Get() != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
@@ -110,7 +110,7 @@
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->EnsureInitialized(h_klass, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   ASSERT_TRUE(h_klass->IsInitialized());
 
   // Lookup fields.
@@ -205,7 +205,7 @@
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->EnsureInitialized(h_klass, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   ASSERT_TRUE(h_klass->IsInitialized());
 
   // Allocate an InstanceFieldTest object.
@@ -305,7 +305,7 @@
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->EnsureInitialized(h_klass, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   ASSERT_TRUE(h_klass->IsInitialized());
 
   // Lookup fields.
@@ -419,12 +419,12 @@
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;", class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(h_klass);
+  class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
-  class_linker_->EnsureInitialized(h_klass, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   Runtime::Current()->ExitTransactionMode();
   ASSERT_FALSE(soa.Self()->IsExceptionPending());
 }
@@ -440,12 +440,12 @@
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;",
                                             class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(h_klass);
+  class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
-  class_linker_->EnsureInitialized(h_klass, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   Runtime::Current()->ExitTransactionMode();
   ASSERT_FALSE(soa.Self()->IsExceptionPending());
 }
@@ -460,33 +460,33 @@
 
   // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
   // be thrown by class initialization due to native call.
-  Handle<mirror::Class> h_klass(
+  MutableHandle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
                                                   "Ljava/lang/ExceptionInInitializerError;")));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(h_klass);
+  class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
   h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(h_klass);
+  class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
   // Load and verify Transaction$NativeSupport used in class initialization.
   h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;",
                                              class_loader));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(h_klass);
+  class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
   h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;",
                                              class_loader));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(h_klass);
+  class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
-  class_linker_->EnsureInitialized(h_klass, true, true);
+  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   Runtime::Current()->ExitTransactionMode();
   ASSERT_TRUE(soa.Self()->IsExceptionPending());
 }
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 3765759..0496d97 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -66,8 +66,9 @@
   uint64_t owner;
   CHECK_PTHREAD_CALL(pthread_threadid_np, (NULL, &owner), __FUNCTION__);  // Requires Mac OS 10.6
   return owner;
+#elif defined(__BIONIC__)
+  return gettid();
 #else
-  // Neither bionic nor glibc exposes gettid(2).
   return syscall(__NR_gettid);
 #endif
 }
@@ -107,6 +108,31 @@
   CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__);
   CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__);
   CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__);
+
+#if defined(__GLIBC__)
+  // If we're the main thread, check whether we were run with an unlimited stack. In that case,
+  // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection
+  // will be broken because we'll die long before we get close to 2GB.
+  bool is_main_thread = (::art::GetTid() == getpid());
+  if (is_main_thread) {
+    rlimit stack_limit;
+    if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) {
+      PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed";
+    }
+    if (stack_limit.rlim_cur == RLIM_INFINITY) {
+      size_t old_stack_size = *stack_size;
+
+      // Use the kernel default limit as our size, and adjust the base to match.
+      *stack_size = 8 * MB;
+      *stack_base = reinterpret_cast<uint8_t*>(*stack_base) + (old_stack_size - *stack_size);
+
+      VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")"
+                    << " to " << PrettySize(*stack_size)
+                    << " with base " << *stack_base;
+    }
+  }
+#endif
+
 #endif
 }
 
@@ -448,6 +474,35 @@
   return result;
 }
 
+std::string PrettyJavaAccessFlags(uint32_t access_flags) {
+  std::string result;
+  if ((access_flags & kAccPublic) != 0) {
+    result += "public ";
+  }
+  if ((access_flags & kAccProtected) != 0) {
+    result += "protected ";
+  }
+  if ((access_flags & kAccPrivate) != 0) {
+    result += "private ";
+  }
+  if ((access_flags & kAccFinal) != 0) {
+    result += "final ";
+  }
+  if ((access_flags & kAccStatic) != 0) {
+    result += "static ";
+  }
+  if ((access_flags & kAccTransient) != 0) {
+    result += "transient ";
+  }
+  if ((access_flags & kAccVolatile) != 0) {
+    result += "volatile ";
+  }
+  if ((access_flags & kAccSynchronized) != 0) {
+    result += "synchronized ";
+  }
+  return result;
+}
+
 std::string PrettySize(int64_t byte_count) {
   // The byte thresholds at which we display amounts.  A byte count is displayed
   // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
@@ -793,7 +848,8 @@
 }
 
 enum ClassNameType { kName, kDescriptor };
-static bool IsValidClassName(const char* s, ClassNameType type, char separator) {
+template<ClassNameType kType, char kSeparator>
+static bool IsValidClassName(const char* s) {
   int arrayCount = 0;
   while (*s == '[') {
     arrayCount++;
@@ -805,7 +861,8 @@
     return false;
   }
 
-  if (arrayCount != 0) {
+  ClassNameType type = kType;
+  if (type != kDescriptor && arrayCount != 0) {
     /*
      * If we're looking at an array of some sort, then it doesn't
      * matter if what is being asked for is a class name; the
@@ -873,7 +930,7 @@
       return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0');
     case '/':
     case '.':
-      if (c != separator) {
+      if (c != kSeparator) {
         // The wrong separator character.
         return false;
       }
@@ -895,15 +952,15 @@
 }
 
 bool IsValidBinaryClassName(const char* s) {
-  return IsValidClassName(s, kName, '.');
+  return IsValidClassName<kName, '.'>(s);
 }
 
 bool IsValidJniClassName(const char* s) {
-  return IsValidClassName(s, kName, '/');
+  return IsValidClassName<kName, '/'>(s);
 }
 
 bool IsValidDescriptor(const char* s) {
-  return IsValidClassName(s, kDescriptor, '/');
+  return IsValidClassName<kDescriptor, '/'>(s);
 }
 
 void Split(const std::string& s, char separator, std::vector<std::string>& result) {
@@ -1002,7 +1059,7 @@
   } else {
     s = thread_name + len - 15;
   }
-#if defined(HAVE_ANDROID_PTHREAD_SETNAME_NP)
+#if defined(__BIONIC__)
   // pthread_setname_np fails rather than truncating long strings.
   char buf[16];       // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
   strncpy(buf, s, sizeof(buf)-1);
@@ -1064,10 +1121,6 @@
 
 void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix,
     mirror::ArtMethod* current_method) {
-  // We may be called from contexts where current_method is not null, so we must assert this.
-  if (current_method != nullptr) {
-    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
-  }
 #ifdef __linux__
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
   if (!backtrace->Unwind(0)) {
@@ -1099,7 +1152,9 @@
         if (it->func_offset != 0) {
           os << "+" << it->func_offset;
         }
-      } else if (current_method != nullptr && current_method->IsWithinQuickCode(it->pc)) {
+      } else if (current_method != nullptr &&
+                 Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
+                 current_method->IsWithinQuickCode(it->pc)) {
         const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode();
         os << JniLongName(current_method) << "+"
            << (it->pc - reinterpret_cast<uintptr_t>(start_of_code));
@@ -1376,6 +1431,21 @@
   return true;
 }
 
+void EncodeUnsignedLeb128(uint32_t data, std::vector<uint8_t>* dst) {
+  Leb128Encoder(dst).PushBackUnsigned(data);
+}
+
+void EncodeSignedLeb128(int32_t data, std::vector<uint8_t>* dst) {
+  Leb128Encoder(dst).PushBackSigned(data);
+}
+
+void PushWord(std::vector<uint8_t>* buf, int data) {
+  buf->push_back(data & 0xff);
+  buf->push_back((data >> 8) & 0xff);
+  buf->push_back((data >> 16) & 0xff);
+  buf->push_back((data >> 24) & 0xff);
+}
+
 std::string PrettyDescriptor(Primitive::Type type) {
   return PrettyDescriptor(Primitive::Descriptor(type));
 }
diff --git a/runtime/utils.h b/runtime/utils.h
index 5bdbba8..3f2d829 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -30,10 +30,6 @@
 #include "instruction_set.h"
 #include "primitive.h"
 
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
 namespace art {
 
 class DexFile;
@@ -315,6 +311,10 @@
 std::string PrettyClassAndClassLoader(mirror::Class* c)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
+// (note the trailing whitespace).
+std::string PrettyJavaAccessFlags(uint32_t access_flags);
+
 // Returns a human-readable size string such as "1MB".
 std::string PrettySize(int64_t size_in_bytes);
 
@@ -503,6 +503,11 @@
   }
 };
 
+void PushWord(std::vector<uint8_t>* buf, int32_t data);
+
+void EncodeUnsignedLeb128(uint32_t data, std::vector<uint8_t>* buf);
+void EncodeSignedLeb128(int32_t data, std::vector<uint8_t>* buf);
+
 // Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
 struct FreeDelete {
   // NOTE: Deleting a const object is valid but free() takes a non-const pointer.
diff --git a/runtime/verifier/instruction_flags.cc b/runtime/verifier/instruction_flags.cc
index f76c226..ca3c687 100644
--- a/runtime/verifier/instruction_flags.cc
+++ b/runtime/verifier/instruction_flags.cc
@@ -22,13 +22,14 @@
 namespace verifier {
 
 std::string InstructionFlags::ToString() const {
-  char encoding[7];
+  char encoding[8];
   if (!IsOpcode()) {
-    strncpy(encoding, "XXXXXX", sizeof(encoding));
+    strncpy(encoding, "XXXXXXX", sizeof(encoding));
   } else {
-    strncpy(encoding, "------", sizeof(encoding));
+    strncpy(encoding, "-------", sizeof(encoding));
     if (IsVisited())               encoding[kVisited] = 'V';
     if (IsChanged())               encoding[kChanged] = 'C';
+    if (IsOpcode())                encoding[kOpcode] = 'O';
     if (IsInTry())                 encoding[kInTry] = 'T';
     if (IsBranchTarget())          encoding[kBranchTarget] = 'B';
     if (IsCompileTimeInfoPoint())  encoding[kCompileTimeInfoPoint] = 'G';
diff --git a/runtime/verifier/instruction_flags.h b/runtime/verifier/instruction_flags.h
index f8abca0..36a6e55 100644
--- a/runtime/verifier/instruction_flags.h
+++ b/runtime/verifier/instruction_flags.h
@@ -20,24 +20,23 @@
 #include <stdint.h>
 #include <string>
 
-#include "base/logging.h"
+#include "base/macros.h"
 
 namespace art {
 namespace verifier {
 
-class InstructionFlags {
+class InstructionFlags FINAL {
  public:
-  InstructionFlags() : length_(0), flags_(0) {}
+  InstructionFlags() : flags_(0) {}
 
-  void SetLengthInCodeUnits(size_t length) {
-    DCHECK_LT(length, 65536u);
-    length_ = length;
+  void SetIsOpcode() {
+    flags_ |= 1 << kOpcode;
   }
-  size_t GetLengthInCodeUnits() {
-    return length_;
+  void ClearIsOpcode() {
+    flags_ &= ~(1 << kOpcode);
   }
   bool IsOpcode() const {
-    return length_ != 0;
+    return (flags_ & (1 << kOpcode)) != 0;
   }
 
   void SetInTry() {
@@ -117,21 +116,22 @@
     // Register type information flowing into the instruction changed and so the instruction must be
     // reprocessed.
     kChanged = 1,
+    // The item at this location is an opcode.
+    kOpcode = 2,
     // Instruction is contained within a try region.
-    kInTry = 2,
+    kInTry = 3,
     // Instruction is the target of a branch (ie the start of a basic block).
-    kBranchTarget = 3,
+    kBranchTarget = 4,
     // Location of interest to the compiler for GC maps and verifier based method sharpening.
-    kCompileTimeInfoPoint = 4,
+    kCompileTimeInfoPoint = 5,
     // A return instruction.
-    kReturn = 5,
+    kReturn = 6,
   };
-
-  // Size of instruction in code units.
-  uint16_t length_;
   uint8_t flags_;
 };
 
+COMPILE_ASSERT(sizeof(InstructionFlags) == sizeof(uint8_t), err);
+
 }  // namespace verifier
 }  // namespace art
 
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
index d4fe106..2d9fd53 100644
--- a/runtime/verifier/method_verifier-inl.h
+++ b/runtime/verifier/method_verifier-inl.h
@@ -39,11 +39,11 @@
 }
 
 inline mirror::ClassLoader* MethodVerifier::GetClassLoader() {
-  return class_loader_->Get();
+  return class_loader_.Get();
 }
 
 inline mirror::DexCache* MethodVerifier::GetDexCache() {
-  return dex_cache_->Get();
+  return dex_cache_.Get();
 }
 
 inline MethodReference MethodVerifier::GetMethodReference() const {
@@ -66,9 +66,9 @@
   return !failure_messages_.empty();
 }
 
-inline RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) {
+inline const RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) {
   DCHECK(!HasFailures());
-  RegType& result = ResolveClassAndCheckAccess(class_idx);
+  const RegType& result = ResolveClassAndCheckAccess(class_idx);
   DCHECK(!HasFailures());
   return result;
 }
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1720e18..9747b4e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -38,6 +38,7 @@
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
+#include "reg_type-inl.h"
 #include "register_line-inl.h"
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
@@ -87,7 +88,8 @@
   }
 }
 
-MethodVerifier::FailureKind MethodVerifier::VerifyClass(mirror::Class* klass,
+MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
+                                                        mirror::Class* klass,
                                                         bool allow_soft_failures,
                                                         std::string* error) {
   if (klass->IsVerified()) {
@@ -117,13 +119,14 @@
     }
     return kHardFailure;
   }
-  StackHandleScope<2> hs(Thread::Current());
+  StackHandleScope<2> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
-  return VerifyClass(&dex_file, dex_cache, class_loader, class_def, allow_soft_failures, error);
+  return VerifyClass(self, &dex_file, dex_cache, class_loader, class_def, allow_soft_failures, error);
 }
 
-MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file,
+MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
+                                                        const DexFile* dex_file,
                                                         Handle<mirror::DexCache> dex_cache,
                                                         Handle<mirror::ClassLoader> class_loader,
                                                         const DexFile::ClassDef* class_def,
@@ -144,6 +147,7 @@
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
   int64_t previous_direct_method_idx = -1;
   while (it.HasNextDirectMethod()) {
+    self->AllowThreadSuspension();
     uint32_t method_idx = it.GetMemberIndex();
     if (method_idx == previous_direct_method_idx) {
       // smali can create dex files with two encoded_methods sharing the same method_idx
@@ -157,17 +161,20 @@
         linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader,
                               NullHandle<mirror::ArtMethod>(), type);
     if (method == nullptr) {
-      DCHECK(Thread::Current()->IsExceptionPending());
+      DCHECK(self->IsExceptionPending());
       // We couldn't resolve the method, but continue regardless.
-      Thread::Current()->ClearException();
+      self->ClearException();
     }
-    MethodVerifier::FailureKind result = VerifyMethod(method_idx,
+    StackHandleScope<1> hs(self);
+    Handle<mirror::ArtMethod> h_method(hs.NewHandle(method));
+    MethodVerifier::FailureKind result = VerifyMethod(self,
+                                                      method_idx,
                                                       dex_file,
                                                       dex_cache,
                                                       class_loader,
                                                       class_def,
                                                       it.GetMethodCodeItem(),
-                                                      method,
+                                                      h_method,
                                                       it.GetMethodAccessFlags(),
                                                       allow_soft_failures,
                                                       false);
@@ -188,6 +195,7 @@
   }
   int64_t previous_virtual_method_idx = -1;
   while (it.HasNextVirtualMethod()) {
+    self->AllowThreadSuspension();
     uint32_t method_idx = it.GetMemberIndex();
     if (method_idx == previous_virtual_method_idx) {
       // smali can create dex files with two encoded_methods sharing the same method_idx
@@ -201,17 +209,20 @@
         linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader,
                               NullHandle<mirror::ArtMethod>(), type);
     if (method == nullptr) {
-      DCHECK(Thread::Current()->IsExceptionPending());
+      DCHECK(self->IsExceptionPending());
       // We couldn't resolve the method, but continue regardless.
-      Thread::Current()->ClearException();
+      self->ClearException();
     }
-    MethodVerifier::FailureKind result = VerifyMethod(method_idx,
+    StackHandleScope<1> hs(self);
+    Handle<mirror::ArtMethod> h_method(hs.NewHandle(method));
+    MethodVerifier::FailureKind result = VerifyMethod(self,
+                                                      method_idx,
                                                       dex_file,
                                                       dex_cache,
                                                       class_loader,
                                                       class_def,
                                                       it.GetMethodCodeItem(),
-                                                      method,
+                                                      h_method,
                                                       it.GetMethodAccessFlags(),
                                                       allow_soft_failures,
                                                       false);
@@ -237,22 +248,22 @@
   }
 }
 
-MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx,
+MethodVerifier::FailureKind MethodVerifier::VerifyMethod(Thread* self, uint32_t method_idx,
                                                          const DexFile* dex_file,
                                                          Handle<mirror::DexCache> dex_cache,
                                                          Handle<mirror::ClassLoader> class_loader,
                                                          const DexFile::ClassDef* class_def,
                                                          const DexFile::CodeItem* code_item,
-                                                         mirror::ArtMethod* method,
+                                                         Handle<mirror::ArtMethod> method,
                                                          uint32_t method_access_flags,
                                                          bool allow_soft_failures,
                                                          bool need_precise_constants) {
   MethodVerifier::FailureKind result = kNoFailure;
   uint64_t start_ns = kTimeVerifyMethod ? NanoTime() : 0;
 
-  MethodVerifier verifier(dex_file, &dex_cache, &class_loader, class_def, code_item,
-                           method_idx, method, method_access_flags, true, allow_soft_failures,
-                           need_precise_constants);
+  MethodVerifier verifier(self, dex_file, dex_cache, class_loader, class_def, code_item,
+                          method_idx, method, method_access_flags, true, allow_soft_failures,
+                          need_precise_constants);
   if (verifier.Verify()) {
     // Verification completed, however failures may be pending that didn't cause the verification
     // to hard fail.
@@ -286,33 +297,41 @@
   return result;
 }
 
-MethodVerifier* MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx,
-                                                    const DexFile* dex_file,
-                                                    Handle<mirror::DexCache> dex_cache,
-                                                    Handle<mirror::ClassLoader> class_loader,
-                                                    const DexFile::ClassDef* class_def,
-                                                    const DexFile::CodeItem* code_item,
-                                                    mirror::ArtMethod* method,
-                                                    uint32_t method_access_flags) {
-  MethodVerifier* verifier = new MethodVerifier(dex_file, &dex_cache, &class_loader, class_def,
-                                                code_item, dex_method_idx, method,
+MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self, std::ostream& os, uint32_t dex_method_idx,
+                                         const DexFile* dex_file,
+                                         Handle<mirror::DexCache> dex_cache,
+                                         Handle<mirror::ClassLoader> class_loader,
+                                         const DexFile::ClassDef* class_def,
+                                         const DexFile::CodeItem* code_item,
+                                         Handle<mirror::ArtMethod> method,
+                                         uint32_t method_access_flags) {
+  MethodVerifier* verifier = new MethodVerifier(self, dex_file, dex_cache, class_loader,
+                                                class_def, code_item, dex_method_idx, method,
                                                 method_access_flags, true, true, true, true);
   verifier->Verify();
   verifier->DumpFailures(os);
   os << verifier->info_messages_.str();
-  verifier->Dump(os);
-
-  return verifier;
+  // Only dump and return if no hard failures. Otherwise the verifier may be not fully initialized
+  // and querying any info is dangerous/can abort.
+  if (verifier->have_pending_hard_failure_) {
+    delete verifier;
+    return nullptr;
+  } else {
+    verifier->Dump(os);
+    return verifier;
+  }
 }
 
-MethodVerifier::MethodVerifier(const DexFile* dex_file, Handle<mirror::DexCache>* dex_cache,
-                               Handle<mirror::ClassLoader>* class_loader,
+MethodVerifier::MethodVerifier(Thread* self,
+                               const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+                               Handle<mirror::ClassLoader> class_loader,
                                const DexFile::ClassDef* class_def,
                                const DexFile::CodeItem* code_item, uint32_t dex_method_idx,
-                               mirror::ArtMethod* method, uint32_t method_access_flags,
+                               Handle<mirror::ArtMethod> method, uint32_t method_access_flags,
                                bool can_load_classes, bool allow_soft_failures,
                                bool need_precise_constants, bool verify_to_dump)
-    : reg_types_(can_load_classes),
+    : self_(self),
+      reg_types_(can_load_classes),
       work_insn_idx_(-1),
       dex_method_idx_(dex_method_idx),
       mirror_method_(method),
@@ -347,21 +366,44 @@
 
 void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc,
                                       std::vector<uint32_t>* monitor_enter_dex_pcs) {
-  StackHandleScope<2> hs(Thread::Current());
+  Thread* self = Thread::Current();
+  StackHandleScope<3> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
-  MethodVerifier verifier(m->GetDexFile(), &dex_cache, &class_loader, &m->GetClassDef(),
-                          m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false,
-                          true, false);
+  Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+  MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
+                          m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
+                          false, true, false);
   verifier.interesting_dex_pc_ = dex_pc;
   verifier.monitor_enter_dex_pcs_ = monitor_enter_dex_pcs;
   verifier.FindLocksAtDexPc();
 }
 
+static bool HasMonitorEnterInstructions(const DexFile::CodeItem* const code_item) {
+  const Instruction* inst = Instruction::At(code_item->insns_);
+
+  uint32_t insns_size = code_item->insns_size_in_code_units_;
+  for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
+    if (inst->Opcode() == Instruction::MONITOR_ENTER) {
+      return true;
+    }
+
+    dex_pc += inst->SizeInCodeUnits();
+    inst = inst->Next();
+  }
+
+  return false;
+}
+
 void MethodVerifier::FindLocksAtDexPc() {
   CHECK(monitor_enter_dex_pcs_ != nullptr);
   CHECK(code_item_ != nullptr);  // This only makes sense for methods with code.
 
+  // Quick check whether there are any monitor_enter instructions at all.
+  if (!HasMonitorEnterInstructions(code_item_)) {
+    return;
+  }
+
   // Strictly speaking, we ought to be able to get away with doing a subset of the full method
   // verification. In practice, the phase we want relies on data structures set up by all the
   // earlier passes, so we just run the full method verification and bail out early when we've
@@ -371,12 +413,14 @@
 
 mirror::ArtField* MethodVerifier::FindAccessedFieldAtDexPc(mirror::ArtMethod* m,
                                                            uint32_t dex_pc) {
-  StackHandleScope<2> hs(Thread::Current());
+  Thread* self = Thread::Current();
+  StackHandleScope<3> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
-  MethodVerifier verifier(m->GetDexFile(), &dex_cache, &class_loader, &m->GetClassDef(),
-                          m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), true,
-                          true, false);
+  Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+  MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
+                          m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
+                          true, true, false);
   return verifier.FindAccessedFieldAtDexPc(dex_pc);
 }
 
@@ -401,12 +445,14 @@
 
 mirror::ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::ArtMethod* m,
                                                             uint32_t dex_pc) {
-  StackHandleScope<2> hs(Thread::Current());
+  Thread* self = Thread::Current();
+  StackHandleScope<3> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
-  MethodVerifier verifier(m->GetDexFile(), &dex_cache, &class_loader, &m->GetClassDef(),
-                          m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), true,
-                          true, false);
+  Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+  MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
+                          m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
+                          true, true, false);
   return verifier.FindInvokedMethodAtDexPc(dex_pc);
 }
 
@@ -506,9 +552,9 @@
     }
   }
   failures_.push_back(error);
-  std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
+  std::string location(StringPrintf("%s: [0x%X] ", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
                                     work_insn_idx_));
-  std::ostringstream* failure_message = new std::ostringstream(location);
+  std::ostringstream* failure_message = new std::ostringstream(location, std::ostringstream::ate);
   failure_messages_.push_back(failure_message);
   return *failure_message;
 }
@@ -523,7 +569,7 @@
   DCHECK_NE(failure_num, 0U);
   std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
   prepend += last_fail_message->str();
-  failure_messages_[failure_num - 1] = new std::ostringstream(prepend);
+  failure_messages_[failure_num - 1] = new std::ostringstream(prepend, std::ostringstream::ate);
   delete last_fail_message;
 }
 
@@ -565,9 +611,9 @@
         break;
     }
     size_t inst_size = inst->SizeInCodeUnits();
-    insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size);
+    insn_flags_[dex_pc].SetIsOpcode();
     dex_pc += inst_size;
-    inst = inst->Next();
+    inst = inst->RelativeAt(inst_size);
   }
 
   if (dex_pc != insns_size) {
@@ -603,9 +649,13 @@
           << "'try' block starts inside an instruction (" << start << ")";
       return false;
     }
-    for (uint32_t dex_pc = start; dex_pc < end;
-        dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
+    uint32_t dex_pc = start;
+    const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
+    while (dex_pc < end) {
       insn_flags_[dex_pc].SetInTry();
+      size_t insn_size = inst->SizeInCodeUnits();
+      dex_pc += insn_size;
+      inst = inst->RelativeAt(insn_size);
     }
   }
   // Iterate over each of the handlers to verify target addresses.
@@ -621,16 +671,21 @@
             << "exception handler starts at bad address (" << dex_pc << ")";
         return false;
       }
+      if (!CheckNotMoveResult(code_item_->insns_, dex_pc)) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+            << "exception handler begins with move-result* (" << dex_pc << ")";
+        return false;
+      }
       insn_flags_[dex_pc].SetBranchTarget();
       // Ensure exception types are resolved so that they don't need resolution to be delivered,
       // unresolved exception types will be ignored by exception delivery
       if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) {
         mirror::Class* exception_type = linker->ResolveType(*dex_file_,
                                                             iterator.GetHandlerTypeIndex(),
-                                                            *dex_cache_, *class_loader_);
+                                                            dex_cache_, class_loader_);
         if (exception_type == nullptr) {
-          DCHECK(Thread::Current()->IsExceptionPending());
-          Thread::Current()->ClearException();
+          DCHECK(self_->IsExceptionPending());
+          self_->ClearException();
         }
       }
     }
@@ -762,7 +817,7 @@
   return result;
 }
 
-bool MethodVerifier::CheckRegisterIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckRegisterIndex(uint32_t idx) {
   if (idx >= code_item_->registers_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register index out of range (" << idx << " >= "
                                       << code_item_->registers_size_ << ")";
@@ -771,7 +826,7 @@
   return true;
 }
 
-bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) {
   if (idx + 1 >= code_item_->registers_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register index out of range (" << idx
                                       << "+1 >= " << code_item_->registers_size_ << ")";
@@ -780,7 +835,7 @@
   return true;
 }
 
-bool MethodVerifier::CheckFieldIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckFieldIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().field_ids_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad field index " << idx << " (max "
                                       << dex_file_->GetHeader().field_ids_size_ << ")";
@@ -789,7 +844,7 @@
   return true;
 }
 
-bool MethodVerifier::CheckMethodIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckMethodIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().method_ids_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad method index " << idx << " (max "
                                       << dex_file_->GetHeader().method_ids_size_ << ")";
@@ -798,7 +853,7 @@
   return true;
 }
 
-bool MethodVerifier::CheckNewInstance(uint32_t idx) {
+inline bool MethodVerifier::CheckNewInstance(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().type_ids_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
                                       << dex_file_->GetHeader().type_ids_size_ << ")";
@@ -813,7 +868,7 @@
   return true;
 }
 
-bool MethodVerifier::CheckStringIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckStringIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().string_ids_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad string index " << idx << " (max "
                                       << dex_file_->GetHeader().string_ids_size_ << ")";
@@ -822,7 +877,7 @@
   return true;
 }
 
-bool MethodVerifier::CheckTypeIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckTypeIndex(uint32_t idx) {
   if (idx >= dex_file_->GetHeader().type_ids_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
                                       << dex_file_->GetHeader().type_ids_size_ << ")";
@@ -1140,10 +1195,10 @@
   std::ostream indent_os(&indent_filter);
   const Instruction* inst = Instruction::At(code_item_->insns_);
   for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
-      dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
+      dex_pc += inst->SizeInCodeUnits()) {
     RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
     if (reg_line != nullptr) {
-      indent_os << reg_line->Dump() << "\n";
+      indent_os << reg_line->Dump(this) << "\n";
     }
     indent_os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].ToString() << " ";
     const bool kDumpHexOfInstruction = false;
@@ -1183,12 +1238,12 @@
     // If this is a constructor for a class other than java.lang.Object, mark the first ("this")
     // argument as uninitialized. This restricts field access until the superclass constructor is
     // called.
-    RegType& declaring_class = GetDeclaringClass();
+    const RegType& declaring_class = GetDeclaringClass();
     if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
-      reg_line->SetRegisterType(arg_start + cur_arg,
+      reg_line->SetRegisterType(this, arg_start + cur_arg,
                                 reg_types_.UninitializedThisArgument(declaring_class));
     } else {
-      reg_line->SetRegisterType(arg_start + cur_arg, declaring_class);
+      reg_line->SetRegisterType(this, arg_start + cur_arg, declaring_class);
     }
     cur_arg++;
   }
@@ -1215,31 +1270,31 @@
         // it's effectively considered initialized the instant we reach here (in the sense that we
         // can return without doing anything or call virtual methods).
         {
-          RegType& reg_type = ResolveClassAndCheckAccess(iterator.GetTypeIdx());
+          const RegType& reg_type = ResolveClassAndCheckAccess(iterator.GetTypeIdx());
           if (!reg_type.IsNonZeroReferenceTypes()) {
             DCHECK(HasFailures());
             return false;
           }
-          reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
+          reg_line->SetRegisterType(this, arg_start + cur_arg, reg_type);
         }
         break;
       case 'Z':
-        reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Boolean());
+        reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Boolean());
         break;
       case 'C':
-        reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Char());
+        reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Char());
         break;
       case 'B':
-        reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Byte());
+        reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Byte());
         break;
       case 'I':
-        reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Integer());
+        reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Integer());
         break;
       case 'S':
-        reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Short());
+        reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Short());
         break;
       case 'F':
-        reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Float());
+        reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Float());
         break;
       case 'J':
       case 'D': {
@@ -1249,9 +1304,16 @@
           return false;
         }
 
-        RegType& lo_half = descriptor[0] == 'J' ? reg_types_.LongLo() : reg_types_.DoubleLo();
-        RegType& hi_half = descriptor[0] == 'J' ? reg_types_.LongHi() : reg_types_.DoubleHi();
-        reg_line->SetRegisterTypeWide(arg_start + cur_arg, lo_half, hi_half);
+        const RegType* lo_half;
+        const RegType* hi_half;
+        if (descriptor[0] == 'J') {
+          lo_half = &reg_types_.LongLo();
+          hi_half = &reg_types_.LongHi();
+        } else {
+          lo_half = &reg_types_.DoubleLo();
+          hi_half = &reg_types_.DoubleHi();
+        }
+        reg_line->SetRegisterTypeWide(this, arg_start + cur_arg, *lo_half, *hi_half);
         cur_arg++;
         break;
       }
@@ -1313,6 +1375,7 @@
 
   /* Continue until no instructions are marked "changed". */
   while (true) {
+    self_->AllowThreadSuspension();
     // Find the first marked one. Use "start_guess" as a way to find one quickly.
     uint32_t insn_idx = start_guess;
     for (; insn_idx < insns_size; insn_idx++) {
@@ -1350,8 +1413,8 @@
           std::cout << info_messages_.str();
           LOG(FATAL) << "work_line diverged in " << PrettyMethod(dex_method_idx_, *dex_file_)
                      << "@" << reinterpret_cast<void*>(work_insn_idx_) << "\n"
-                     << " work_line=" << *work_line_ << "\n"
-                     << "  expected=" << *register_line;
+                     << " work_line=" << work_line_->Dump(this) << "\n"
+                     << "  expected=" << register_line->Dump(this);
         }
       }
     }
@@ -1377,7 +1440,8 @@
      */
     int dead_start = -1;
     uint32_t insn_idx = 0;
-    for (; insn_idx < insns_size; insn_idx += insn_flags_[insn_idx].GetLengthInCodeUnits()) {
+    for (; insn_idx < insns_size;
+         insn_idx += Instruction::At(code_item_->insns_ + insn_idx)->SizeInCodeUnits()) {
       /*
        * Switch-statement data doesn't get "visited" by scanner. It
        * may or may not be preceded by a padding NOP (for alignment).
@@ -1453,7 +1517,7 @@
   if (gDebugVerify) {
     // Generate processing back trace to debug verifier
     LogVerifyInfo() << "Processing " << inst->DumpString(dex_file_) << "\n"
-                    << *work_line_.get() << "\n";
+                    << work_line_->Dump(this) << "\n";
   }
 
   /*
@@ -1489,31 +1553,31 @@
       break;
 
     case Instruction::MOVE:
-      work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategory1nr);
+      work_line_->CopyRegister1(this, inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategory1nr);
       break;
     case Instruction::MOVE_FROM16:
-      work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategory1nr);
+      work_line_->CopyRegister1(this, inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategory1nr);
       break;
     case Instruction::MOVE_16:
-      work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategory1nr);
+      work_line_->CopyRegister1(this, inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategory1nr);
       break;
     case Instruction::MOVE_WIDE:
-      work_line_->CopyRegister2(inst->VRegA_12x(), inst->VRegB_12x());
+      work_line_->CopyRegister2(this, inst->VRegA_12x(), inst->VRegB_12x());
       break;
     case Instruction::MOVE_WIDE_FROM16:
-      work_line_->CopyRegister2(inst->VRegA_22x(), inst->VRegB_22x());
+      work_line_->CopyRegister2(this, inst->VRegA_22x(), inst->VRegB_22x());
       break;
     case Instruction::MOVE_WIDE_16:
-      work_line_->CopyRegister2(inst->VRegA_32x(), inst->VRegB_32x());
+      work_line_->CopyRegister2(this, inst->VRegA_32x(), inst->VRegB_32x());
       break;
     case Instruction::MOVE_OBJECT:
-      work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategoryRef);
+      work_line_->CopyRegister1(this, inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategoryRef);
       break;
     case Instruction::MOVE_OBJECT_FROM16:
-      work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategoryRef);
+      work_line_->CopyRegister1(this, inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategoryRef);
       break;
     case Instruction::MOVE_OBJECT_16:
-      work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategoryRef);
+      work_line_->CopyRegister1(this, inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategoryRef);
       break;
 
     /*
@@ -1528,13 +1592,13 @@
      * easier to read in some cases.)
      */
     case Instruction::MOVE_RESULT:
-      work_line_->CopyResultRegister1(inst->VRegA_11x(), false);
+      work_line_->CopyResultRegister1(this, inst->VRegA_11x(), false);
       break;
     case Instruction::MOVE_RESULT_WIDE:
-      work_line_->CopyResultRegister2(inst->VRegA_11x());
+      work_line_->CopyResultRegister2(this, inst->VRegA_11x());
       break;
     case Instruction::MOVE_RESULT_OBJECT:
-      work_line_->CopyResultRegister1(inst->VRegA_11x(), true);
+      work_line_->CopyResultRegister1(this, inst->VRegA_11x(), true);
       break;
 
     case Instruction::MOVE_EXCEPTION: {
@@ -1542,21 +1606,21 @@
        * This statement can only appear as the first instruction in an exception handler. We verify
        * that as part of extracting the exception type from the catch block list.
        */
-      RegType& res_type = GetCaughtExceptionType();
-      work_line_->SetRegisterType(inst->VRegA_11x(), res_type);
+      const RegType& res_type = GetCaughtExceptionType();
+      work_line_->SetRegisterType(this, inst->VRegA_11x(), res_type);
       break;
     }
     case Instruction::RETURN_VOID:
-      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+      if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
         if (!GetMethodReturnType().IsConflict()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected";
         }
       }
       break;
     case Instruction::RETURN:
-      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+      if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
         /* check the method signature */
-        RegType& return_type = GetMethodReturnType();
+        const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsCategory1Types()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected non-category 1 return type "
                                             << return_type;
@@ -1564,14 +1628,14 @@
           // Compilers may generate synthetic functions that write byte values into boolean fields.
           // Also, it may use integer values for boolean, byte, short, and character return types.
           const uint32_t vregA = inst->VRegA_11x();
-          RegType& src_type = work_line_->GetRegisterType(vregA);
+          const RegType& src_type = work_line_->GetRegisterType(this, vregA);
           bool use_src = ((return_type.IsBoolean() && src_type.IsByte()) ||
                           ((return_type.IsBoolean() || return_type.IsByte() ||
                            return_type.IsShort() || return_type.IsChar()) &&
                            src_type.IsInteger()));
           /* check the register contents */
           bool success =
-              work_line_->VerifyRegisterType(vregA, use_src ? src_type : return_type);
+              work_line_->VerifyRegisterType(this, vregA, use_src ? src_type : return_type);
           if (!success) {
             AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", vregA));
           }
@@ -1579,15 +1643,15 @@
       }
       break;
     case Instruction::RETURN_WIDE:
-      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+      if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
         /* check the method signature */
-        RegType& return_type = GetMethodReturnType();
+        const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsCategory2Types()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected";
         } else {
           /* check the register contents */
           const uint32_t vregA = inst->VRegA_11x();
-          bool success = work_line_->VerifyRegisterType(vregA, return_type);
+          bool success = work_line_->VerifyRegisterType(this, vregA, return_type);
           if (!success) {
             AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", vregA));
           }
@@ -1595,8 +1659,8 @@
       }
       break;
     case Instruction::RETURN_OBJECT:
-      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
-        RegType& return_type = GetMethodReturnType();
+      if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
+        const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsReferenceTypes()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected";
         } else {
@@ -1604,7 +1668,7 @@
           DCHECK(!return_type.IsZero());
           DCHECK(!return_type.IsUninitializedReference());
           const uint32_t vregA = inst->VRegA_11x();
-          RegType& reg_type = work_line_->GetRegisterType(vregA);
+          const RegType& reg_type = work_line_->GetRegisterType(this, vregA);
           // Disallow returning uninitialized values and verify that the reference in vAA is an
           // instance of the "return_type"
           if (reg_type.IsUninitializedTypes()) {
@@ -1626,75 +1690,75 @@
       /* could be boolean, int, float, or a null reference */
     case Instruction::CONST_4: {
       int32_t val = static_cast<int32_t>(inst->VRegB_11n() << 28) >> 28;
-      work_line_->SetRegisterType(inst->VRegA_11n(),
+      work_line_->SetRegisterType(this, inst->VRegA_11n(),
                                   DetermineCat1Constant(val, need_precise_constants_));
       break;
     }
     case Instruction::CONST_16: {
       int16_t val = static_cast<int16_t>(inst->VRegB_21s());
-      work_line_->SetRegisterType(inst->VRegA_21s(),
+      work_line_->SetRegisterType(this, inst->VRegA_21s(),
                                   DetermineCat1Constant(val, need_precise_constants_));
       break;
     }
     case Instruction::CONST: {
       int32_t val = inst->VRegB_31i();
-      work_line_->SetRegisterType(inst->VRegA_31i(),
+      work_line_->SetRegisterType(this, inst->VRegA_31i(),
                                   DetermineCat1Constant(val, need_precise_constants_));
       break;
     }
     case Instruction::CONST_HIGH16: {
       int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
-      work_line_->SetRegisterType(inst->VRegA_21h(),
+      work_line_->SetRegisterType(this, inst->VRegA_21h(),
                                   DetermineCat1Constant(val, need_precise_constants_));
       break;
     }
       /* could be long or double; resolved upon use */
     case Instruction::CONST_WIDE_16: {
       int64_t val = static_cast<int16_t>(inst->VRegB_21s());
-      RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
-      RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
-      work_line_->SetRegisterTypeWide(inst->VRegA_21s(), lo, hi);
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(this, inst->VRegA_21s(), lo, hi);
       break;
     }
     case Instruction::CONST_WIDE_32: {
       int64_t val = static_cast<int32_t>(inst->VRegB_31i());
-      RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
-      RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
-      work_line_->SetRegisterTypeWide(inst->VRegA_31i(), lo, hi);
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(this, inst->VRegA_31i(), lo, hi);
       break;
     }
     case Instruction::CONST_WIDE: {
       int64_t val = inst->VRegB_51l();
-      RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
-      RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
-      work_line_->SetRegisterTypeWide(inst->VRegA_51l(), lo, hi);
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(this, inst->VRegA_51l(), lo, hi);
       break;
     }
     case Instruction::CONST_WIDE_HIGH16: {
       int64_t val = static_cast<uint64_t>(inst->VRegB_21h()) << 48;
-      RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
-      RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
-      work_line_->SetRegisterTypeWide(inst->VRegA_21h(), lo, hi);
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(this, inst->VRegA_21h(), lo, hi);
       break;
     }
     case Instruction::CONST_STRING:
-      work_line_->SetRegisterType(inst->VRegA_21c(), reg_types_.JavaLangString());
+      work_line_->SetRegisterType(this, inst->VRegA_21c(), reg_types_.JavaLangString());
       break;
     case Instruction::CONST_STRING_JUMBO:
-      work_line_->SetRegisterType(inst->VRegA_31c(), reg_types_.JavaLangString());
+      work_line_->SetRegisterType(this, inst->VRegA_31c(), reg_types_.JavaLangString());
       break;
     case Instruction::CONST_CLASS: {
       // Get type from instruction if unresolved then we need an access check
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
-      RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+      const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
       // Register holds class, ie its type is class, on error it will hold Conflict.
-      work_line_->SetRegisterType(inst->VRegA_21c(),
+      work_line_->SetRegisterType(this, inst->VRegA_21c(),
                                   res_type.IsConflict() ? res_type
-                                                        : reg_types_.JavaLangClass(true));
+                                                        : reg_types_.JavaLangClass());
       break;
     }
     case Instruction::MONITOR_ENTER:
-      work_line_->PushMonitor(inst->VRegA_11x(), work_insn_idx_);
+      work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_);
       break;
     case Instruction::MONITOR_EXIT:
       /*
@@ -1718,7 +1782,7 @@
        * "live" so we still need to check it.
        */
       opcode_flags &= ~Instruction::kThrow;
-      work_line_->PopMonitor(inst->VRegA_11x());
+      work_line_->PopMonitor(this, inst->VRegA_11x());
       break;
 
     case Instruction::CHECK_CAST:
@@ -1732,10 +1796,10 @@
        */
       const bool is_checkcast = (inst->Opcode() == Instruction::CHECK_CAST);
       const uint32_t type_idx = (is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c();
-      RegType& res_type = ResolveClassAndCheckAccess(type_idx);
+      const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
       if (res_type.IsConflict()) {
         // If this is a primitive type, fail HARD.
-        mirror::Class* klass = (*dex_cache_)->GetResolvedType(type_idx);
+        mirror::Class* klass = dex_cache_->GetResolvedType(type_idx);
         if (klass != nullptr && klass->IsPrimitive()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type "
               << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in "
@@ -1745,13 +1809,13 @@
 
         DCHECK_NE(failures_.size(), 0U);
         if (!is_checkcast) {
-          work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean());
+          work_line_->SetRegisterType(this, inst->VRegA_22c(), reg_types_.Boolean());
         }
         break;  // bad class
       }
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
       uint32_t orig_type_reg = (is_checkcast) ? inst->VRegA_21c() : inst->VRegB_22c();
-      RegType& orig_type = work_line_->GetRegisterType(orig_type_reg);
+      const RegType& orig_type = work_line_->GetRegisterType(this, orig_type_reg);
       if (!res_type.IsNonZeroReferenceTypes()) {
         if (is_checkcast) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on unexpected class " << res_type;
@@ -1766,20 +1830,20 @@
         }
       } else {
         if (is_checkcast) {
-          work_line_->SetRegisterType(inst->VRegA_21c(), res_type);
+          work_line_->SetRegisterType(this, inst->VRegA_21c(), res_type);
         } else {
-          work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean());
+          work_line_->SetRegisterType(this, inst->VRegA_22c(), reg_types_.Boolean());
         }
       }
       break;
     }
     case Instruction::ARRAY_LENGTH: {
-      RegType& res_type = work_line_->GetRegisterType(inst->VRegB_12x());
+      const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegB_12x());
       if (res_type.IsReferenceTypes()) {
         if (!res_type.IsArrayTypes() && !res_type.IsZero()) {  // ie not an array or null
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
         } else {
-          work_line_->SetRegisterType(inst->VRegA_12x(), reg_types_.Integer());
+          work_line_->SetRegisterType(this, inst->VRegA_12x(), reg_types_.Integer());
         }
       } else {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
@@ -1787,7 +1851,7 @@
       break;
     }
     case Instruction::NEW_INSTANCE: {
-      RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+      const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
       if (res_type.IsConflict()) {
         DCHECK_NE(failures_.size(), 0U);
         break;  // bad class
@@ -1799,12 +1863,12 @@
             << "new-instance on primitive, interface or abstract class" << res_type;
         // Soft failure so carry on to set register type.
       }
-      RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_);
+      const RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_);
       // Any registers holding previous allocations from this address that have not yet been
       // initialized must be marked invalid.
-      work_line_->MarkUninitRefsAsInvalid(uninit_type);
+      work_line_->MarkUninitRefsAsInvalid(this, uninit_type);
       // add the new uninitialized reference to the register state
-      work_line_->SetRegisterType(inst->VRegA_21c(), uninit_type);
+      work_line_->SetRegisterType(this, inst->VRegA_21c(), uninit_type);
       break;
     }
     case Instruction::NEW_ARRAY:
@@ -1820,39 +1884,39 @@
       break;
     case Instruction::CMPL_FLOAT:
     case Instruction::CMPG_FLOAT:
-      if (!work_line_->VerifyRegisterType(inst->VRegB_23x(), reg_types_.Float())) {
+      if (!work_line_->VerifyRegisterType(this, inst->VRegB_23x(), reg_types_.Float())) {
         break;
       }
-      if (!work_line_->VerifyRegisterType(inst->VRegC_23x(), reg_types_.Float())) {
+      if (!work_line_->VerifyRegisterType(this, inst->VRegC_23x(), reg_types_.Float())) {
         break;
       }
-      work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
+      work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Integer());
       break;
     case Instruction::CMPL_DOUBLE:
     case Instruction::CMPG_DOUBLE:
-      if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.DoubleLo(),
+      if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegB_23x(), reg_types_.DoubleLo(),
                                               reg_types_.DoubleHi())) {
         break;
       }
-      if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.DoubleLo(),
+      if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegC_23x(), reg_types_.DoubleLo(),
                                               reg_types_.DoubleHi())) {
         break;
       }
-      work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
+      work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Integer());
       break;
     case Instruction::CMP_LONG:
-      if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.LongLo(),
+      if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegB_23x(), reg_types_.LongLo(),
                                               reg_types_.LongHi())) {
         break;
       }
-      if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.LongLo(),
+      if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegC_23x(), reg_types_.LongLo(),
                                               reg_types_.LongHi())) {
         break;
       }
-      work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
+      work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Integer());
       break;
     case Instruction::THROW: {
-      RegType& res_type = work_line_->GetRegisterType(inst->VRegA_11x());
+      const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegA_11x());
       if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) {
         Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT)
             << "thrown class " << res_type << " not instanceof Throwable";
@@ -1868,20 +1932,19 @@
     case Instruction::PACKED_SWITCH:
     case Instruction::SPARSE_SWITCH:
       /* verify that vAA is an integer, or can be converted to one */
-      work_line_->VerifyRegisterType(inst->VRegA_31t(), reg_types_.Integer());
+      work_line_->VerifyRegisterType(this, inst->VRegA_31t(), reg_types_.Integer());
       break;
 
     case Instruction::FILL_ARRAY_DATA: {
       /* Similar to the verification done for APUT */
-      RegType& array_type = work_line_->GetRegisterType(inst->VRegA_31t());
+      const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegA_31t());
       /* array_type can be null if the reg type is Zero */
       if (!array_type.IsZero()) {
         if (!array_type.IsArrayTypes()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type "
                                             << array_type;
         } else {
-          RegType& component_type = reg_types_.GetComponentType(array_type,
-                                                                      class_loader_->Get());
+          const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
           DCHECK(!component_type.IsConflict());
           if (component_type.IsNonZeroReferenceTypes()) {
             Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type "
@@ -1908,8 +1971,8 @@
     }
     case Instruction::IF_EQ:
     case Instruction::IF_NE: {
-      RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t());
-      RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t());
+      const RegType& reg_type1 = work_line_->GetRegisterType(this, inst->VRegA_22t());
+      const RegType& reg_type2 = work_line_->GetRegisterType(this, inst->VRegB_22t());
       bool mismatch = false;
       if (reg_type1.IsZero()) {  // zero then integral or reference expected
         mismatch = !reg_type2.IsReferenceTypes() && !reg_type2.IsIntegralTypes();
@@ -1928,8 +1991,8 @@
     case Instruction::IF_GE:
     case Instruction::IF_GT:
     case Instruction::IF_LE: {
-      RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t());
-      RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t());
+      const RegType& reg_type1 = work_line_->GetRegisterType(this, inst->VRegA_22t());
+      const RegType& reg_type2 = work_line_->GetRegisterType(this, inst->VRegB_22t());
       if (!reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes()) {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to 'if' (" << reg_type1 << ","
                                           << reg_type2 << ") must be integral";
@@ -1938,7 +2001,7 @@
     }
     case Instruction::IF_EQZ:
     case Instruction::IF_NEZ: {
-      RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t());
+      const RegType& reg_type = work_line_->GetRegisterType(this, inst->VRegA_21t());
       if (!reg_type.IsReferenceTypes() && !reg_type.IsIntegralTypes()) {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type
                                           << " unexpected as arg to if-eqz/if-nez";
@@ -1984,8 +2047,8 @@
         // type is assignable to the original then allow optimization. This check is performed to
         // ensure that subsequent merges don't lose type information - such as becoming an
         // interface from a class that would lose information relevant to field checks.
-        RegType& orig_type = work_line_->GetRegisterType(instance_of_inst->VRegB_22c());
-        RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c());
+        const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst->VRegB_22c());
+        const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c());
 
         if (!orig_type.Equals(cast_type) &&
             !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
@@ -2000,7 +2063,7 @@
             branch_line.reset(update_line);
           }
           update_line->CopyFromLine(work_line_.get());
-          update_line->SetRegisterType(instance_of_inst->VRegB_22c(), cast_type);
+          update_line->SetRegisterType(this, instance_of_inst->VRegB_22c(), cast_type);
           if (!insn_flags_[instance_of_idx].IsBranchTarget() && 0 != instance_of_idx) {
             // See if instance-of was preceded by a move-object operation, common due to the small
             // register encoding space of instance-of, and propagate type information to the source
@@ -2014,17 +2077,17 @@
             switch (move_inst->Opcode()) {
               case Instruction::MOVE_OBJECT:
                 if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) {
-                  update_line->SetRegisterType(move_inst->VRegB_12x(), cast_type);
+                  update_line->SetRegisterType(this, move_inst->VRegB_12x(), cast_type);
                 }
                 break;
               case Instruction::MOVE_OBJECT_FROM16:
                 if (move_inst->VRegA_22x() == instance_of_inst->VRegB_22c()) {
-                  update_line->SetRegisterType(move_inst->VRegB_22x(), cast_type);
+                  update_line->SetRegisterType(this, move_inst->VRegB_22x(), cast_type);
                 }
                 break;
               case Instruction::MOVE_OBJECT_16:
                 if (move_inst->VRegA_32x() == instance_of_inst->VRegB_22c()) {
-                  update_line->SetRegisterType(move_inst->VRegB_32x(), cast_type);
+                  update_line->SetRegisterType(this, move_inst->VRegB_32x(), cast_type);
                 }
                 break;
               default:
@@ -2040,7 +2103,7 @@
     case Instruction::IF_GEZ:
     case Instruction::IF_GTZ:
     case Instruction::IF_LEZ: {
-      RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t());
+      const RegType& reg_type = work_line_->GetRegisterType(this, inst->VRegA_21t());
       if (!reg_type.IsIntegralTypes()) {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type
                                           << " unexpected as arg to if-ltz/if-gez/if-gtz/if-lez";
@@ -2189,10 +2252,9 @@
                        inst->Opcode() == Instruction::INVOKE_SUPER_RANGE);
       mirror::ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL, is_range,
                                                               is_super);
-      RegType* return_type = nullptr;
+      const RegType* return_type = nullptr;
       if (called_method != nullptr) {
-        Thread* self = Thread::Current();
-        StackHandleScope<1> hs(self);
+        StackHandleScope<1> hs(self_);
         Handle<mirror::ArtMethod> h_called_method(hs.NewHandle(called_method));
         MethodHelper mh(h_called_method);
         mirror::Class* return_type_class = mh.GetReturnType(can_load_classes_);
@@ -2201,8 +2263,8 @@
                                               return_type_class,
                                               return_type_class->CannotBeAssignedFromOtherTypes());
         } else {
-          DCHECK(!can_load_classes_ || self->IsExceptionPending());
-          self->ClearException();
+          DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+          self_->ClearException();
         }
       }
       if (return_type == nullptr) {
@@ -2210,10 +2272,10 @@
         const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
         uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
         const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
-        return_type = &reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+        return_type = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
       }
       if (!return_type->IsLowHalf()) {
-        work_line_->SetResultRegisterType(*return_type);
+        work_line_->SetResultRegisterType(this, *return_type);
       } else {
         work_line_->SetResultRegisterTypeWide(*return_type, return_type->HighHalf(&reg_types_));
       }
@@ -2227,7 +2289,7 @@
                                                                    is_range, false);
       const char* return_type_descriptor;
       bool is_constructor;
-      RegType* return_type = nullptr;
+      const RegType* return_type = nullptr;
       if (called_method == nullptr) {
         uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
         const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
@@ -2237,8 +2299,7 @@
       } else {
         is_constructor = called_method->IsConstructor();
         return_type_descriptor = called_method->GetReturnTypeDescriptor();
-        Thread* self = Thread::Current();
-        StackHandleScope<1> hs(self);
+        StackHandleScope<1> hs(self_);
         Handle<mirror::ArtMethod> h_called_method(hs.NewHandle(called_method));
         MethodHelper mh(h_called_method);
         mirror::Class* return_type_class = mh.GetReturnType(can_load_classes_);
@@ -2247,8 +2308,8 @@
                                               return_type_class,
                                               return_type_class->CannotBeAssignedFromOtherTypes());
         } else {
-          DCHECK(!can_load_classes_ || self->IsExceptionPending());
-          self->ClearException();
+          DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+          self_->ClearException();
         }
       }
       if (is_constructor) {
@@ -2259,7 +2320,7 @@
          * allowing the latter only if the "this" argument is the same as the "this" argument to
          * this method (which implies that we're in a constructor ourselves).
          */
-        RegType& this_type = work_line_->GetInvocationThis(inst, is_range);
+        const RegType& this_type = work_line_->GetInvocationThis(this, inst, is_range);
         if (this_type.IsConflict())  // failure.
           break;
 
@@ -2270,7 +2331,7 @@
         }
 
         /* must be in same class or in superclass */
-        // RegType& this_super_klass = this_type.GetSuperClass(&reg_types_);
+        // const RegType& this_super_klass = this_type.GetSuperClass(&reg_types_);
         // TODO: re-enable constructor type verification
         // if (this_super_klass.IsConflict()) {
           // Unknown super class, fail so we re-check at runtime.
@@ -2289,14 +2350,14 @@
          * Replace the uninitialized reference with an initialized one. We need to do this for all
          * registers that have the same object instance in them, not just the "this" register.
          */
-        work_line_->MarkRefsAsInitialized(this_type);
+        work_line_->MarkRefsAsInitialized(this, this_type);
       }
       if (return_type == nullptr) {
-        return_type = &reg_types_.FromDescriptor(class_loader_->Get(),
-                                                 return_type_descriptor, false);
+        return_type = &reg_types_.FromDescriptor(GetClassLoader(), return_type_descriptor,
+                                                 false);
       }
       if (!return_type->IsLowHalf()) {
-        work_line_->SetResultRegisterType(*return_type);
+        work_line_->SetResultRegisterType(this, *return_type);
       } else {
         work_line_->SetResultRegisterTypeWide(*return_type, return_type->HighHalf(&reg_types_));
       }
@@ -2319,10 +2380,9 @@
         } else {
           descriptor = called_method->GetReturnTypeDescriptor();
         }
-        RegType& return_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor,
-                                                               false);
+        const RegType& return_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
         if (!return_type.IsLowHalf()) {
-          work_line_->SetResultRegisterType(return_type);
+          work_line_->SetResultRegisterType(this, return_type);
         } else {
           work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
         }
@@ -2347,7 +2407,7 @@
       /* Get the type of the "this" arg, which should either be a sub-interface of called
        * interface or Object (see comments in RegType::JoinClass).
        */
-      RegType& this_type = work_line_->GetInvocationThis(inst, is_range);
+      const RegType& this_type = work_line_->GetInvocationThis(this, inst, is_range);
       if (this_type.IsZero()) {
         /* null pointer always passes (and always fails at runtime) */
       } else {
@@ -2377,10 +2437,9 @@
       } else {
         descriptor = abs_method->GetReturnTypeDescriptor();
       }
-      RegType& return_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor,
-                                                             false);
+      const RegType& return_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
       if (!return_type.IsLowHalf()) {
-        work_line_->SetResultRegisterType(return_type);
+        work_line_->SetResultRegisterType(this, return_type);
       } else {
         work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
       }
@@ -2389,74 +2448,74 @@
     }
     case Instruction::NEG_INT:
     case Instruction::NOT_INT:
-      work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Integer());
+      work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer());
       break;
     case Instruction::NEG_LONG:
     case Instruction::NOT_LONG:
-      work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckUnaryOpWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                    reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::NEG_FLOAT:
-      work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Float());
+      work_line_->CheckUnaryOp(this, inst, reg_types_.Float(), reg_types_.Float());
       break;
     case Instruction::NEG_DOUBLE:
-      work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+      work_line_->CheckUnaryOpWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
                                    reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::INT_TO_LONG:
-      work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckUnaryOpToWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                      reg_types_.Integer());
       break;
     case Instruction::INT_TO_FLOAT:
-      work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Integer());
+      work_line_->CheckUnaryOp(this, inst, reg_types_.Float(), reg_types_.Integer());
       break;
     case Instruction::INT_TO_DOUBLE:
-      work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+      work_line_->CheckUnaryOpToWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
                                      reg_types_.Integer());
       break;
     case Instruction::LONG_TO_INT:
-      work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(),
+      work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Integer(),
                                        reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::LONG_TO_FLOAT:
-      work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(),
+      work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Float(),
                                        reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::LONG_TO_DOUBLE:
-      work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+      work_line_->CheckUnaryOpWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
                                    reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::FLOAT_TO_INT:
-      work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Float());
+      work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Float());
       break;
     case Instruction::FLOAT_TO_LONG:
-      work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckUnaryOpToWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                      reg_types_.Float());
       break;
     case Instruction::FLOAT_TO_DOUBLE:
-      work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+      work_line_->CheckUnaryOpToWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
                                      reg_types_.Float());
       break;
     case Instruction::DOUBLE_TO_INT:
-      work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(),
+      work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Integer(),
                                        reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::DOUBLE_TO_LONG:
-      work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckUnaryOpWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                    reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::DOUBLE_TO_FLOAT:
-      work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(),
+      work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Float(),
                                        reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::INT_TO_BYTE:
-      work_line_->CheckUnaryOp(inst, reg_types_.Byte(), reg_types_.Integer());
+      work_line_->CheckUnaryOp(this, inst, reg_types_.Byte(), reg_types_.Integer());
       break;
     case Instruction::INT_TO_CHAR:
-      work_line_->CheckUnaryOp(inst, reg_types_.Char(), reg_types_.Integer());
+      work_line_->CheckUnaryOp(this, inst, reg_types_.Char(), reg_types_.Integer());
       break;
     case Instruction::INT_TO_SHORT:
-      work_line_->CheckUnaryOp(inst, reg_types_.Short(), reg_types_.Integer());
+      work_line_->CheckUnaryOp(this, inst, reg_types_.Short(), reg_types_.Integer());
       break;
 
     case Instruction::ADD_INT:
@@ -2467,13 +2526,13 @@
     case Instruction::SHL_INT:
     case Instruction::SHR_INT:
     case Instruction::USHR_INT:
-      work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(),
+      work_line_->CheckBinaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer(),
                                 reg_types_.Integer(), false);
       break;
     case Instruction::AND_INT:
     case Instruction::OR_INT:
     case Instruction::XOR_INT:
-      work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(),
+      work_line_->CheckBinaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer(),
                                 reg_types_.Integer(), true);
       break;
     case Instruction::ADD_LONG:
@@ -2484,7 +2543,7 @@
     case Instruction::AND_LONG:
     case Instruction::OR_LONG:
     case Instruction::XOR_LONG:
-      work_line_->CheckBinaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckBinaryOpWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                     reg_types_.LongLo(), reg_types_.LongHi(),
                                     reg_types_.LongLo(), reg_types_.LongHi());
       break;
@@ -2492,7 +2551,7 @@
     case Instruction::SHR_LONG:
     case Instruction::USHR_LONG:
       /* shift distance is Int, making these different from other binary operations */
-      work_line_->CheckBinaryOpWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckBinaryOpWideShift(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                          reg_types_.Integer());
       break;
     case Instruction::ADD_FLOAT:
@@ -2500,18 +2559,15 @@
     case Instruction::MUL_FLOAT:
     case Instruction::DIV_FLOAT:
     case Instruction::REM_FLOAT:
-      work_line_->CheckBinaryOp(inst,
-                                reg_types_.Float(),
-                                reg_types_.Float(),
-                                reg_types_.Float(),
-                                false);
+      work_line_->CheckBinaryOp(this, inst, reg_types_.Float(), reg_types_.Float(),
+                                reg_types_.Float(), false);
       break;
     case Instruction::ADD_DOUBLE:
     case Instruction::SUB_DOUBLE:
     case Instruction::MUL_DOUBLE:
     case Instruction::DIV_DOUBLE:
     case Instruction::REM_DOUBLE:
-      work_line_->CheckBinaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+      work_line_->CheckBinaryOpWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
                                     reg_types_.DoubleLo(), reg_types_.DoubleHi(),
                                     reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
@@ -2522,27 +2578,18 @@
     case Instruction::SHL_INT_2ADDR:
     case Instruction::SHR_INT_2ADDR:
     case Instruction::USHR_INT_2ADDR:
-      work_line_->CheckBinaryOp2addr(inst,
-                                     reg_types_.Integer(),
-                                     reg_types_.Integer(),
-                                     reg_types_.Integer(),
-                                     false);
+      work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Integer(), reg_types_.Integer(),
+                                     reg_types_.Integer(), false);
       break;
     case Instruction::AND_INT_2ADDR:
     case Instruction::OR_INT_2ADDR:
     case Instruction::XOR_INT_2ADDR:
-      work_line_->CheckBinaryOp2addr(inst,
-                                     reg_types_.Integer(),
-                                     reg_types_.Integer(),
-                                     reg_types_.Integer(),
-                                     true);
+      work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Integer(), reg_types_.Integer(),
+                                     reg_types_.Integer(), true);
       break;
     case Instruction::DIV_INT_2ADDR:
-      work_line_->CheckBinaryOp2addr(inst,
-                                     reg_types_.Integer(),
-                                     reg_types_.Integer(),
-                                     reg_types_.Integer(),
-                                     false);
+      work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Integer(), reg_types_.Integer(),
+                                     reg_types_.Integer(), false);
       break;
     case Instruction::ADD_LONG_2ADDR:
     case Instruction::SUB_LONG_2ADDR:
@@ -2552,14 +2599,14 @@
     case Instruction::AND_LONG_2ADDR:
     case Instruction::OR_LONG_2ADDR:
     case Instruction::XOR_LONG_2ADDR:
-      work_line_->CheckBinaryOp2addrWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckBinaryOp2addrWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                          reg_types_.LongLo(), reg_types_.LongHi(),
                                          reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::SHL_LONG_2ADDR:
     case Instruction::SHR_LONG_2ADDR:
     case Instruction::USHR_LONG_2ADDR:
-      work_line_->CheckBinaryOp2addrWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+      work_line_->CheckBinaryOp2addrWideShift(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
                                               reg_types_.Integer());
       break;
     case Instruction::ADD_FLOAT_2ADDR:
@@ -2567,18 +2614,15 @@
     case Instruction::MUL_FLOAT_2ADDR:
     case Instruction::DIV_FLOAT_2ADDR:
     case Instruction::REM_FLOAT_2ADDR:
-      work_line_->CheckBinaryOp2addr(inst,
-                                     reg_types_.Float(),
-                                     reg_types_.Float(),
-                                     reg_types_.Float(),
-                                     false);
+      work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Float(), reg_types_.Float(),
+                                     reg_types_.Float(), false);
       break;
     case Instruction::ADD_DOUBLE_2ADDR:
     case Instruction::SUB_DOUBLE_2ADDR:
     case Instruction::MUL_DOUBLE_2ADDR:
     case Instruction::DIV_DOUBLE_2ADDR:
     case Instruction::REM_DOUBLE_2ADDR:
-      work_line_->CheckBinaryOp2addrWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+      work_line_->CheckBinaryOp2addrWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
                                          reg_types_.DoubleLo(),  reg_types_.DoubleHi(),
                                          reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
@@ -2587,12 +2631,14 @@
     case Instruction::MUL_INT_LIT16:
     case Instruction::DIV_INT_LIT16:
     case Instruction::REM_INT_LIT16:
-      work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, true);
+      work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), false,
+                                 true);
       break;
     case Instruction::AND_INT_LIT16:
     case Instruction::OR_INT_LIT16:
     case Instruction::XOR_INT_LIT16:
-      work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, true);
+      work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), true,
+                                 true);
       break;
     case Instruction::ADD_INT_LIT8:
     case Instruction::RSUB_INT_LIT8:
@@ -2602,12 +2648,14 @@
     case Instruction::SHL_INT_LIT8:
     case Instruction::SHR_INT_LIT8:
     case Instruction::USHR_INT_LIT8:
-      work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, false);
+      work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), false,
+                                 false);
       break;
     case Instruction::AND_INT_LIT8:
     case Instruction::OR_INT_LIT8:
     case Instruction::XOR_INT_LIT8:
-      work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false);
+      work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), true,
+                                 false);
       break;
 
     // Special instructions.
@@ -2631,6 +2679,18 @@
     case Instruction::IPUT_QUICK:
       VerifyIPutQuick(inst, reg_types_.Integer(), true);
       break;
+    case Instruction::IPUT_BOOLEAN_QUICK:
+        VerifyIPutQuick(inst, reg_types_.Boolean(), true);
+      break;
+    case Instruction::IPUT_BYTE_QUICK:
+        VerifyIPutQuick(inst, reg_types_.Byte(), true);
+      break;
+    case Instruction::IPUT_CHAR_QUICK:
+        VerifyIPutQuick(inst, reg_types_.Char(), true);
+      break;
+    case Instruction::IPUT_SHORT_QUICK:
+        VerifyIPutQuick(inst, reg_types_.Short(), true);
+      break;
     case Instruction::IPUT_WIDE_QUICK:
       VerifyIPutQuick(inst, reg_types_.LongLo(), true);
       break;
@@ -2643,10 +2703,9 @@
       mirror::ArtMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range);
       if (called_method != nullptr) {
         const char* descriptor = called_method->GetReturnTypeDescriptor();
-        RegType& return_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor,
-                                                               false);
+        const RegType& return_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
         if (!return_type.IsLowHalf()) {
-          work_line_->SetResultRegisterType(return_type);
+          work_line_->SetResultRegisterType(this, return_type);
         } else {
           work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
         }
@@ -2664,10 +2723,6 @@
     case Instruction::UNUSED_43:
     case Instruction::UNUSED_79:
     case Instruction::UNUSED_7A:
-    case Instruction::UNUSED_EB:
-    case Instruction::UNUSED_EC:
-    case Instruction::UNUSED_ED:
-    case Instruction::UNUSED_EE:
     case Instruction::UNUSED_EF:
     case Instruction::UNUSED_F0:
     case Instruction::UNUSED_F1:
@@ -2712,7 +2767,7 @@
    * not expensive and it makes our debugging output cleaner.)
    */
   if (!just_set_result) {
-    work_line_->SetResultTypeToUnknown();
+    work_line_->SetResultTypeToUnknown(this);
   }
 
 
@@ -2737,7 +2792,7 @@
       return false;
     }
     DCHECK_EQ(isConditional, (opcode_flags & Instruction::kContinue) != 0);
-    if (!CheckNotMoveException(code_item_->insns_, work_insn_idx_ + branch_target)) {
+    if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, work_insn_idx_ + branch_target)) {
       return false;
     }
     /* update branch target, set "changed" if appropriate */
@@ -2783,7 +2838,7 @@
          (((int32_t) switch_insns[offset_to_targets + targ * 2 + 1]) << 16);
       abs_offset = work_insn_idx_ + offset;
       DCHECK_LT(abs_offset, code_item_->insns_size_in_code_units_);
-      if (!CheckNotMoveException(code_item_->insns_, abs_offset)) {
+      if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, abs_offset)) {
         return false;
       }
       if (!UpdateRegisters(abs_offset, work_line_.get(), false)) {
@@ -2809,17 +2864,16 @@
         has_catch_all_handler = true;
       } else {
         // It is also a catch-all if it is java.lang.Throwable.
-        mirror::Class* klass = linker->ResolveType(*dex_file_, handler_type_idx, *dex_cache_,
-                                                   *class_loader_);
+        mirror::Class* klass = linker->ResolveType(*dex_file_, handler_type_idx, dex_cache_,
+                                                   class_loader_);
         if (klass != nullptr) {
           if (klass == mirror::Throwable::GetJavaLangThrowable()) {
             has_catch_all_handler = true;
           }
         } else {
           // Clear exception.
-          Thread* self = Thread::Current();
-          DCHECK(self->IsExceptionPending());
-          self->ClearException();
+          DCHECK(self_->IsExceptionPending());
+          self_->ClearException();
         }
       }
       /*
@@ -2856,7 +2910,8 @@
    *        and this change should not be used in those cases.
    */
   if ((opcode_flags & Instruction::kContinue) != 0) {
-    uint32_t next_insn_idx = work_insn_idx_ + CurrentInsnFlags()->GetLengthInCodeUnits();
+    DCHECK_EQ(Instruction::At(code_item_->insns_ + work_insn_idx_), inst);
+    uint32_t next_insn_idx = work_insn_idx_ + inst->SizeInCodeUnits();
     if (next_insn_idx >= code_item_->insns_size_in_code_units_) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area";
       return false;
@@ -2875,12 +2930,12 @@
       const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx);
       Instruction::Code opcode = ret_inst->Opcode();
       if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
-        work_line_->MarkAllRegistersAsConflicts();
+        work_line_->MarkAllRegistersAsConflicts(this);
       } else {
         if (opcode == Instruction::RETURN_WIDE) {
-          work_line_->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+          work_line_->MarkAllRegistersAsConflictsExceptWide(this, ret_inst->VRegA_11x());
         } else {
-          work_line_->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+          work_line_->MarkAllRegistersAsConflictsExcept(this, ret_inst->VRegA_11x());
         }
       }
     }
@@ -2903,7 +2958,7 @@
 
   /* If we're returning from the method, make sure monitor stack is empty. */
   if ((opcode_flags & Instruction::kReturn) != 0) {
-    if (!work_line_->VerifyMonitorStackEmpty()) {
+    if (!work_line_->VerifyMonitorStackEmpty(this)) {
       return false;
     }
   }
@@ -2915,7 +2970,8 @@
    * alone and let the caller sort it out.
    */
   if ((opcode_flags & Instruction::kContinue) != 0) {
-    *start_guess = work_insn_idx_ + insn_flags_[work_insn_idx_].GetLengthInCodeUnits();
+    DCHECK_EQ(Instruction::At(code_item_->insns_ + work_insn_idx_), inst);
+    *start_guess = work_insn_idx_ + inst->SizeInCodeUnits();
   } else if ((opcode_flags & Instruction::kBranch) != 0) {
     /* we're still okay if branch_target is zero */
     *start_guess = work_insn_idx_ + branch_target;
@@ -2927,21 +2983,20 @@
   return true;
 }  // NOLINT(readability/fn_size)
 
-RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
+const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
   const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
-  RegType& referrer = GetDeclaringClass();
-  mirror::Class* klass = (*dex_cache_)->GetResolvedType(class_idx);
-  RegType& result =
-      klass != nullptr ? reg_types_.FromClass(descriptor, klass,
-                                           klass->CannotBeAssignedFromOtherTypes())
-                    : reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+  const RegType& referrer = GetDeclaringClass();
+  mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
+  const RegType& result = klass != nullptr ?
+      reg_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) :
+      reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
   if (result.IsConflict()) {
     Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor
         << "' in " << referrer;
     return result;
   }
   if (klass == nullptr && !result.IsUnresolvedTypes()) {
-    (*dex_cache_)->SetResolvedType(class_idx, result.GetClass());
+    dex_cache_->SetResolvedType(class_idx, result.GetClass());
   }
   // Check if access is allowed. Unresolved types use xxxWithAccessCheck to
   // check at runtime if access is allowed and so pass here. If result is
@@ -2954,8 +3009,8 @@
   return result;
 }
 
-RegType& MethodVerifier::GetCaughtExceptionType() {
-  RegType* common_super = nullptr;
+const RegType& MethodVerifier::GetCaughtExceptionType() {
+  const RegType* common_super = nullptr;
   if (code_item_->tries_size_ != 0) {
     const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
     uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
@@ -2966,7 +3021,7 @@
           if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) {
             common_super = &reg_types_.JavaLangThrowable(false);
           } else {
-            RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
+            const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
             if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
               if (exception.IsUnresolvedTypes()) {
                 // We don't know enough about the type. Fail here and let runtime handle it.
@@ -3001,7 +3056,7 @@
 mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx,
                                                                MethodType method_type) {
   const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
-  RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
+  const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
   if (klass_type.IsConflict()) {
     std::string append(" in attempt to access method ");
     append += dex_file_->GetMethodName(method_id);
@@ -3012,8 +3067,8 @@
     return nullptr;  // Can't resolve Class so no more to do here
   }
   mirror::Class* klass = klass_type.GetClass();
-  RegType& referrer = GetDeclaringClass();
-  mirror::ArtMethod* res_method = (*dex_cache_)->GetResolvedMethod(dex_method_idx);
+  const RegType& referrer = GetDeclaringClass();
+  mirror::ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx);
   if (res_method == nullptr) {
     const char* name = dex_file_->GetMethodName(method_id);
     const Signature signature = dex_file_->GetMethodSignature(method_id);
@@ -3026,7 +3081,7 @@
       res_method = klass->FindVirtualMethod(name, signature);
     }
     if (res_method != nullptr) {
-      (*dex_cache_)->SetResolvedMethod(dex_method_idx, res_method);
+      dex_cache_->SetResolvedMethod(dex_method_idx, res_method);
     } else {
       // If a virtual or interface method wasn't found with the expected type, look in
       // the direct methods. This can happen when the wrong invoke type is used or when
@@ -3119,7 +3174,7 @@
    * rigorous check here (which is okay since we have to do it at runtime).
    */
   if (method_type != METHOD_STATIC) {
-    RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
+    const RegType& actual_arg_type = work_line_->GetInvocationThis(this, inst, is_range);
     if (actual_arg_type.IsConflict()) {  // GetInvocationThis failed.
       CHECK(have_pending_hard_failure_);
       return nullptr;
@@ -3140,7 +3195,7 @@
       }
     }
     if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
-      RegType* res_method_class;
+      const RegType* res_method_class;
       if (res_method != nullptr) {
         mirror::Class* klass = res_method->GetDeclaringClass();
         std::string temp;
@@ -3149,7 +3204,7 @@
       } else {
         const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
         const uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
-        res_method_class = &reg_types_.FromDescriptor(class_loader_->Get(),
+        res_method_class = &reg_types_.FromDescriptor(GetClassLoader(),
                                                       dex_file_->StringByTypeIdx(class_idx),
                                                       false);
       }
@@ -3182,18 +3237,17 @@
       return nullptr;
     }
 
-    RegType& reg_type = reg_types_.FromDescriptor(class_loader_->Get(), param_descriptor,
-                                                        false);
+    const RegType& reg_type = reg_types_.FromDescriptor(GetClassLoader(), param_descriptor, false);
     uint32_t get_reg = is_range ? inst->VRegC_3rc() + static_cast<uint32_t>(sig_registers) :
         arg[sig_registers];
     if (reg_type.IsIntegralTypes()) {
-      RegType& src_type = work_line_->GetRegisterType(get_reg);
+      const RegType& src_type = work_line_->GetRegisterType(this, get_reg);
       if (!src_type.IsIntegralTypes()) {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << get_reg << " has type " << src_type
             << " but expected " << reg_type;
         return res_method;
       }
-    } else if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
+    } else if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
       // Continue on soft failures. We need to find possible hard failures to avoid problems in the
       // compiler.
       if (have_pending_hard_failure_) {
@@ -3270,7 +3324,7 @@
   // has a vtable entry for the target method.
   if (is_super) {
     DCHECK(method_type == METHOD_VIRTUAL);
-    RegType& super = GetDeclaringClass().GetSuperClass(&reg_types_);
+    const RegType& super = GetDeclaringClass().GetSuperClass(&reg_types_);
     if (super.IsUnresolvedTypes()) {
       Fail(VERIFY_ERROR_NO_METHOD) << "unknown super class in invoke-super from "
                                    << PrettyMethod(dex_method_idx_, *dex_file_)
@@ -3298,7 +3352,7 @@
                                                          RegisterLine* reg_line, bool is_range) {
   DCHECK(inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK ||
          inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK);
-  RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range);
+  const RegType& actual_arg_type = reg_line->GetInvocationThis(this, inst, is_range);
   if (!actual_arg_type.HasClass()) {
     VLOG(verifier) << "Failed to get mirror::Class* from '" << actual_arg_type << "'";
     return nullptr;
@@ -3316,9 +3370,10 @@
   CHECK(dispatch_class->HasVTable()) << PrettyDescriptor(dispatch_class);
   uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
   CHECK_LT(static_cast<int32_t>(vtable_index), dispatch_class->GetVTableLength())
-      << PrettyDescriptor(klass);
+      << PrettyDescriptor(klass) << " in method "
+      << PrettyMethod(dex_method_idx_, *dex_file_, true);
   mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index);
-  CHECK(!Thread::Current()->IsExceptionPending());
+  CHECK(!self_->IsExceptionPending());
   return res_method;
 }
 
@@ -3336,7 +3391,7 @@
   // We use vAA as our expected arg count, rather than res_method->insSize, because we need to
   // match the call to the signature. Also, we might be calling through an abstract method
   // definition (which doesn't have register count values).
-  RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
+  const RegType& actual_arg_type = work_line_->GetInvocationThis(this, inst, is_range);
   if (actual_arg_type.IsConflict()) {  // GetInvocationThis failed.
     return nullptr;
   }
@@ -3361,7 +3416,7 @@
   if (!actual_arg_type.IsZero()) {
     mirror::Class* klass = res_method->GetDeclaringClass();
     std::string temp;
-    RegType& res_method_class =
+    const RegType& res_method_class =
         reg_types_.FromClass(klass->GetDescriptor(&temp), klass,
                              klass->CannotBeAssignedFromOtherTypes());
     if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
@@ -3397,9 +3452,9 @@
                                         << " missing signature component";
       return nullptr;
     }
-    RegType& reg_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+    const RegType& reg_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
     uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args];
-    if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
+    if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
       return res_method;
     }
     actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1;
@@ -3425,7 +3480,7 @@
     DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE);
     type_idx = inst->VRegB_3rc();
   }
-  RegType& res_type = ResolveClassAndCheckAccess(type_idx);
+  const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
   if (res_type.IsConflict()) {  // bad class
     DCHECK_NE(failures_.size(), 0U);
   } else {
@@ -3434,14 +3489,14 @@
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "new-array on non-array class " << res_type;
     } else if (!is_filled) {
       /* make sure "size" register is valid type */
-      work_line_->VerifyRegisterType(inst->VRegB_22c(), reg_types_.Integer());
+      work_line_->VerifyRegisterType(this, inst->VRegB_22c(), reg_types_.Integer());
       /* set register type to array class */
-      RegType& precise_type = reg_types_.FromUninitialized(res_type);
-      work_line_->SetRegisterType(inst->VRegA_22c(), precise_type);
+      const RegType& precise_type = reg_types_.FromUninitialized(res_type);
+      work_line_->SetRegisterType(this, inst->VRegA_22c(), precise_type);
     } else {
       // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of
       // the list and fail. It's legal, if silly, for arg_count to be zero.
-      RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_->Get());
+      const RegType& expected_type = reg_types_.GetComponentType(res_type, GetClassLoader());
       uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
       uint32_t arg[5];
       if (!is_range) {
@@ -3449,41 +3504,42 @@
       }
       for (size_t ui = 0; ui < arg_count; ui++) {
         uint32_t get_reg = is_range ? inst->VRegC_3rc() + ui : arg[ui];
-        if (!work_line_->VerifyRegisterType(get_reg, expected_type)) {
-          work_line_->SetResultRegisterType(reg_types_.Conflict());
+        if (!work_line_->VerifyRegisterType(this, get_reg, expected_type)) {
+          work_line_->SetResultRegisterType(this, reg_types_.Conflict());
           return;
         }
       }
       // filled-array result goes into "result" register
-      RegType& precise_type = reg_types_.FromUninitialized(res_type);
-      work_line_->SetResultRegisterType(precise_type);
+      const RegType& precise_type = reg_types_.FromUninitialized(res_type);
+      work_line_->SetResultRegisterType(this, precise_type);
     }
   }
 }
 
 void MethodVerifier::VerifyAGet(const Instruction* inst,
-                                RegType& insn_type, bool is_primitive) {
-  RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x());
+                                const RegType& insn_type, bool is_primitive) {
+  const RegType& index_type = work_line_->GetRegisterType(this, inst->VRegC_23x());
   if (!index_type.IsArrayIndexTypes()) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
   } else {
-    RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x());
+    const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x());
     if (array_type.IsZero()) {
       // Null array class; this code path will fail at runtime. Infer a merge-able type from the
       // instruction type. TODO: have a proper notion of bottom here.
       if (!is_primitive || insn_type.IsCategory1Types()) {
         // Reference or category 1
-        work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Zero());
+        work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Zero());
       } else {
         // Category 2
-        work_line_->SetRegisterTypeWide(inst->VRegA_23x(), reg_types_.FromCat2ConstLo(0, false),
+        work_line_->SetRegisterTypeWide(this, inst->VRegA_23x(),
+                                        reg_types_.FromCat2ConstLo(0, false),
                                         reg_types_.FromCat2ConstHi(0, false));
       }
     } else if (!array_type.IsArrayTypes()) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
     } else {
       /* verify the class */
-      RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->Get());
+      const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
       if (!component_type.IsReferenceTypes() && !is_primitive) {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
             << " source for aget-object";
@@ -3500,9 +3556,9 @@
         // instruction, which can't differentiate object types and ints from floats, longs from
         // doubles.
         if (!component_type.IsLowHalf()) {
-          work_line_->SetRegisterType(inst->VRegA_23x(), component_type);
+          work_line_->SetRegisterType(this, inst->VRegA_23x(), component_type);
         } else {
-          work_line_->SetRegisterTypeWide(inst->VRegA_23x(), component_type,
+          work_line_->SetRegisterTypeWide(this, inst->VRegA_23x(), component_type,
                                           component_type.HighHalf(&reg_types_));
         }
       }
@@ -3510,12 +3566,12 @@
   }
 }
 
-void MethodVerifier::VerifyPrimitivePut(RegType& target_type, RegType& insn_type,
+void MethodVerifier::VerifyPrimitivePut(const RegType& target_type, const RegType& insn_type,
                                         const uint32_t vregA) {
   // Primitive assignability rules are weaker than regular assignability rules.
   bool instruction_compatible;
   bool value_compatible;
-  RegType& value_type = work_line_->GetRegisterType(vregA);
+  const RegType& value_type = work_line_->GetRegisterType(this, vregA);
   if (target_type.IsIntegralTypes()) {
     instruction_compatible = target_type.Equals(insn_type);
     value_compatible = value_type.IsIntegralTypes();
@@ -3527,7 +3583,7 @@
     // Additional register check: this is not checked statically (as part of VerifyInstructions),
     // as target_type depends on the resolved type of the field.
     if (instruction_compatible && work_line_->NumRegs() > vregA + 1) {
-      RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+      const RegType& value_type_hi = work_line_->GetRegisterType(this, vregA + 1);
       value_compatible = value_type.IsLongTypes() && value_type.CheckWidePair(value_type_hi);
     } else {
       value_compatible = false;
@@ -3537,7 +3593,7 @@
     // Additional register check: this is not checked statically (as part of VerifyInstructions),
     // as target_type depends on the resolved type of the field.
     if (instruction_compatible && work_line_->NumRegs() > vregA + 1) {
-      RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+      const RegType& value_type_hi = work_line_->GetRegisterType(this, vregA + 1);
       value_compatible = value_type.IsDoubleTypes() && value_type.CheckWidePair(value_type_hi);
     } else {
       value_compatible = false;
@@ -3562,19 +3618,19 @@
 }
 
 void MethodVerifier::VerifyAPut(const Instruction* inst,
-                                RegType& insn_type, bool is_primitive) {
-  RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x());
+                                const RegType& insn_type, bool is_primitive) {
+  const RegType& index_type = work_line_->GetRegisterType(this, inst->VRegC_23x());
   if (!index_type.IsArrayIndexTypes()) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
   } else {
-    RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x());
+    const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x());
     if (array_type.IsZero()) {
       // Null array type; this code path will fail at runtime. Infer a merge-able type from the
       // instruction type.
     } else if (!array_type.IsArrayTypes()) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
     } else {
-      RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->Get());
+      const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
       const uint32_t vregA = inst->VRegA_23x();
       if (is_primitive) {
         VerifyPrimitivePut(component_type, insn_type, vregA);
@@ -3586,7 +3642,7 @@
           // The instruction agrees with the type of array, confirm the value to be stored does too
           // Note: we use the instruction type (rather than the component type) for aput-object as
           // incompatible classes will be caught at runtime as an array store exception
-          work_line_->VerifyRegisterType(vregA, insn_type);
+          work_line_->VerifyRegisterType(this, vregA, insn_type);
         }
       }
     }
@@ -3596,7 +3652,7 @@
 mirror::ArtField* MethodVerifier::GetStaticField(int field_idx) {
   const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
   // Check access to class
-  RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
+  const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
   if (klass_type.IsConflict()) {  // bad class
     AppendToLastFailMessage(StringPrintf(" in attempt to access static field %d (%s) in %s",
                                          field_idx, dex_file_->GetFieldName(field_id),
@@ -3607,14 +3663,14 @@
     return nullptr;  // Can't resolve Class so no more to do here, will do checking at runtime.
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_,
-                                                          *class_loader_);
+  mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_,
+                                                          class_loader_);
   if (field == nullptr) {
     VLOG(verifier) << "Unable to resolve static field " << field_idx << " ("
               << dex_file_->GetFieldName(field_id) << ") in "
               << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
-    DCHECK(Thread::Current()->IsExceptionPending());
-    Thread::Current()->ClearException();
+    DCHECK(self_->IsExceptionPending());
+    self_->ClearException();
     return nullptr;
   } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
                                                   field->GetAccessFlags())) {
@@ -3628,10 +3684,10 @@
   return field;
 }
 
-mirror::ArtField* MethodVerifier::GetInstanceField(RegType& obj_type, int field_idx) {
+mirror::ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) {
   const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
   // Check access to class
-  RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
+  const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
   if (klass_type.IsConflict()) {
     AppendToLastFailMessage(StringPrintf(" in attempt to access instance field %d (%s) in %s",
                                          field_idx, dex_file_->GetFieldName(field_id),
@@ -3642,14 +3698,14 @@
     return nullptr;  // Can't resolve Class so no more to do here
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_,
-                                                          *class_loader_);
+  mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_,
+                                                          class_loader_);
   if (field == nullptr) {
     VLOG(verifier) << "Unable to resolve instance field " << field_idx << " ("
               << dex_file_->GetFieldName(field_id) << ") in "
               << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
-    DCHECK(Thread::Current()->IsExceptionPending());
-    Thread::Current()->ClearException();
+    DCHECK(self_->IsExceptionPending());
+    self_->ClearException();
     return nullptr;
   } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
                                                   field->GetAccessFlags())) {
@@ -3670,7 +3726,7 @@
     return nullptr;
   } else {
     mirror::Class* klass = field->GetDeclaringClass();
-    RegType& field_klass =
+    const RegType& field_klass =
         reg_types_.FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id),
                              klass, klass->CannotBeAssignedFromOtherTypes());
     if (obj_type.IsUninitializedTypes() &&
@@ -3695,22 +3751,21 @@
   }
 }
 
-void MethodVerifier::VerifyISGet(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_type,
                                  bool is_primitive, bool is_static) {
   uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
   mirror::ArtField* field;
   if (is_static) {
     field = GetStaticField(field_idx);
   } else {
-    RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+    const RegType& object_type = work_line_->GetRegisterType(this, inst->VRegB_22c());
     field = GetInstanceField(object_type, field_idx);
   }
-  RegType* field_type = nullptr;
+  const RegType* field_type = nullptr;
   if (field != nullptr) {
-    Thread* self = Thread::Current();
     mirror::Class* field_type_class;
     {
-      StackHandleScope<1> hs(self);
+      StackHandleScope<1> hs(self_);
       HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
       field_type_class = FieldHelper(h_field).GetType(can_load_classes_);
     }
@@ -3718,14 +3773,14 @@
       field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
                                          field_type_class->CannotBeAssignedFromOtherTypes());
     } else {
-      DCHECK(!can_load_classes_ || self->IsExceptionPending());
-      self->ClearException();
+      DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+      self_->ClearException();
     }
   }
   if (field_type == nullptr) {
     const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
     const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
-    field_type = &reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+    field_type = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
   }
   DCHECK(field_type != nullptr);
   const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
@@ -3750,28 +3805,28 @@
                                         << " to be compatible with type '" << insn_type
                                         << "' but found type '" << *field_type
                                         << "' in Get-object";
-      work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+      work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
       return;
     }
   }
   if (!field_type->IsLowHalf()) {
-    work_line_->SetRegisterType(vregA, *field_type);
+    work_line_->SetRegisterType(this, vregA, *field_type);
   } else {
-    work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
+    work_line_->SetRegisterTypeWide(this, vregA, *field_type, field_type->HighHalf(&reg_types_));
   }
 }
 
-void MethodVerifier::VerifyISPut(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_type,
                                  bool is_primitive, bool is_static) {
   uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
   mirror::ArtField* field;
   if (is_static) {
     field = GetStaticField(field_idx);
   } else {
-    RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+    const RegType& object_type = work_line_->GetRegisterType(this, inst->VRegB_22c());
     field = GetInstanceField(object_type, field_idx);
   }
-  RegType* field_type = nullptr;
+  const RegType* field_type = nullptr;
   if (field != nullptr) {
     if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
       Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
@@ -3780,7 +3835,7 @@
     }
     mirror::Class* field_type_class;
     {
-      StackHandleScope<1> hs(Thread::Current());
+      StackHandleScope<1> hs(self_);
       HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
       FieldHelper fh(h_field);
       field_type_class = fh.GetType(can_load_classes_);
@@ -3789,15 +3844,14 @@
       field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
                                          field_type_class->CannotBeAssignedFromOtherTypes());
     } else {
-      Thread* self = Thread::Current();
-      DCHECK(!can_load_classes_ || self->IsExceptionPending());
-      self->ClearException();
+      DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+      self_->ClearException();
     }
   }
   if (field_type == nullptr) {
     const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
     const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
-    field_type = &reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+    field_type = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
   }
   DCHECK(field_type != nullptr);
   const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
@@ -3811,7 +3865,7 @@
                                         << "' in put-object";
       return;
     }
-    work_line_->VerifyRegisterType(vregA, *field_type);
+    work_line_->VerifyRegisterType(this, vregA, *field_type);
   }
 }
 
@@ -3822,8 +3876,12 @@
          inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
          inst->Opcode() == Instruction::IPUT_QUICK ||
          inst->Opcode() == Instruction::IPUT_WIDE_QUICK ||
-         inst->Opcode() == Instruction::IPUT_OBJECT_QUICK);
-  RegType& object_type = reg_line->GetRegisterType(inst->VRegB_22c());
+         inst->Opcode() == Instruction::IPUT_OBJECT_QUICK ||
+         inst->Opcode() == Instruction::IPUT_BOOLEAN_QUICK ||
+         inst->Opcode() == Instruction::IPUT_BYTE_QUICK ||
+         inst->Opcode() == Instruction::IPUT_CHAR_QUICK ||
+         inst->Opcode() == Instruction::IPUT_SHORT_QUICK);
+  const RegType& object_type = reg_line->GetRegisterType(this, inst->VRegB_22c());
   if (!object_type.HasClass()) {
     VLOG(verifier) << "Failed to get mirror::Class* from '" << object_type << "'";
     return nullptr;
@@ -3838,7 +3896,7 @@
   return f;
 }
 
-void MethodVerifier::VerifyIGetQuick(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
                                      bool is_primitive) {
   DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
   mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get());
@@ -3848,19 +3906,18 @@
   }
   mirror::Class* field_type_class;
   {
-    StackHandleScope<1> hs(Thread::Current());
+    StackHandleScope<1> hs(self_);
     HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
     FieldHelper fh(h_field);
     field_type_class = fh.GetType(can_load_classes_);
   }
-  RegType* field_type;
+  const RegType* field_type;
   if (field_type_class != nullptr) {
     field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
                                        field_type_class->CannotBeAssignedFromOtherTypes());
   } else {
-    Thread* self = Thread::Current();
-    DCHECK(!can_load_classes_ || self->IsExceptionPending());
-    self->ClearException();
+    DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+    self_->ClearException();
     field_type = &reg_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(),
                                             field->GetTypeDescriptor(), false);
   }
@@ -3887,18 +3944,18 @@
                                         << " to be compatible with type '" << insn_type
                                         << "' but found type '" << *field_type
                                         << "' in get-object";
-      work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+      work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
       return;
     }
   }
   if (!field_type->IsLowHalf()) {
-    work_line_->SetRegisterType(vregA, *field_type);
+    work_line_->SetRegisterType(this, vregA, *field_type);
   } else {
-    work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
+    work_line_->SetRegisterTypeWide(this, vregA, *field_type, field_type->HighHalf(&reg_types_));
   }
 }
 
-void MethodVerifier::VerifyIPutQuick(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
                                      bool is_primitive) {
   DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
   mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get());
@@ -3908,7 +3965,7 @@
   }
   const char* descriptor = field->GetTypeDescriptor();
   mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
-  RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
   if (field != nullptr) {
     if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
       Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
@@ -3921,7 +3978,7 @@
     // Primitive field assignability rules are weaker than regular assignability rules
     bool instruction_compatible;
     bool value_compatible;
-    RegType& value_type = work_line_->GetRegisterType(vregA);
+    const RegType& value_type = work_line_->GetRegisterType(this, vregA);
     if (field_type.IsIntegralTypes()) {
       instruction_compatible = insn_type.IsIntegralTypes();
       value_compatible = value_type.IsIntegralTypes();
@@ -3963,7 +4020,7 @@
                                         << "' in put-object";
       return;
     }
-    work_line_->VerifyRegisterType(vregA, field_type);
+    work_line_->VerifyRegisterType(this, vregA, field_type);
   }
 }
 
@@ -3975,6 +4032,19 @@
   return true;
 }
 
+bool MethodVerifier::CheckNotMoveResult(const uint16_t* insns, int insn_idx) {
+  if (((insns[insn_idx] & 0xff) >= Instruction::MOVE_RESULT) &&
+      ((insns[insn_idx] & 0xff) <= Instruction::MOVE_RESULT_OBJECT)) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-result*";
+    return false;
+  }
+  return true;
+}
+
+bool MethodVerifier::CheckNotMoveExceptionOrMoveResult(const uint16_t* insns, int insn_idx) {
+  return (CheckNotMoveException(insns, insn_idx) && CheckNotMoveResult(insns, insn_idx));
+}
+
 bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_line,
                                      bool update_merge_line) {
   bool changed = true;
@@ -3989,7 +4059,7 @@
       target_line->CopyFromLine(merge_line);
     } else {
       // Verify that the monitor stack is empty on return.
-      if (!merge_line->VerifyMonitorStackEmpty()) {
+      if (!merge_line->VerifyMonitorStackEmpty(this)) {
         return false;
       }
       // For returns we only care about the operand to the return, all other registers are dead.
@@ -3997,33 +4067,33 @@
       const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn);
       Instruction::Code opcode = ret_inst->Opcode();
       if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
-        target_line->MarkAllRegistersAsConflicts();
+        target_line->MarkAllRegistersAsConflicts(this);
       } else {
         target_line->CopyFromLine(merge_line);
         if (opcode == Instruction::RETURN_WIDE) {
-          target_line->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+          target_line->MarkAllRegistersAsConflictsExceptWide(this, ret_inst->VRegA_11x());
         } else {
-          target_line->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+          target_line->MarkAllRegistersAsConflictsExcept(this, ret_inst->VRegA_11x());
         }
       }
     }
   } else {
     std::unique_ptr<RegisterLine> copy(gDebugVerify ?
-                                           RegisterLine::Create(target_line->NumRegs(), this) :
-                                           nullptr);
+                                 RegisterLine::Create(target_line->NumRegs(), this) :
+                                 nullptr);
     if (gDebugVerify) {
       copy->CopyFromLine(target_line);
     }
-    changed = target_line->MergeRegisters(merge_line);
+    changed = target_line->MergeRegisters(this, merge_line);
     if (have_pending_hard_failure_) {
       return false;
     }
     if (gDebugVerify && changed) {
       LogVerifyInfo() << "Merging at [" << reinterpret_cast<void*>(work_insn_idx_) << "]"
                       << " to [" << reinterpret_cast<void*>(next_insn) << "]: " << "\n"
-                      << *copy.get() << "  MERGE\n"
-                      << *merge_line << "  ==\n"
-                      << *target_line << "\n";
+                      << copy->Dump(this) << "  MERGE\n"
+                      << merge_line->Dump(this) << "  ==\n"
+                      << target_line->Dump(this) << "\n";
     }
     if (update_merge_line && changed) {
       merge_line->CopyFromLine(target_line);
@@ -4039,23 +4109,19 @@
   return &insn_flags_[work_insn_idx_];
 }
 
-RegType& MethodVerifier::GetMethodReturnType() {
+const RegType& MethodVerifier::GetMethodReturnType() {
   if (return_type_ == nullptr) {
-    if (mirror_method_ != nullptr) {
-      Thread* self = Thread::Current();
-      StackHandleScope<1> hs(self);
-      mirror::Class* return_type_class;
-      {
-        HandleWrapper<mirror::ArtMethod> h_mirror_method(hs.NewHandleWrapper(&mirror_method_));
-        return_type_class = MethodHelper(h_mirror_method).GetReturnType(can_load_classes_);
-      }
+    if (mirror_method_.Get() != nullptr) {
+      StackHandleScope<1> hs(self_);
+      mirror::Class* return_type_class =
+          MethodHelper(hs.NewHandle(mirror_method_.Get())).GetReturnType(can_load_classes_);
       if (return_type_class != nullptr) {
         return_type_ = &reg_types_.FromClass(mirror_method_->GetReturnTypeDescriptor(),
                                              return_type_class,
                                              return_type_class->CannotBeAssignedFromOtherTypes());
       } else {
-        DCHECK(!can_load_classes_ || self->IsExceptionPending());
-        self->ClearException();
+        DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+        self_->ClearException();
       }
     }
     if (return_type_ == nullptr) {
@@ -4063,23 +4129,23 @@
       const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
       uint16_t return_type_idx = proto_id.return_type_idx_;
       const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
-      return_type_ = &reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+      return_type_ = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
     }
   }
   return *return_type_;
 }
 
-RegType& MethodVerifier::GetDeclaringClass() {
+const RegType& MethodVerifier::GetDeclaringClass() {
   if (declaring_class_ == nullptr) {
     const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
     const char* descriptor
         = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
-    if (mirror_method_ != nullptr) {
+    if (mirror_method_.Get() != nullptr) {
       mirror::Class* klass = mirror_method_->GetDeclaringClass();
       declaring_class_ = &reg_types_.FromClass(descriptor, klass,
                                                klass->CannotBeAssignedFromOtherTypes());
     } else {
-      declaring_class_ = &reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+      declaring_class_ = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
     }
   }
   return *declaring_class_;
@@ -4090,16 +4156,19 @@
   DCHECK(line != nullptr) << "No register line at DEX pc " << StringPrintf("0x%x", dex_pc);
   std::vector<int32_t> result;
   for (size_t i = 0; i < line->NumRegs(); ++i) {
-    RegType& type = line->GetRegisterType(i);
+    const RegType& type = line->GetRegisterType(this, i);
     if (type.IsConstant()) {
       result.push_back(type.IsPreciseConstant() ? kConstant : kImpreciseConstant);
-      result.push_back(type.ConstantValue());
+      const ConstantType* const_val = down_cast<const ConstantType*>(&type);
+      result.push_back(const_val->ConstantValue());
     } else if (type.IsConstantLo()) {
       result.push_back(type.IsPreciseConstantLo() ? kConstant : kImpreciseConstant);
-      result.push_back(type.ConstantValueLo());
+      const ConstantType* const_val = down_cast<const ConstantType*>(&type);
+      result.push_back(const_val->ConstantValueLo());
     } else if (type.IsConstantHi()) {
       result.push_back(type.IsPreciseConstantHi() ? kConstant : kImpreciseConstant);
-      result.push_back(type.ConstantValueHi());
+      const ConstantType* const_val = down_cast<const ConstantType*>(&type);
+      result.push_back(const_val->ConstantValueHi());
     } else if (type.IsIntegralTypes()) {
       result.push_back(kIntVReg);
       result.push_back(0);
@@ -4130,7 +4199,7 @@
   return result;
 }
 
-RegType& MethodVerifier::DetermineCat1Constant(int32_t value, bool precise) {
+const RegType& MethodVerifier::DetermineCat1Constant(int32_t value, bool precise) {
   if (precise) {
     // Precise constant type.
     return reg_types_.FromCat1Const(value, true);
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 17713d2..9f5efe8 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -27,6 +27,7 @@
 #include "class_reference.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
+#include "handle.h"
 #include "instruction_flags.h"
 #include "method_reference.h"
 #include "reg_type.h"
@@ -139,21 +140,23 @@
   };
 
   /* Verify a class. Returns "kNoFailure" on success. */
-  static FailureKind VerifyClass(mirror::Class* klass, bool allow_soft_failures, std::string* error)
+  static FailureKind VerifyClass(Thread* self, mirror::Class* klass, bool allow_soft_failures,
+                                 std::string* error)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static FailureKind VerifyClass(const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+  static FailureKind VerifyClass(Thread* self, const DexFile* dex_file,
+                                 Handle<mirror::DexCache> dex_cache,
                                  Handle<mirror::ClassLoader> class_loader,
                                  const DexFile::ClassDef* class_def,
                                  bool allow_soft_failures, std::string* error)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static MethodVerifier* VerifyMethodAndDump(std::ostream& os, uint32_t method_idx,
+  static MethodVerifier* VerifyMethodAndDump(Thread* self, std::ostream& os, uint32_t method_idx,
                                              const DexFile* dex_file,
                                              Handle<mirror::DexCache> dex_cache,
                                              Handle<mirror::ClassLoader> class_loader,
                                              const DexFile::ClassDef* class_def,
                                              const DexFile::CodeItem* code_item,
-                                             mirror::ArtMethod* method,
+                                             Handle<mirror::ArtMethod> method,
                                              uint32_t method_access_flags)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -203,14 +206,15 @@
     return can_load_classes_;
   }
 
-  MethodVerifier(const DexFile* dex_file, Handle<mirror::DexCache>* dex_cache,
-                 Handle<mirror::ClassLoader>* class_loader, const DexFile::ClassDef* class_def,
-                 const DexFile::CodeItem* code_item, uint32_t method_idx, mirror::ArtMethod* method,
+  MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+                 Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
+                 const DexFile::CodeItem* code_item, uint32_t method_idx,
+                 Handle<mirror::ArtMethod> method,
                  uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
                  bool need_precise_constants) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : MethodVerifier(dex_file, dex_cache, class_loader, class_def, code_item, method_idx, method,
-                       access_flags, can_load_classes, allow_soft_failures, need_precise_constants,
-                       false) {}
+      : MethodVerifier(self, dex_file, dex_cache, class_loader, class_def, code_item, method_idx,
+                       method, access_flags, can_load_classes, allow_soft_failures,
+                       need_precise_constants, false) {}
 
   ~MethodVerifier();
 
@@ -236,16 +240,17 @@
   bool HasCheckCasts() const;
   bool HasVirtualOrInterfaceInvokes() const;
   bool HasFailures() const;
-  RegType& ResolveCheckedClass(uint32_t class_idx)
+  const RegType& ResolveCheckedClass(uint32_t class_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
   // Private constructor for dumping.
-  MethodVerifier(const DexFile* dex_file, Handle<mirror::DexCache>* dex_cache,
-                 Handle<mirror::ClassLoader>* class_loader, const DexFile::ClassDef* class_def,
-                 const DexFile::CodeItem* code_item, uint32_t method_idx, mirror::ArtMethod* method,
-                 uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
-                 bool need_precise_constants, bool verify_to_dump)
+  MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+                 Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
+                 const DexFile::CodeItem* code_item, uint32_t method_idx,
+                 Handle<mirror::ArtMethod> method, uint32_t access_flags,
+                 bool can_load_classes, bool allow_soft_failures, bool need_precise_constants,
+                 bool verify_to_dump)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Adds the given string to the beginning of the last failure message.
@@ -265,12 +270,12 @@
    *  (3) Iterate through the method, checking type safety and looking
    *      for code flow problems.
    */
-  static FailureKind VerifyMethod(uint32_t method_idx, const DexFile* dex_file,
+  static FailureKind VerifyMethod(Thread* self, uint32_t method_idx, const DexFile* dex_file,
                                   Handle<mirror::DexCache> dex_cache,
                                   Handle<mirror::ClassLoader> class_loader,
                                   const DexFile::ClassDef* class_def_idx,
                                   const DexFile::CodeItem* code_item,
-                                  mirror::ArtMethod* method, uint32_t method_access_flags,
+                                  Handle<mirror::ArtMethod> method, uint32_t method_access_flags,
                                   bool allow_soft_failures, bool need_precise_constants)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -485,34 +490,34 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Helper to perform verification on puts of primitive type.
-  void VerifyPrimitivePut(RegType& target_type, RegType& insn_type,
+  void VerifyPrimitivePut(const RegType& target_type, const RegType& insn_type,
                           const uint32_t vregA) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Perform verification of an aget instruction. The destination register's type will be set to
   // be that of component type of the array unless the array type is unknown, in which case a
   // bottom type inferred from the type of instruction is used. is_primitive is false for an
   // aget-object.
-  void VerifyAGet(const Instruction* inst, RegType& insn_type,
+  void VerifyAGet(const Instruction* inst, const RegType& insn_type,
                   bool is_primitive) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Perform verification of an aput instruction.
-  void VerifyAPut(const Instruction* inst, RegType& insn_type,
+  void VerifyAPut(const Instruction* inst, const RegType& insn_type,
                   bool is_primitive) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Lookup instance field and fail for resolution violations
-  mirror::ArtField* GetInstanceField(RegType& obj_type, int field_idx)
+  mirror::ArtField* GetInstanceField(const RegType& obj_type, int field_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Lookup static field and fail for resolution violations
   mirror::ArtField* GetStaticField(int field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Perform verification of an iget or sget instruction.
-  void VerifyISGet(const Instruction* inst, RegType& insn_type,
+  void VerifyISGet(const Instruction* inst, const RegType& insn_type,
                    bool is_primitive, bool is_static)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Perform verification of an iput or sput instruction.
-  void VerifyISPut(const Instruction* inst, RegType& insn_type,
+  void VerifyISPut(const Instruction* inst, const RegType& insn_type,
                    bool is_primitive, bool is_static)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -522,18 +527,18 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Perform verification of an iget-quick instruction.
-  void VerifyIGetQuick(const Instruction* inst, RegType& insn_type,
+  void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
                        bool is_primitive)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Perform verification of an iput-quick instruction.
-  void VerifyIPutQuick(const Instruction* inst, RegType& insn_type,
+  void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
                        bool is_primitive)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Resolves a class based on an index and performs access checks to ensure the referrer can
   // access the resolved class.
-  RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
+  const RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
@@ -541,7 +546,7 @@
    * address, determine the Join of all exceptions that can land here. Fails if no matching
    * exception handler can be found or if the Join of exception types fails.
    */
-  RegType& GetCaughtExceptionType()
+  const RegType& GetCaughtExceptionType()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
@@ -607,6 +612,21 @@
   bool CheckNotMoveException(const uint16_t* insns, int insn_idx);
 
   /*
+   * Verify that the target instruction is not "move-result". It is important that we cannot
+   * branch to move-result instructions, but we have to make this a distinct check instead of
+   * adding it to CheckNotMoveException, because it is legal to continue into "move-result"
+   * instructions - as long as the previous instruction was an invoke, which is checked elsewhere.
+   */
+  bool CheckNotMoveResult(const uint16_t* insns, int insn_idx);
+
+  /*
+   * Verify that the target instruction is not "move-result" or "move-exception". This is to
+   * be used when checking branch and switch instructions, but not instructions that can
+   * continue.
+   */
+  bool CheckNotMoveExceptionOrMoveResult(const uint16_t* insns, int insn_idx);
+
+  /*
   * Control can transfer to "next_insn". Merge the registers from merge_line into the table at
   * next_insn, and set the changed flag on the target address if any of the registers were changed.
   * In the case of fall-through, update the merge line on a change as its the working line for the
@@ -627,16 +647,19 @@
   }
 
   // Return the register type for the method.
-  RegType& GetMethodReturnType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const RegType& GetMethodReturnType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get a type representing the declaring class of the method.
-  RegType& GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const RegType& GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   InstructionFlags* CurrentInsnFlags();
 
-  RegType& DetermineCat1Constant(int32_t value, bool precise)
+  const RegType& DetermineCat1Constant(int32_t value, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // The thread we're verifying on.
+  Thread* const self_;
+
   RegTypeCache reg_types_;
 
   PcToRegisterLineTable reg_table_;
@@ -653,17 +676,17 @@
 
   const uint32_t dex_method_idx_;  // The method we're working on.
   // Its object representation if known.
-  mirror::ArtMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_);
+  Handle<mirror::ArtMethod> mirror_method_ GUARDED_BY(Locks::mutator_lock_);
   const uint32_t method_access_flags_;  // Method's access flags.
-  RegType* return_type_;  // Lazily computed return type of the method.
+  const RegType* return_type_;  // Lazily computed return type of the method.
   const DexFile* const dex_file_;  // The dex file containing the method.
   // The dex_cache for the declaring class of the method.
-  Handle<mirror::DexCache>* dex_cache_ GUARDED_BY(Locks::mutator_lock_);
+  Handle<mirror::DexCache> dex_cache_ GUARDED_BY(Locks::mutator_lock_);
   // The class loader for the declaring class of the method.
-  Handle<mirror::ClassLoader>* class_loader_ GUARDED_BY(Locks::mutator_lock_);
+  Handle<mirror::ClassLoader> class_loader_ GUARDED_BY(Locks::mutator_lock_);
   const DexFile::ClassDef* const class_def_;  // The class def of the declaring class of the method.
   const DexFile::CodeItem* const code_item_;  // The code item containing the code for the method.
-  RegType* declaring_class_;  // Lazily computed reg type of the method's declaring class.
+  const RegType* declaring_class_;  // Lazily computed reg type of the method's declaring class.
   // Instruction widths and flags, one entry per code unit.
   std::unique_ptr<InstructionFlags[]> insn_flags_;
   // The dex PC of a FindLocksAtDexPc request, -1 otherwise.
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index a5895e6..770ca7e 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -32,11 +32,12 @@
   void VerifyClass(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ASSERT_TRUE(descriptor != NULL);
-    mirror::Class* klass = class_linker_->FindSystemClass(Thread::Current(), descriptor.c_str());
+    Thread* self = Thread::Current();
+    mirror::Class* klass = class_linker_->FindSystemClass(self, descriptor.c_str());
 
     // Verify the class
     std::string error_msg;
-    ASSERT_TRUE(MethodVerifier::VerifyClass(klass, true, &error_msg) == MethodVerifier::kNoFailure)
+    ASSERT_TRUE(MethodVerifier::VerifyClass(self, klass, true, &error_msg) == MethodVerifier::kNoFailure)
         << error_msg;
   }
 
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
new file mode 100644
index 0000000..480ed40
--- /dev/null
+++ b/runtime/verifier/reg_type-inl.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_VERIFIER_REG_TYPE_INL_H_
+#define ART_RUNTIME_VERIFIER_REG_TYPE_INL_H_
+
+#include "reg_type.h"
+
+#include "base/casts.h"
+#include "mirror/class.h"
+
+namespace art {
+namespace verifier {
+
+inline bool RegType::CanAccess(const RegType& other) const {
+  if (Equals(other)) {
+    return true;  // Trivial accessibility.
+  } else {
+    bool this_unresolved = IsUnresolvedTypes();
+    bool other_unresolved = other.IsUnresolvedTypes();
+    if (!this_unresolved && !other_unresolved) {
+      return GetClass()->CanAccess(other.GetClass());
+    } else if (!other_unresolved) {
+      return other.GetClass()->IsPublic();  // Be conservative, only allow if other is public.
+    } else {
+      return false;  // More complicated test not possible on unresolved types, be conservative.
+    }
+  }
+}
+
+inline bool RegType::CanAccessMember(mirror::Class* klass, uint32_t access_flags) const {
+  if ((access_flags & kAccPublic) != 0) {
+    return true;
+  }
+  if (!IsUnresolvedTypes()) {
+    return GetClass()->CanAccessMember(klass, access_flags);
+  } else {
+    return false;  // More complicated test not possible on unresolved types, be conservative.
+  }
+}
+
+inline bool RegType::IsConstantBoolean() const {
+  if (!IsConstant()) {
+    return false;
+  } else {
+    const ConstantType* const_val = down_cast<const ConstantType*>(this);
+    return const_val->ConstantValue() >= 0 && const_val->ConstantValue() <= 1;
+  }
+}
+
+inline bool RegType::AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict) {
+  if (lhs.Equals(rhs)) {
+    return true;
+  } else {
+    if (lhs.IsBoolean()) {
+      return rhs.IsBooleanTypes();
+    } else if (lhs.IsByte()) {
+      return rhs.IsByteTypes();
+    } else if (lhs.IsShort()) {
+      return rhs.IsShortTypes();
+    } else if (lhs.IsChar()) {
+      return rhs.IsCharTypes();
+    } else if (lhs.IsInteger()) {
+      return rhs.IsIntegralTypes();
+    } else if (lhs.IsFloat()) {
+      return rhs.IsFloatTypes();
+    } else if (lhs.IsLongLo()) {
+      return rhs.IsLongTypes();
+    } else if (lhs.IsDoubleLo()) {
+      return rhs.IsDoubleTypes();
+    } else {
+      CHECK(lhs.IsReferenceTypes())
+          << "Unexpected register type in IsAssignableFrom: '"
+          << lhs << "' := '" << rhs << "'";
+      if (rhs.IsZero()) {
+        return true;  // All reference types can be assigned null.
+      } else if (!rhs.IsReferenceTypes()) {
+        return false;  // Expect rhs to be a reference type.
+      } else if (lhs.IsJavaLangObject()) {
+        return true;  // All reference types can be assigned to Object.
+      } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
+        // If we're not strict allow assignment to any interface, see comment in ClassJoin.
+        return true;
+      } else if (lhs.IsJavaLangObjectArray()) {
+        return rhs.IsObjectArrayTypes();  // All reference arrays may be assigned to Object[]
+      } else if (lhs.HasClass() && rhs.HasClass() &&
+                 lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) {
+        // We're assignable from the Class point-of-view.
+        return true;
+      } else {
+        // Unresolved types are only assignable for null and equality.
+        return false;
+      }
+    }
+  }
+}
+
+inline bool RegType::IsAssignableFrom(const RegType& src) const {
+  return AssignableFrom(*this, src, false);
+}
+
+inline bool RegType::IsStrictlyAssignableFrom(const RegType& src) const {
+  return AssignableFrom(*this, src, true);
+}
+
+inline const DoubleHiType* DoubleHiType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const DoubleLoType* DoubleLoType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const LongHiType* LongHiType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const LongLoType* LongLoType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const FloatType* FloatType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const CharType* CharType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const ShortType* ShortType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const ByteType* ByteType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+
+inline const IntegerType* IntegerType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const BooleanType* BooleanType::GetInstance() {
+  DCHECK(BooleanType::instance_ != nullptr);
+  return BooleanType::instance_;
+}
+
+inline const ConflictType* ConflictType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+inline const UndefinedType* UndefinedType::GetInstance() {
+  DCHECK(instance_ != nullptr);
+  return instance_;
+}
+
+}  // namespace verifier
+}  // namespace art
+
+#endif  // ART_RUNTIME_VERIFIER_REG_TYPE_INL_H_
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 30be82f..41541b5 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#include "reg_type.h"
-
+#include "reg_type-inl.h"
 
 #include "base/casts.h"
 #include "class_linker-inl.h"
@@ -33,41 +32,23 @@
 namespace art {
 namespace verifier {
 
-UndefinedType* UndefinedType::instance_ = NULL;
-ConflictType* ConflictType::instance_ = NULL;
-BooleanType* BooleanType::instance = NULL;
-ByteType* ByteType::instance_ = NULL;
-ShortType* ShortType::instance_ = NULL;
-CharType* CharType::instance_ = NULL;
-FloatType* FloatType::instance_ = NULL;
-LongLoType* LongLoType::instance_ = NULL;
-LongHiType* LongHiType::instance_ = NULL;
-DoubleLoType* DoubleLoType::instance_ = NULL;
-DoubleHiType* DoubleHiType::instance_ = NULL;
-IntegerType* IntegerType::instance_ = NULL;
-
-int32_t RegType::ConstantValue() const {
-  ScopedObjectAccess soa(Thread::Current());
-  LOG(FATAL) << "Unexpected call to ConstantValue: " << *this;
-  return 0;
-}
-
-int32_t RegType::ConstantValueLo() const {
-  ScopedObjectAccess soa(Thread::Current());
-  LOG(FATAL) << "Unexpected call to ConstantValueLo: " << *this;
-  return 0;
-}
-
-int32_t RegType::ConstantValueHi() const {
-  ScopedObjectAccess soa(Thread::Current());
-  LOG(FATAL) << "Unexpected call to ConstantValueHi: " << *this;
-  return 0;
-}
+const UndefinedType* UndefinedType::instance_ = nullptr;
+const ConflictType* ConflictType::instance_ = nullptr;
+const BooleanType* BooleanType::instance_ = nullptr;
+const ByteType* ByteType::instance_ = nullptr;
+const ShortType* ShortType::instance_ = nullptr;
+const CharType* CharType::instance_ = nullptr;
+const FloatType* FloatType::instance_ = nullptr;
+const LongLoType* LongLoType::instance_ = nullptr;
+const LongHiType* LongHiType::instance_ = nullptr;
+const DoubleLoType* DoubleLoType::instance_ = nullptr;
+const DoubleHiType* DoubleHiType::instance_ = nullptr;
+const IntegerType* IntegerType::instance_ = nullptr;
 
 PrimitiveType::PrimitiveType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
     : RegType(klass, descriptor, cache_id) {
-  CHECK(klass != NULL);
+  CHECK(klass != nullptr);
   CHECK(!descriptor.empty());
 }
 
@@ -81,7 +62,7 @@
     : PrimitiveType(klass, descriptor, cache_id) {
 }
 
-std::string PreciseConstType::Dump() {
+std::string PreciseConstType::Dump() const {
   std::stringstream result;
   uint32_t val = ConstantValue();
   if (val == 0) {
@@ -98,290 +79,223 @@
   return result.str();
 }
 
-std::string BooleanType::Dump() {
+std::string BooleanType::Dump() const {
   return "Boolean";
 }
 
-std::string ConflictType::Dump() {
+std::string ConflictType::Dump() const {
     return "Conflict";
 }
 
-std::string ByteType::Dump() {
+std::string ByteType::Dump() const {
   return "Byte";
 }
 
-std::string ShortType::Dump() {
+std::string ShortType::Dump() const {
   return "Short";
 }
 
-std::string CharType::Dump() {
+std::string CharType::Dump() const {
   return "Char";
 }
 
-std::string FloatType::Dump() {
+std::string FloatType::Dump() const {
   return "Float";
 }
 
-std::string LongLoType::Dump() {
+std::string LongLoType::Dump() const {
   return "Long (Low Half)";
 }
 
-std::string LongHiType::Dump() {
+std::string LongHiType::Dump() const {
   return "Long (High Half)";
 }
 
-std::string DoubleLoType::Dump() {
+std::string DoubleLoType::Dump() const {
   return "Double (Low Half)";
 }
 
-std::string DoubleHiType::Dump() {
+std::string DoubleHiType::Dump() const {
   return "Double (High Half)";
 }
 
-std::string IntegerType::Dump() {
+std::string IntegerType::Dump() const {
     return "Integer";
 }
 
-DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                           uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new DoubleHiType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-DoubleHiType* DoubleHiType::GetInstance() {
-  CHECK(instance_ != NULL);
+const DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass,
+                                                 const std::string& descriptor,
+                                                 uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new DoubleHiType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void DoubleHiType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                           uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new DoubleLoType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-DoubleLoType* DoubleLoType::GetInstance() {
-  CHECK(instance_ != NULL);
+const DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass,
+                                                 const std::string& descriptor,
+                                                 uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new DoubleLoType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void DoubleLoType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                       uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new LongLoType(klass, descriptor, cache_id);
-  }
+const LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+                                             uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new LongLoType(klass, descriptor, cache_id);
   return instance_;
 }
 
-LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                       uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new LongHiType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-LongHiType* LongHiType::GetInstance() {
-  CHECK(instance_ != NULL);
+const LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+                                             uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new LongHiType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void LongHiType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-LongLoType* LongLoType::GetInstance() {
-  CHECK(instance_ != NULL);
-  return instance_;
-}
-
 void LongLoType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-FloatType* FloatType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                     uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new FloatType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-FloatType* FloatType::GetInstance() {
-  CHECK(instance_ != NULL);
+const FloatType* FloatType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+                                           uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new FloatType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void FloatType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-CharType* CharType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                   uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new CharType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-CharType* CharType::GetInstance() {
-  CHECK(instance_ != NULL);
+const CharType* CharType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+                                         uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new CharType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void CharType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-ShortType* ShortType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                     uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new ShortType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-ShortType* ShortType::GetInstance() {
-  CHECK(instance_ != NULL);
+const ShortType* ShortType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+                                           uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new ShortType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void ShortType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-ByteType* ByteType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                   uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new ByteType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-ByteType* ByteType::GetInstance() {
-  CHECK(instance_ != NULL);
+const ByteType* ByteType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+                                         uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new ByteType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void ByteType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                         uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new IntegerType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-IntegerType* IntegerType::GetInstance() {
-  CHECK(instance_ != NULL);
+const IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+                                               uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new IntegerType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void IntegerType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-ConflictType* ConflictType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                           uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new ConflictType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-ConflictType* ConflictType::GetInstance() {
-  CHECK(instance_ != NULL);
+const ConflictType* ConflictType::CreateInstance(mirror::Class* klass,
+                                                 const std::string& descriptor,
+                                                 uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new ConflictType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void ConflictType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
-BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+const BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
                                          uint16_t cache_id) {
-  if (BooleanType::instance == NULL) {
-    instance = new BooleanType(klass, descriptor, cache_id);
-  }
-  return BooleanType::instance;
-}
-
-BooleanType* BooleanType::GetInstance() {
-  CHECK(BooleanType::instance != NULL);
-  return BooleanType::instance;
+  CHECK(BooleanType::instance_ == nullptr);
+  instance_ = new BooleanType(klass, descriptor, cache_id);
+  return BooleanType::instance_;
 }
 
 void BooleanType::Destroy() {
-  if (BooleanType::instance != NULL) {
-    delete instance;
-    instance = NULL;
+  if (BooleanType::instance_ != nullptr) {
+    delete instance_;
+    instance_ = nullptr;
   }
 }
 
-std::string UndefinedType::Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+std::string UndefinedType::Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return "Undefined";
 }
 
-UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                             uint16_t cache_id) {
-  if (instance_ == NULL) {
-    instance_ = new UndefinedType(klass, descriptor, cache_id);
-  }
-  return instance_;
-}
-
-UndefinedType* UndefinedType::GetInstance() {
-  CHECK(instance_ != NULL);
+const UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass,
+                                                   const std::string& descriptor,
+                                                   uint16_t cache_id) {
+  CHECK(instance_ == nullptr);
+  instance_ = new UndefinedType(klass, descriptor, cache_id);
   return instance_;
 }
 
 void UndefinedType::Destroy() {
-  if (instance_ != NULL) {
+  if (instance_ != nullptr) {
     delete instance_;
-    instance_ = NULL;
+    instance_ = nullptr;
   }
 }
 
@@ -391,7 +305,7 @@
   DCHECK(klass->IsInstantiable());
 }
 
-std::string UnresolvedMergedType::Dump() {
+std::string UnresolvedMergedType::Dump() const {
   std::stringstream result;
   std::set<uint16_t> types = GetMergedTypes();
   result << "UnresolvedMergedReferences(";
@@ -405,20 +319,20 @@
   return result.str();
 }
 
-std::string UnresolvedSuperClass::Dump() {
+std::string UnresolvedSuperClass::Dump() const {
   std::stringstream result;
   uint16_t super_type_id = GetUnresolvedSuperClassChildId();
   result << "UnresolvedSuperClass(" << reg_type_cache_->GetFromId(super_type_id).Dump() << ")";
   return result.str();
 }
 
-std::string UnresolvedReferenceType::Dump() {
+std::string UnresolvedReferenceType::Dump() const {
   std::stringstream result;
   result << "Unresolved Reference" << ": " << PrettyDescriptor(GetDescriptor().c_str());
   return result.str();
 }
 
-std::string UnresolvedUninitializedRefType::Dump() {
+std::string UnresolvedUninitializedRefType::Dump() const {
   std::stringstream result;
   result << "Unresolved And Uninitialized Reference" << ": "
       << PrettyDescriptor(GetDescriptor().c_str())
@@ -426,40 +340,40 @@
   return result.str();
 }
 
-std::string UnresolvedUninitializedThisRefType::Dump() {
+std::string UnresolvedUninitializedThisRefType::Dump() const {
   std::stringstream result;
   result << "Unresolved And Uninitialized This Reference"
       << PrettyDescriptor(GetDescriptor().c_str());
   return result.str();
 }
 
-std::string ReferenceType::Dump() {
+std::string ReferenceType::Dump() const {
   std::stringstream result;
   result << "Reference" << ": " << PrettyDescriptor(GetClass());
   return result.str();
 }
 
-std::string PreciseReferenceType::Dump() {
+std::string PreciseReferenceType::Dump() const {
   std::stringstream result;
   result << "Precise Reference" << ": "<< PrettyDescriptor(GetClass());
   return result.str();
 }
 
-std::string UninitializedReferenceType::Dump() {
+std::string UninitializedReferenceType::Dump() const {
   std::stringstream result;
   result << "Uninitialized Reference" << ": " << PrettyDescriptor(GetClass());
   result << " Allocation PC: " << GetAllocationPc();
   return result.str();
 }
 
-std::string UninitializedThisReferenceType::Dump() {
+std::string UninitializedThisReferenceType::Dump() const {
   std::stringstream result;
   result << "Uninitialized This Reference" << ": " << PrettyDescriptor(GetClass());
   result << "Allocation PC: " << GetAllocationPc();
   return result.str();
 }
 
-std::string ImpreciseConstType::Dump() {
+std::string ImpreciseConstType::Dump() const {
   std::stringstream result;
   uint32_t val = ConstantValue();
   if (val == 0) {
@@ -474,7 +388,7 @@
   }
   return result.str();
 }
-std::string PreciseConstLoType::Dump() {
+std::string PreciseConstLoType::Dump() const {
   std::stringstream result;
 
   int32_t val = ConstantValueLo();
@@ -488,7 +402,7 @@
   return result.str();
 }
 
-std::string ImpreciseConstLoType::Dump() {
+std::string ImpreciseConstLoType::Dump() const {
   std::stringstream result;
 
   int32_t val = ConstantValueLo();
@@ -502,7 +416,7 @@
   return result.str();
 }
 
-std::string PreciseConstHiType::Dump() {
+std::string PreciseConstHiType::Dump() const {
   std::stringstream result;
   int32_t val = ConstantValueHi();
   result << "Precise ";
@@ -515,7 +429,7 @@
   return result.str();
 }
 
-std::string ImpreciseConstHiType::Dump() {
+std::string ImpreciseConstHiType::Dump() const {
   std::stringstream result;
   int32_t val = ConstantValueHi();
   result << "Imprecise ";
@@ -528,19 +442,7 @@
   return result.str();
 }
 
-ConstantType::ConstantType(uint32_t constant, uint16_t cache_id)
-    : RegType(NULL, "", cache_id), constant_(constant) {
-}
-
-RegType& UndefinedType::Merge(RegType& incoming_type, RegTypeCache* reg_types)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (incoming_type.IsUndefined()) {
-    return *this;  // Undefined MERGE Undefined => Undefined
-  }
-  return reg_types->Conflict();
-}
-
-RegType& RegType::HighHalf(RegTypeCache* cache) const {
+const RegType& RegType::HighHalf(RegTypeCache* cache) const {
   DCHECK(IsLowHalf());
   if (IsLongLo()) {
     return cache->LongHi();
@@ -548,7 +450,8 @@
     return cache->DoubleHi();
   } else {
     DCHECK(IsImpreciseConstantLo());
-    return cache->FromCat2ConstHi(ConstantValue(), false);
+    const ConstantType* const_val = down_cast<const ConstantType*>(this);
+    return cache->FromCat2ConstHi(const_val->ConstantValue(), false);
   }
 }
 
@@ -586,22 +489,21 @@
 bool UnresolvedType::IsNonZeroReferenceTypes() const {
   return true;
 }
+
 std::set<uint16_t> UnresolvedMergedType::GetMergedTypes() const {
   std::pair<uint16_t, uint16_t> refs = GetTopMergedTypes();
-  RegType& _left(reg_type_cache_->GetFromId(refs.first));
-  UnresolvedMergedType* left = down_cast<UnresolvedMergedType*>(&_left);
-
-  RegType& _right(reg_type_cache_->GetFromId(refs.second));
-  UnresolvedMergedType* right = down_cast<UnresolvedMergedType*>(&_right);
+  const RegType& left = reg_type_cache_->GetFromId(refs.first);
+  const RegType& right = reg_type_cache_->GetFromId(refs.second);
 
   std::set<uint16_t> types;
-  if (left->IsUnresolvedMergedReference()) {
-    types = left->GetMergedTypes();
+  if (left.IsUnresolvedMergedReference()) {
+    types = down_cast<const UnresolvedMergedType*>(&left)->GetMergedTypes();
   } else {
     types.insert(refs.first);
   }
-  if (right->IsUnresolvedMergedReference()) {
-    std::set<uint16_t> right_types = right->GetMergedTypes();
+  if (right.IsUnresolvedMergedReference()) {
+    std::set<uint16_t> right_types =
+        down_cast<const UnresolvedMergedType*>(&right)->GetMergedTypes();
     types.insert(right_types.begin(), right_types.end());
   } else {
     types.insert(refs.second);
@@ -614,10 +516,10 @@
   return types;
 }
 
-RegType& RegType::GetSuperClass(RegTypeCache* cache) {
+const RegType& RegType::GetSuperClass(RegTypeCache* cache) const {
   if (!IsUnresolvedTypes()) {
     mirror::Class* super_klass = GetClass()->GetSuperClass();
-    if (super_klass != NULL) {
+    if (super_klass != nullptr) {
       // A super class of a precise type isn't precise as a precise type indicates the register
       // holds exactly that type.
       std::string temp;
@@ -636,34 +538,7 @@
   }
 }
 
-bool RegType::CanAccess(RegType& other) {
-  if (Equals(other)) {
-    return true;  // Trivial accessibility.
-  } else {
-    bool this_unresolved = IsUnresolvedTypes();
-    bool other_unresolved = other.IsUnresolvedTypes();
-    if (!this_unresolved && !other_unresolved) {
-      return GetClass()->CanAccess(other.GetClass());
-    } else if (!other_unresolved) {
-      return other.GetClass()->IsPublic();  // Be conservative, only allow if other is public.
-    } else {
-      return false;  // More complicated test not possible on unresolved types, be conservative.
-    }
-  }
-}
-
-bool RegType::CanAccessMember(mirror::Class* klass, uint32_t access_flags) {
-  if ((access_flags & kAccPublic) != 0) {
-    return true;
-  }
-  if (!IsUnresolvedTypes()) {
-    return GetClass()->CanAccessMember(klass, access_flags);
-  } else {
-    return false;  // More complicated test not possible on unresolved types, be conservative.
-  }
-}
-
-bool RegType::IsObjectArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+bool RegType::IsObjectArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
     // Primitive arrays will always resolve
     DCHECK(descriptor_[1] == 'L' || descriptor_[1] == '[');
@@ -676,11 +551,11 @@
   }
 }
 
-bool RegType::IsJavaLangObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+bool RegType::IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   return IsReference() && GetClass()->IsObjectClass();
 }
 
-bool RegType::IsArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+bool RegType::IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
     return descriptor_[0] == '[';
   } else if (HasClass()) {
@@ -690,7 +565,7 @@
   }
 }
 
-bool RegType::IsJavaLangObjectArray() {
+bool RegType::IsJavaLangObjectArray() const {
   if (HasClass()) {
     mirror::Class* type = GetClass();
     return type->IsArrayClass() && type->GetComponentType()->IsObjectClass();
@@ -698,110 +573,42 @@
   return false;
 }
 
-bool RegType::IsInstantiableTypes() {
+bool RegType::IsInstantiableTypes() const {
   return IsUnresolvedTypes() || (IsNonZeroReferenceTypes() && GetClass()->IsInstantiable());
 }
 
-ImpreciseConstType::ImpreciseConstType(uint32_t constat, uint16_t cache_id)
-  : ConstantType(constat, cache_id) {
-}
-
-static bool AssignableFrom(RegType& lhs, RegType& rhs, bool strict)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (lhs.Equals(rhs)) {
-    return true;
-  } else {
-    if (lhs.IsBoolean()) {
-      return rhs.IsBooleanTypes();
-    } else if (lhs.IsByte()) {
-      return rhs.IsByteTypes();
-    } else if (lhs.IsShort()) {
-      return rhs.IsShortTypes();
-    } else if (lhs.IsChar()) {
-      return rhs.IsCharTypes();
-    } else if (lhs.IsInteger()) {
-      return rhs.IsIntegralTypes();
-    } else if (lhs.IsFloat()) {
-      return rhs.IsFloatTypes();
-    } else if (lhs.IsLongLo()) {
-      return rhs.IsLongTypes();
-    } else if (lhs.IsDoubleLo()) {
-      return rhs.IsDoubleTypes();
-    } else {
-      CHECK(lhs.IsReferenceTypes())
-          << "Unexpected register type in IsAssignableFrom: '"
-          << lhs << "' := '" << rhs << "'";
-      if (rhs.IsZero()) {
-        return true;  // All reference types can be assigned null.
-      } else if (!rhs.IsReferenceTypes()) {
-        return false;  // Expect rhs to be a reference type.
-      } else if (lhs.IsJavaLangObject()) {
-        return true;  // All reference types can be assigned to Object.
-      } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
-        // If we're not strict allow assignment to any interface, see comment in ClassJoin.
-        return true;
-      } else if (lhs.IsJavaLangObjectArray()) {
-        return rhs.IsObjectArrayTypes();  // All reference arrays may be assigned to Object[]
-      } else if (lhs.HasClass() && rhs.HasClass() &&
-                 lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) {
-        // We're assignable from the Class point-of-view.
-        return true;
-      } else {
-        // Unresolved types are only assignable for null and equality.
-        return false;
-      }
-    }
-  }
-}
-
-bool RegType::IsAssignableFrom(RegType& src) {
-  return AssignableFrom(*this, src, false);
-}
-
-bool RegType::IsStrictlyAssignableFrom(RegType& src) {
-  return AssignableFrom(*this, src, true);
-}
-
-int32_t ConstantType::ConstantValueLo() const {
-  DCHECK(IsConstantLo());
-  return constant_;
-}
-
-int32_t ConstantType::ConstantValueHi() const {
-  if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) {
-    return constant_;
-  } else {
-    DCHECK(false);
-    return 0;
-  }
-}
-
-static RegType& SelectNonConstant(RegType& a, RegType& b) {
+static const RegType& SelectNonConstant(const RegType& a, const RegType& b) {
   return a.IsConstantTypes() ? b : a;
 }
 
-RegType& RegType::Merge(RegType& incoming_type, RegTypeCache* reg_types) {
+const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const {
   DCHECK(!Equals(incoming_type));  // Trivial equality handled by caller
-  if (IsConflict()) {
+  // Perform pointer equality tests for conflict to avoid virtual method dispatch.
+  const ConflictType& conflict = reg_types->Conflict();
+  if (this == &conflict) {
+    DCHECK(IsConflict());
     return *this;  // Conflict MERGE * => Conflict
-  } else if (incoming_type.IsConflict()) {
+  } else if (&incoming_type == &conflict) {
+    DCHECK(incoming_type.IsConflict());
     return incoming_type;  // * MERGE Conflict => Conflict
   } else if (IsUndefined() || incoming_type.IsUndefined()) {
-    return reg_types->Conflict();  // Unknown MERGE * => Conflict
+    return conflict;  // Unknown MERGE * => Conflict
   } else if (IsConstant() && incoming_type.IsConstant()) {
-    int32_t val1 = ConstantValue();
-    int32_t val2 = incoming_type.ConstantValue();
+    const ConstantType& type1 = *down_cast<const ConstantType*>(this);
+    const ConstantType& type2 = *down_cast<const ConstantType*>(&incoming_type);
+    int32_t val1 = type1.ConstantValue();
+    int32_t val2 = type2.ConstantValue();
     if (val1 >= 0 && val2 >= 0) {
       // +ve1 MERGE +ve2 => MAX(+ve1, +ve2)
       if (val1 >= val2) {
-        if (!IsPreciseConstant()) {
+        if (!type1.IsPreciseConstant()) {
           return *this;
         } else {
           return reg_types->FromCat1Const(val1, false);
         }
       } else {
-        if (!incoming_type.IsPreciseConstant()) {
-          return incoming_type;
+        if (!type2.IsPreciseConstant()) {
+          return type2;
         } else {
           return reg_types->FromCat1Const(val2, false);
         }
@@ -809,30 +616,30 @@
     } else if (val1 < 0 && val2 < 0) {
       // -ve1 MERGE -ve2 => MIN(-ve1, -ve2)
       if (val1 <= val2) {
-        if (!IsPreciseConstant()) {
+        if (!type1.IsPreciseConstant()) {
           return *this;
         } else {
           return reg_types->FromCat1Const(val1, false);
         }
       } else {
-        if (!incoming_type.IsPreciseConstant()) {
-          return incoming_type;
+        if (!type2.IsPreciseConstant()) {
+          return type2;
         } else {
           return reg_types->FromCat1Const(val2, false);
         }
       }
     } else {
       // Values are +ve and -ve, choose smallest signed type in which they both fit
-      if (IsConstantByte()) {
-        if (incoming_type.IsConstantByte()) {
+      if (type1.IsConstantByte()) {
+        if (type2.IsConstantByte()) {
           return reg_types->ByteConstant();
-        } else if (incoming_type.IsConstantShort()) {
+        } else if (type2.IsConstantShort()) {
           return reg_types->ShortConstant();
         } else {
           return reg_types->IntConstant();
         }
-      } else if (IsConstantShort()) {
-        if (incoming_type.IsConstantShort()) {
+      } else if (type1.IsConstantShort()) {
+        if (type2.IsConstantShort()) {
           return reg_types->ShortConstant();
         } else {
           return reg_types->IntConstant();
@@ -842,12 +649,16 @@
       }
     }
   } else if (IsConstantLo() && incoming_type.IsConstantLo()) {
-    int32_t val1 = ConstantValueLo();
-    int32_t val2 = incoming_type.ConstantValueLo();
+    const ConstantType& type1 = *down_cast<const ConstantType*>(this);
+    const ConstantType& type2 = *down_cast<const ConstantType*>(&incoming_type);
+    int32_t val1 = type1.ConstantValueLo();
+    int32_t val2 = type2.ConstantValueLo();
     return reg_types->FromCat2ConstLo(val1 | val2, false);
   } else if (IsConstantHi() && incoming_type.IsConstantHi()) {
-    int32_t val1 = ConstantValueHi();
-    int32_t val2 = incoming_type.ConstantValueHi();
+    const ConstantType& type1 = *down_cast<const ConstantType*>(this);
+    const ConstantType& type2 = *down_cast<const ConstantType*>(&incoming_type);
+    int32_t val1 = type1.ConstantValueHi();
+    int32_t val2 = type2.ConstantValueHi();
     return reg_types->FromCat2ConstHi(val1 | val2, false);
   } else if (IsIntegralTypes() && incoming_type.IsIntegralTypes()) {
     if (IsBooleanTypes() && incoming_type.IsBooleanTypes()) {
@@ -887,12 +698,12 @@
       // Something that is uninitialized hasn't had its constructor called. Mark any merge
       // of this type with something that is initialized as conflicting. The cases of a merge
       // with itself, 0 or Object are handled above.
-      return reg_types->Conflict();
+      return conflict;
     } else {  // Two reference types, compute Join
       mirror::Class* c1 = GetClass();
       mirror::Class* c2 = incoming_type.GetClass();
-      DCHECK(c1 != NULL && !c1->IsPrimitive());
-      DCHECK(c2 != NULL && !c2->IsPrimitive());
+      DCHECK(c1 != nullptr && !c1->IsPrimitive());
+      DCHECK(c2 != nullptr && !c2->IsPrimitive());
       mirror::Class* join_class = ClassJoin(c1, c2);
       if (c1 == join_class && !IsPreciseReference()) {
         return *this;
@@ -904,7 +715,7 @@
       }
     }
   } else {
-    return reg_types->Conflict();  // Unexpected types => Conflict
+    return conflict;  // Unexpected types => Conflict
   }
 }
 
@@ -931,7 +742,7 @@
     mirror::Class* common_elem = ClassJoin(s_ct, t_ct);
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), &common_elem);
-    DCHECK(array_class != NULL);
+    DCHECK(array_class != nullptr);
     return array_class;
   } else {
     size_t s_depth = s->Depth();
@@ -967,9 +778,9 @@
   }
 }
 
-void RegType::VisitRoots(RootCallback* callback, void* arg) {
+void RegType::VisitRoots(RootCallback* callback, void* arg) const {
   if (!klass_.IsNull()) {
-    klass_.VisitRoot(callback, arg, 0, kRootUnknown);
+    callback(reinterpret_cast<mirror::Object**>(&klass_), arg, 0, kRootUnknown);
   }
 }
 
@@ -1009,8 +820,7 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const RegType& rhs) {
-  RegType& rhs_non_const = const_cast<RegType&>(rhs);
-  os << rhs_non_const.Dump();
+  os << rhs.Dump();
   return os;
 }
 
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index d508fb5..34d6caa 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -25,6 +25,7 @@
 #include "jni.h"
 
 #include "base/macros.h"
+#include "base/mutex.h"
 #include "gc_root.h"
 #include "globals.h"
 #include "object_callbacks.h"
@@ -59,7 +60,9 @@
   virtual bool IsUninitializedReference() const { return false; }
   virtual bool IsUninitializedThisReference() const { return false; }
   virtual bool IsUnresolvedAndUninitializedReference() const { return false; }
-  virtual bool IsUnresolvedAndUninitializedThisReference() const { return false; }
+  virtual bool IsUnresolvedAndUninitializedThisReference() const {
+    return false;
+  }
   virtual bool IsUnresolvedMergedReference() const { return false; }
   virtual bool IsUnresolvedSuperClass() const { return false; }
   virtual bool IsReference() const { return false; }
@@ -72,90 +75,64 @@
   virtual bool IsImpreciseConstant() const { return false; }
   virtual bool IsConstantTypes() const { return false; }
   bool IsConstant() const {
-    return IsPreciseConstant() || IsImpreciseConstant();
+    return IsImpreciseConstant() || IsPreciseConstant();
   }
   bool IsConstantLo() const {
-    return IsPreciseConstantLo() || IsImpreciseConstantLo();
+    return IsImpreciseConstantLo() || IsPreciseConstantLo();
   }
   bool IsPrecise() const {
-    return IsPreciseConstantLo() || IsPreciseConstant() || IsPreciseConstantHi();
+    return IsPreciseConstantLo() || IsPreciseConstant() ||
+           IsPreciseConstantHi();
   }
-  bool IsLongConstant() const {
-    return IsConstantLo();
-  }
+  bool IsLongConstant() const { return IsConstantLo(); }
   bool IsConstantHi() const {
     return (IsPreciseConstantHi() || IsImpreciseConstantHi());
   }
-  bool IsLongConstantHigh() const {
-    return IsConstantHi();
-  }
+  bool IsLongConstantHigh() const { return IsConstantHi(); }
   virtual bool IsUninitializedTypes() const { return false; }
-  bool IsUnresolvedTypes() const {
-    return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() ||
-           IsUnresolvedAndUninitializedThisReference() ||
-           IsUnresolvedMergedReference() || IsUnresolvedSuperClass();
-  }
+  virtual bool IsUnresolvedTypes() const { return false; }
 
   bool IsLowHalf() const {
-    return (IsLongLo() || IsDoubleLo() || IsPreciseConstantLo() ||
-            IsImpreciseConstantLo());
+    return (IsLongLo() || IsDoubleLo() || IsPreciseConstantLo() || IsImpreciseConstantLo());
   }
   bool IsHighHalf() const {
-    return (IsLongHi() || IsDoubleHi() || IsPreciseConstantHi() ||
-            IsImpreciseConstantHi());
+    return (IsLongHi() || IsDoubleHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
   }
-  bool IsLongOrDoubleTypes() const {
-    return IsLowHalf();
-  }
+  bool IsLongOrDoubleTypes() const { return IsLowHalf(); }
   // Check this is the low half, and that type_h is its matching high-half.
-  inline bool CheckWidePair(RegType& type_h) const {
+  inline bool CheckWidePair(const RegType& type_h) const {
     if (IsLowHalf()) {
-      return ((IsPreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
-              (IsPreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
-              (IsImpreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
+      return ((IsImpreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
               (IsImpreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
+              (IsPreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
+              (IsPreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
               (IsDoubleLo() && type_h.IsDoubleHi()) ||
               (IsLongLo() && type_h.IsLongHi()));
     }
     return false;
   }
   // The high half that corresponds to this low half
-  RegType& HighHalf(RegTypeCache* cache) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const RegType& HighHalf(RegTypeCache* cache) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsConstantBoolean() const {
-    return IsConstant() && (ConstantValue() >= 0) && (ConstantValue() <= 1);
-  }
-  virtual bool IsConstantChar() const {
-    return false;
-  }
-  virtual bool IsConstantByte() const {
-    return false;
-  }
-  virtual bool IsConstantShort() const {
-    return false;
-  }
-  virtual bool IsOne() const {
-    return false;
-  }
-  virtual bool IsZero() const {
-    return false;
-  }
+  bool IsConstantBoolean() const;
+  virtual bool IsConstantChar() const { return false; }
+  virtual bool IsConstantByte() const { return false; }
+  virtual bool IsConstantShort() const { return false; }
+  virtual bool IsOne() const { return false; }
+  virtual bool IsZero() const { return false; }
   bool IsReferenceTypes() const {
     return IsNonZeroReferenceTypes() || IsZero();
   }
-  virtual bool IsNonZeroReferenceTypes() const {
-    return false;
-  }
+  virtual bool IsNonZeroReferenceTypes() const { return false; }
   bool IsCategory1Types() const {
-    return IsChar() || IsInteger() || IsFloat() || IsConstant() || IsByte() || IsShort() ||
-        IsBoolean();
+    return IsChar() || IsInteger() || IsFloat() || IsConstant() || IsByte() ||
+           IsShort() || IsBoolean();
   }
   bool IsCategory2Types() const {
     return IsLowHalf();  // Don't expect explicit testing of high halves
   }
-  bool IsBooleanTypes() const {
-    return IsBoolean() || IsConstantBoolean();
-  }
+  bool IsBooleanTypes() const { return IsBoolean() || IsConstantBoolean(); }
   bool IsByteTypes() const {
     return IsConstantByte() || IsByte() || IsBoolean();
   }
@@ -166,102 +143,107 @@
     return IsChar() || IsBooleanTypes() || IsConstantChar();
   }
   bool IsIntegralTypes() const {
-    return IsInteger() || IsConstant() || IsByte() || IsShort() || IsChar() || IsBoolean();
+    return IsInteger() || IsConstant() || IsByte() || IsShort() || IsChar() ||
+           IsBoolean();
   }
-  // Give the constant value encoded, but this shouldn't be called in the general case.
-  virtual int32_t ConstantValue() const;
-  virtual int32_t ConstantValueLo() const;
-  virtual int32_t ConstantValueHi() const;
-  bool IsArrayIndexTypes() const {
-    return IsIntegralTypes();
-  }
+  // Give the constant value encoded, but this shouldn't be called in the
+  // general case.
+  bool IsArrayIndexTypes() const { return IsIntegralTypes(); }
   // Float type may be derived from any constant type
-  bool IsFloatTypes() const {
-    return IsFloat() || IsConstant();
-  }
-  bool IsLongTypes() const {
-    return IsLongLo() || IsLongConstant();
-  }
+  bool IsFloatTypes() const { return IsFloat() || IsConstant(); }
+  bool IsLongTypes() const { return IsLongLo() || IsLongConstant(); }
   bool IsLongHighTypes() const {
-    return (IsLongHi() ||
-            IsPreciseConstantHi() ||
-            IsImpreciseConstantHi());
+    return (IsLongHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
   }
-  bool IsDoubleTypes() const {
-    return IsDoubleLo() || IsLongConstant();
-  }
+  bool IsDoubleTypes() const { return IsDoubleLo() || IsLongConstant(); }
   bool IsDoubleHighTypes() const {
     return (IsDoubleHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
   }
-  virtual bool IsLong() const {
-    return false;
+  virtual bool IsLong() const { return false; }
+  bool HasClass() const {
+    bool result = !klass_.IsNull();
+    DCHECK_EQ(result, HasClassVirtual());
+    return result;
   }
-  virtual bool HasClass() const {
-    return false;
-  }
-  bool IsJavaLangObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsObjectArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  virtual bool HasClassVirtual() const { return false; }
+  bool IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsObjectArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   Primitive::Type GetPrimitiveType() const;
-  bool IsJavaLangObjectArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsInstantiableTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsJavaLangObjectArray() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsInstantiableTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const std::string& GetDescriptor() const {
-    DCHECK(HasClass() || (IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
-                          !IsUnresolvedSuperClass()));
+    DCHECK(HasClass() ||
+           (IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
+            !IsUnresolvedSuperClass()));
     return descriptor_;
   }
-  mirror::Class* GetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::Class* GetClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(!IsUnresolvedReference());
     DCHECK(!klass_.IsNull()) << Dump();
     DCHECK(HasClass());
     return klass_.Read();
   }
-  uint16_t GetId() const {
-    return cache_id_;
-  }
-  RegType& GetSuperClass(RegTypeCache* cache)
+  uint16_t GetId() const { return cache_id_; }
+  const RegType& GetSuperClass(RegTypeCache* cache) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  virtual std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+  virtual std::string Dump() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
 
   // Can this type access other?
-  bool CanAccess(RegType& other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool CanAccess(const RegType& other) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Can this type access a member with the given properties?
-  bool CanAccessMember(mirror::Class* klass, uint32_t access_flags)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool CanAccessMember(mirror::Class* klass, uint32_t access_flags) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Can this type be assigned by src?
-  // Note: Object and interface types may always be assigned to one another, see comment on
+  // Note: Object and interface types may always be assigned to one another, see
+  // comment on
   // ClassJoin.
-  bool IsAssignableFrom(RegType& src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsAssignableFrom(const RegType& src) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't allow assignment to
+  // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't
+  // allow assignment to
   // an interface from an Object.
-  bool IsStrictlyAssignableFrom(RegType& src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsStrictlyAssignableFrom(const RegType& src) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Are these RegTypes the same?
-  bool Equals(RegType& other) const {
-    return GetId() == other.GetId();
-  }
+  bool Equals(const RegType& other) const { return GetId() == other.GetId(); }
 
-  // Compute the merge of this register from one edge (path) with incoming_type from another.
-  virtual RegType& Merge(RegType& incoming_type, RegTypeCache* reg_types)
+  // Compute the merge of this register from one edge (path) with incoming_type
+  // from another.
+  const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
-   * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
-   * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
-   * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
+   * A basic Join operation on classes. For a pair of types S and T the Join,
+   *written S v T = J, is
+   * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is
+   *J is the parent of
+   * S and T such that there isn't a parent of both S and T that isn't also the
+   *parent of J (ie J
    * is the deepest (lowest upper bound) parent of S and T).
    *
-   * This operation applies for regular classes and arrays, however, for interface types there
-   * needn't be a partial ordering on the types. We could solve the problem of a lack of a partial
-   * order by introducing sets of types, however, the only operation permissible on an interface is
-   * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface
-   * types until an invoke-interface call on the interface typed reference at runtime and allow
-   * the perversion of Object being assignable to an interface type (note, however, that we don't
-   * allow assignment of Object or Interface to any concrete class and are therefore type safe).
+   * This operation applies for regular classes and arrays, however, for
+   *interface types there
+   * needn't be a partial ordering on the types. We could solve the problem of a
+   *lack of a partial
+   * order by introducing sets of types, however, the only operation permissible
+   *on an interface is
+   * invoke-interface. In the tradition of Java verifiers [1] we defer the
+   *verification of interface
+   * types until an invoke-interface call on the interface typed reference at
+   *runtime and allow
+   * the perversion of Object being assignable to an interface type (note,
+   *however, that we don't
+   * allow assignment of Object or Interface to any concrete class and are
+   *therefore type safe).
    *
    * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
    */
@@ -270,12 +252,13 @@
 
   virtual ~RegType() {}
 
-  void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void VisitRoots(RootCallback* callback, void* arg) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  protected:
-  RegType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : descriptor_(descriptor), klass_(GcRoot<mirror::Class>(klass)), cache_id_(cache_id) {
+  RegType(mirror::Class* klass, const std::string& descriptor,
+          uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : descriptor_(descriptor), klass_(klass), cache_id_(cache_id) {
     if (kIsDebugBuild) {
       CheckInvariants();
     }
@@ -283,414 +266,404 @@
 
   void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-
   const std::string descriptor_;
-  GcRoot<mirror::Class> klass_;
+  mutable GcRoot<mirror::Class>
+      klass_;  // Non-const only due to moving classes.
   const uint16_t cache_id_;
 
   friend class RegTypeCache;
 
  private:
+  static bool AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   DISALLOW_COPY_AND_ASSIGN(RegType);
 };
 
 // Bottom type.
-class ConflictType : public RegType {
+class ConflictType FINAL : public RegType {
  public:
-  bool IsConflict() const {
-    return true;
-  }
+  bool IsConflict() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get the singleton Conflict instance.
-  static ConflictType* GetInstance();
+  static const ConflictType* GetInstance() PURE;
 
   // Create the singleton instance.
-  static ConflictType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                      uint16_t cache_id)
+  static const ConflictType* CreateInstance(mirror::Class* klass,
+                                            const std::string& descriptor,
+                                            uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Destroy the singleton instance.
   static void Destroy();
 
  private:
-  ConflictType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : RegType(klass, descriptor, cache_id) {
-  }
+  ConflictType(mirror::Class* klass, const std::string& descriptor,
+               uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : RegType(klass, descriptor, cache_id) {}
 
-  static ConflictType* instance_;
+  static const ConflictType* instance_;
 };
 
-// A variant of the bottom type used to specify an undefined value in the incoming registers.
+// A variant of the bottom type used to specify an undefined value in the
+// incoming registers.
 // Merging with UndefinedType yields ConflictType which is the true bottom.
-class UndefinedType : public RegType {
+class UndefinedType FINAL : public RegType {
  public:
-  bool IsUndefined() const {
-    return true;
-  }
+  bool IsUndefined() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get the singleton Undefined instance.
-  static UndefinedType* GetInstance();
+  static const UndefinedType* GetInstance() PURE;
 
   // Create the singleton instance.
-  static UndefinedType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                       uint16_t cache_id)
+  static const UndefinedType* CreateInstance(mirror::Class* klass,
+                                             const std::string& descriptor,
+                                             uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Destroy the singleton instance.
   static void Destroy();
 
  private:
-  UndefinedType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : RegType(klass, descriptor, cache_id) {
-  }
+  UndefinedType(mirror::Class* klass, const std::string& descriptor,
+                uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : RegType(klass, descriptor, cache_id) {}
 
-  virtual RegType& Merge(RegType& incoming_type, RegTypeCache* reg_types)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static UndefinedType* instance_;
+  static const UndefinedType* instance_;
 };
 
 class PrimitiveType : public RegType {
  public:
-  PrimitiveType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  PrimitiveType(mirror::Class* klass, const std::string& descriptor,
+                uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool HasClassVirtual() const OVERRIDE { return true; }
 };
 
 class Cat1Type : public PrimitiveType {
  public:
-  Cat1Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  Cat1Type(mirror::Class* klass, const std::string& descriptor,
+           uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
 class IntegerType : public Cat1Type {
  public:
-  bool IsInteger() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static IntegerType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                     uint16_t cache_id)
+  bool IsInteger() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static const IntegerType* CreateInstance(mirror::Class* klass,
+                                           const std::string& descriptor,
+                                           uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static IntegerType* GetInstance();
+  static const IntegerType* GetInstance() PURE;
   static void Destroy();
+
  private:
-  IntegerType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat1Type(klass, descriptor, cache_id) {
-  }
-  static IntegerType* instance_;
+  IntegerType(mirror::Class* klass, const std::string& descriptor,
+              uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat1Type(klass, descriptor, cache_id) {}
+  static const IntegerType* instance_;
 };
 
-class BooleanType : public Cat1Type {
+class BooleanType FINAL : public Cat1Type {
  public:
-  bool IsBoolean() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static BooleanType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                     uint16_t cache_id)
+  bool IsBoolean() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static const BooleanType* CreateInstance(mirror::Class* klass,
+                                           const std::string& descriptor,
+                                           uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static BooleanType* GetInstance();
+  static const BooleanType* GetInstance() PURE;
   static void Destroy();
- private:
-  BooleanType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat1Type(klass, descriptor, cache_id) {
-  }
 
-  static BooleanType* instance;
+ private:
+  BooleanType(mirror::Class* klass, const std::string& descriptor,
+              uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat1Type(klass, descriptor, cache_id) {}
+
+  static const BooleanType* instance_;
 };
 
-class ByteType : public Cat1Type {
+class ByteType FINAL : public Cat1Type {
  public:
-  bool IsByte() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static ByteType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                  uint16_t cache_id)
+  bool IsByte() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static const ByteType* CreateInstance(mirror::Class* klass,
+                                        const std::string& descriptor,
+                                        uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static ByteType* GetInstance();
+  static const ByteType* GetInstance() PURE;
   static void Destroy();
+
  private:
-  ByteType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat1Type(klass, descriptor, cache_id) {
-  }
-  static ByteType* instance_;
+  ByteType(mirror::Class* klass, const std::string& descriptor,
+           uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat1Type(klass, descriptor, cache_id) {}
+  static const ByteType* instance_;
 };
 
-class ShortType : public Cat1Type {
+class ShortType FINAL : public Cat1Type {
  public:
-  bool IsShort() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static ShortType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                   uint16_t cache_id)
+  bool IsShort() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static const ShortType* CreateInstance(mirror::Class* klass,
+                                         const std::string& descriptor,
+                                         uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static ShortType* GetInstance();
+  static const ShortType* GetInstance() PURE;
   static void Destroy();
+
  private:
-  ShortType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat1Type(klass, descriptor, cache_id) {
-  }
-  static ShortType* instance_;
+  ShortType(mirror::Class* klass, const std::string& descriptor,
+            uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat1Type(klass, descriptor, cache_id) {}
+  static const ShortType* instance_;
 };
 
-class CharType : public Cat1Type {
+class CharType FINAL : public Cat1Type {
  public:
-  bool IsChar() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static CharType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                  uint16_t cache_id)
+  bool IsChar() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static const CharType* CreateInstance(mirror::Class* klass,
+                                        const std::string& descriptor,
+                                        uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static CharType* GetInstance();
+  static const CharType* GetInstance() PURE;
   static void Destroy();
+
  private:
-  CharType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat1Type(klass, descriptor, cache_id) {
-  }
-  static CharType* instance_;
+  CharType(mirror::Class* klass, const std::string& descriptor,
+           uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat1Type(klass, descriptor, cache_id) {}
+  static const CharType* instance_;
 };
 
-class FloatType : public Cat1Type {
+class FloatType FINAL : public Cat1Type {
  public:
-  bool IsFloat() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static FloatType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                   uint16_t cache_id)
+  bool IsFloat() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static const FloatType* CreateInstance(mirror::Class* klass,
+                                         const std::string& descriptor,
+                                         uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static FloatType* GetInstance();
+  static const FloatType* GetInstance() PURE;
   static void Destroy();
+
  private:
-  FloatType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat1Type(klass, descriptor, cache_id) {
-  }
-  static FloatType* instance_;
+  FloatType(mirror::Class* klass, const std::string& descriptor,
+            uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat1Type(klass, descriptor, cache_id) {}
+  static const FloatType* instance_;
 };
 
 class Cat2Type : public PrimitiveType {
  public:
-  Cat2Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  Cat2Type(mirror::Class* klass, const std::string& descriptor,
+           uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-class LongLoType : public Cat2Type {
+class LongLoType FINAL : public Cat2Type {
  public:
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsLongLo() const {
-    return true;
-  }
-  bool IsLong() const {
-    return true;
-  }
-  static LongLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                    uint16_t cache_id)
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsLongLo() const OVERRIDE { return true; }
+  bool IsLong() const OVERRIDE { return true; }
+  static const LongLoType* CreateInstance(mirror::Class* klass,
+                                          const std::string& descriptor,
+                                          uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static LongLoType* GetInstance();
+  static const LongLoType* GetInstance() PURE;
   static void Destroy();
+
  private:
-  LongLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat2Type(klass, descriptor, cache_id) {
-  }
-  static LongLoType* instance_;
+  LongLoType(mirror::Class* klass, const std::string& descriptor,
+             uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat2Type(klass, descriptor, cache_id) {}
+  static const LongLoType* instance_;
 };
 
-class LongHiType : public Cat2Type {
+class LongHiType FINAL : public Cat2Type {
  public:
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsLongHi() const {
-    return true;
-  }
-  static LongHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                    uint16_t cache_id)
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsLongHi() const OVERRIDE { return true; }
+  static const LongHiType* CreateInstance(mirror::Class* klass,
+                                          const std::string& descriptor,
+                                          uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static LongHiType* GetInstance();
+  static const LongHiType* GetInstance() PURE;
   static void Destroy();
+
  private:
-  LongHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat2Type(klass, descriptor, cache_id) {
-  }
-  static LongHiType* instance_;
+  LongHiType(mirror::Class* klass, const std::string& descriptor,
+             uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat2Type(klass, descriptor, cache_id) {}
+  static const LongHiType* instance_;
 };
 
-class DoubleLoType : public Cat2Type {
+class DoubleLoType FINAL : public Cat2Type {
  public:
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsDoubleLo() const {
-    return true;
-  }
-  bool IsDouble() const {
-    return true;
-  }
-  static DoubleLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsDoubleLo() const OVERRIDE { return true; }
+  bool IsDouble() const OVERRIDE { return true; }
+  static const DoubleLoType* CreateInstance(mirror::Class* klass,
+                                            const std::string& descriptor,
+                                            uint16_t cache_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static const DoubleLoType* GetInstance() PURE;
+  static void Destroy();
+
+ private:
+  DoubleLoType(mirror::Class* klass, const std::string& descriptor,
+               uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat2Type(klass, descriptor, cache_id) {}
+  static const DoubleLoType* instance_;
+};
+
+class DoubleHiType FINAL : public Cat2Type {
+ public:
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  virtual bool IsDoubleHi() const OVERRIDE { return true; }
+  static const DoubleHiType* CreateInstance(mirror::Class* klass,
+                                      const std::string& descriptor,
                                       uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static DoubleLoType* GetInstance();
+  static const DoubleHiType* GetInstance() PURE;
   static void Destroy();
- private:
-  DoubleLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat2Type(klass, descriptor, cache_id) {
-  }
-  static DoubleLoType* instance_;
-};
 
-class DoubleHiType : public Cat2Type {
- public:
-  std::string Dump();
-  virtual bool IsDoubleHi() const {
-    return true;
-  }
-  static DoubleHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
-                                      uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static DoubleHiType* GetInstance();
-  static void Destroy();
  private:
-  DoubleHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : Cat2Type(klass, descriptor, cache_id) {
-  }
-  static DoubleHiType* instance_;
+  DoubleHiType(mirror::Class* klass, const std::string& descriptor,
+               uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : Cat2Type(klass, descriptor, cache_id) {}
+  static const DoubleHiType* instance_;
 };
 
 class ConstantType : public RegType {
  public:
-  ConstantType(uint32_t constat, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ConstantType(uint32_t constant, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : RegType(nullptr, "", cache_id), constant_(constant) {
+  }
 
-  // If this is a 32-bit constant, what is the value? This value may be imprecise in which case
-  // the value represents part of the integer range of values that may be held in the register.
+
+  // If this is a 32-bit constant, what is the value? This value may be
+  // imprecise in which case
+  // the value represents part of the integer range of values that may be held
+  // in the register.
   int32_t ConstantValue() const {
     DCHECK(IsConstantTypes());
     return constant_;
   }
-  int32_t ConstantValueLo() const;
-  int32_t ConstantValueHi() const;
 
-  bool IsZero() const {
+  int32_t ConstantValueLo() const {
+    DCHECK(IsConstantLo());
+    return constant_;
+  }
+
+  int32_t ConstantValueHi() const {
+    if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) {
+      return constant_;
+    } else {
+      DCHECK(false);
+      return 0;
+    }
+  }
+
+  bool IsZero() const OVERRIDE {
     return IsPreciseConstant() && ConstantValue() == 0;
   }
-  bool IsOne() const {
+  bool IsOne() const OVERRIDE {
     return IsPreciseConstant() && ConstantValue() == 1;
   }
 
-  bool IsConstantChar() const {
+  bool IsConstantChar() const OVERRIDE {
     return IsConstant() && ConstantValue() >= 0 &&
            ConstantValue() <= std::numeric_limits<jchar>::max();
   }
-  bool IsConstantByte() const {
+  bool IsConstantByte() const OVERRIDE {
     return IsConstant() &&
            ConstantValue() >= std::numeric_limits<jbyte>::min() &&
            ConstantValue() <= std::numeric_limits<jbyte>::max();
   }
-  bool IsConstantShort() const {
+  bool IsConstantShort() const OVERRIDE {
     return IsConstant() &&
            ConstantValue() >= std::numeric_limits<jshort>::min() &&
            ConstantValue() <= std::numeric_limits<jshort>::max();
   }
-  virtual bool IsConstantTypes() const { return true; }
+  virtual bool IsConstantTypes() const OVERRIDE { return true; }
 
  private:
   const uint32_t constant_;
 };
 
-class PreciseConstType : public ConstantType {
+class PreciseConstType FINAL : public ConstantType {
  public:
-  PreciseConstType(uint32_t constat, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : ConstantType(constat, cache_id) {
-  }
+  PreciseConstType(uint32_t constant, uint16_t cache_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : ConstantType(constant, cache_id) {}
 
-  bool IsPreciseConstant() const {
-    return true;
-  }
+  bool IsPreciseConstant() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-class PreciseConstLoType : public ConstantType {
+class PreciseConstLoType FINAL : public ConstantType {
  public:
-  PreciseConstLoType(uint32_t constat, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : ConstantType(constat, cache_id) {
-  }
-  bool IsPreciseConstantLo() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  PreciseConstLoType(uint32_t constant, uint16_t cache_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : ConstantType(constant, cache_id) {}
+  bool IsPreciseConstantLo() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-class PreciseConstHiType : public ConstantType {
+class PreciseConstHiType FINAL : public ConstantType {
  public:
-  PreciseConstHiType(uint32_t constat, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : ConstantType(constat, cache_id) {
-  }
-  bool IsPreciseConstantHi() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  PreciseConstHiType(uint32_t constant, uint16_t cache_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : ConstantType(constant, cache_id) {}
+  bool IsPreciseConstantHi() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-class ImpreciseConstType : public ConstantType {
+class ImpreciseConstType FINAL : public ConstantType {
  public:
   ImpreciseConstType(uint32_t constat, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool IsImpreciseConstant() const {
-    return true;
+       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+       : ConstantType(constat, cache_id) {
   }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsImpreciseConstant() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-class ImpreciseConstLoType : public ConstantType {
+class ImpreciseConstLoType FINAL : public ConstantType {
  public:
-  ImpreciseConstLoType(uint32_t constat, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : ConstantType(constat, cache_id) {
-  }
-  bool IsImpreciseConstantLo() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ImpreciseConstLoType(uint32_t constant, uint16_t cache_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : ConstantType(constant, cache_id) {}
+  bool IsImpreciseConstantLo() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-class ImpreciseConstHiType : public ConstantType {
+class ImpreciseConstHiType FINAL : public ConstantType {
  public:
-  ImpreciseConstHiType(uint32_t constat, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : ConstantType(constat, cache_id) {
-  }
-  bool IsImpreciseConstantHi() const {
-    return true;
-  }
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  ImpreciseConstHiType(uint32_t constant, uint16_t cache_id)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : ConstantType(constant, cache_id) {}
+  bool IsImpreciseConstantHi() const OVERRIDE { return true; }
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-// Common parent of all uninitialized types. Uninitialized types are created by "new" dex
+// Common parent of all uninitialized types. Uninitialized types are created by
+// "new" dex
 // instructions and must be passed to a constructor.
 class UninitializedType : public RegType {
  public:
-  UninitializedType(mirror::Class* klass, const std::string& descriptor, uint32_t allocation_pc,
-                    uint16_t cache_id)
-      : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {
-  }
+  UninitializedType(mirror::Class* klass, const std::string& descriptor,
+                    uint32_t allocation_pc, uint16_t cache_id)
+      : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {}
 
-  bool IsUninitializedTypes() const;
-  bool IsNonZeroReferenceTypes() const;
+  bool IsUninitializedTypes() const OVERRIDE;
+  bool IsNonZeroReferenceTypes() const OVERRIDE;
 
   uint32_t GetAllocationPc() const {
     DCHECK(IsUninitializedTypes());
@@ -702,30 +675,27 @@
 };
 
 // Similar to ReferenceType but not yet having been passed to a constructor.
-class UninitializedReferenceType : public UninitializedType {
+class UninitializedReferenceType FINAL : public UninitializedType {
  public:
-  UninitializedReferenceType(mirror::Class* klass, const std::string& descriptor,
+  UninitializedReferenceType(mirror::Class* klass,
+                             const std::string& descriptor,
                              uint32_t allocation_pc, uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : UninitializedType(klass, descriptor, allocation_pc, cache_id) {
-  }
+      : UninitializedType(klass, descriptor, allocation_pc, cache_id) {}
 
-  bool IsUninitializedReference() const {
-    return true;
-  }
+  bool IsUninitializedReference() const OVERRIDE { return true; }
 
-  bool HasClass() const {
-    return true;
-  }
+  bool HasClassVirtual() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-// Similar to UnresolvedReferenceType but not yet having been passed to a constructor.
-class UnresolvedUninitializedRefType : public UninitializedType {
+// Similar to UnresolvedReferenceType but not yet having been passed to a
+// constructor.
+class UnresolvedUninitializedRefType FINAL : public UninitializedType {
  public:
-  UnresolvedUninitializedRefType(const std::string& descriptor, uint32_t allocation_pc,
-                                 uint16_t cache_id)
+  UnresolvedUninitializedRefType(const std::string& descriptor,
+                                 uint32_t allocation_pc, uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : UninitializedType(NULL, descriptor, allocation_pc, cache_id) {
     if (kIsDebugBuild) {
@@ -733,19 +703,22 @@
     }
   }
 
-  bool IsUnresolvedAndUninitializedReference() const {
-    return true;
-  }
+  bool IsUnresolvedAndUninitializedReference() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
   void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-// Similar to UninitializedReferenceType but special case for the this argument of a constructor.
-class UninitializedThisReferenceType : public UninitializedType {
+// Similar to UninitializedReferenceType but special case for the this argument
+// of a constructor.
+class UninitializedThisReferenceType FINAL : public UninitializedType {
  public:
-  UninitializedThisReferenceType(mirror::Class* klass, const std::string& descriptor,
+  UninitializedThisReferenceType(mirror::Class* klass,
+                                 const std::string& descriptor,
                                  uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : UninitializedType(klass, descriptor, 0, cache_id) {
@@ -754,23 +727,20 @@
     }
   }
 
-  virtual bool IsUninitializedThisReference() const {
-    return true;
-  }
+  virtual bool IsUninitializedThisReference() const OVERRIDE { return true; }
 
-  bool HasClass() const {
-    return true;
-  }
+  bool HasClassVirtual() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
   void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-class UnresolvedUninitializedThisRefType : public UninitializedType {
+class UnresolvedUninitializedThisRefType FINAL : public UninitializedType {
  public:
-  UnresolvedUninitializedThisRefType(const std::string& descriptor, uint16_t cache_id)
+  UnresolvedUninitializedThisRefType(const std::string& descriptor,
+                                     uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : UninitializedType(NULL, descriptor, 0, cache_id) {
     if (kIsDebugBuild) {
@@ -778,112 +748,108 @@
     }
   }
 
-  bool IsUnresolvedAndUninitializedThisReference() const {
-    return true;
-  }
+  bool IsUnresolvedAndUninitializedThisReference() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
   void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-// A type of register holding a reference to an Object of type GetClass or a sub-class.
-class ReferenceType : public RegType {
+// A type of register holding a reference to an Object of type GetClass or a
+// sub-class.
+class ReferenceType FINAL : public RegType {
  public:
-  ReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-     : RegType(klass, descriptor, cache_id) {
-  }
+  ReferenceType(mirror::Class* klass, const std::string& descriptor,
+                uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : RegType(klass, descriptor, cache_id) {}
 
-  bool IsReference() const {
-    return true;
-  }
+  bool IsReference() const OVERRIDE { return true; }
 
-  bool IsNonZeroReferenceTypes() const {
-    return true;
-  }
+  bool IsNonZeroReferenceTypes() const OVERRIDE { return true; }
 
-  bool HasClass() const {
-    return true;
-  }
+  bool HasClassVirtual() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
-// A type of register holding a reference to an Object of type GetClass and only an object of that
+// A type of register holding a reference to an Object of type GetClass and only
+// an object of that
 // type.
-class PreciseReferenceType : public RegType {
+class PreciseReferenceType FINAL : public RegType {
  public:
-  PreciseReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
+  PreciseReferenceType(mirror::Class* klass, const std::string& descriptor,
+                       uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsPreciseReference() const {
-    return true;
-  }
+  bool IsPreciseReference() const OVERRIDE { return true; }
 
-  bool IsNonZeroReferenceTypes() const {
-    return true;
-  }
+  bool IsNonZeroReferenceTypes() const OVERRIDE { return true; }
 
-  bool HasClass() const {
-    return true;
-  }
+  bool HasClassVirtual() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
 // Common parent of unresolved types.
 class UnresolvedType : public RegType {
  public:
   UnresolvedType(const std::string& descriptor, uint16_t cache_id)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : RegType(NULL, descriptor, cache_id) {
-  }
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : RegType(NULL, descriptor, cache_id) {}
 
-  bool IsNonZeroReferenceTypes() const;
+  bool IsNonZeroReferenceTypes() const OVERRIDE;
 };
 
-// Similar to ReferenceType except the Class couldn't be loaded. Assignability and other tests made
+// Similar to ReferenceType except the Class couldn't be loaded. Assignability
+// and other tests made
 // of this type must be conservative.
-class UnresolvedReferenceType : public UnresolvedType {
+class UnresolvedReferenceType FINAL : public UnresolvedType {
  public:
   UnresolvedReferenceType(const std::string& descriptor, uint16_t cache_id)
-     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : UnresolvedType(descriptor, cache_id) {
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : UnresolvedType(descriptor, cache_id) {
     if (kIsDebugBuild) {
       CheckInvariants();
     }
   }
 
-  bool IsUnresolvedReference() const {
-    return true;
-  }
+  bool IsUnresolvedReference() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
   void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 };
 
 // Type representing the super-class of an unresolved type.
-class UnresolvedSuperClass : public UnresolvedType {
+class UnresolvedSuperClass FINAL : public UnresolvedType {
  public:
-  UnresolvedSuperClass(uint16_t child_id, RegTypeCache* reg_type_cache, uint16_t cache_id)
+  UnresolvedSuperClass(uint16_t child_id, RegTypeCache* reg_type_cache,
+                       uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : UnresolvedType("", cache_id), unresolved_child_id_(child_id),
+      : UnresolvedType("", cache_id),
+        unresolved_child_id_(child_id),
         reg_type_cache_(reg_type_cache) {
     if (kIsDebugBuild) {
       CheckInvariants();
     }
   }
 
-  bool IsUnresolvedSuperClass() const {
-    return true;
-  }
+  bool IsUnresolvedSuperClass() const OVERRIDE { return true; }
+
+  bool IsUnresolvedTypes() const OVERRIDE { return true; }
 
   uint16_t GetUnresolvedSuperClassChildId() const {
     DCHECK(IsUnresolvedSuperClass());
     return static_cast<uint16_t>(unresolved_child_id_ & 0xFFFF);
   }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
   void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -892,14 +858,17 @@
   const RegTypeCache* const reg_type_cache_;
 };
 
-// A merge of two unresolved types. If the types were resolved this may be Conflict or another
+// A merge of two unresolved types. If the types were resolved this may be
+// Conflict or another
 // known ReferenceType.
-class UnresolvedMergedType : public UnresolvedType {
+class UnresolvedMergedType FINAL : public UnresolvedType {
  public:
-  UnresolvedMergedType(uint16_t left_id, uint16_t right_id, const RegTypeCache* reg_type_cache,
-                       uint16_t cache_id)
+  UnresolvedMergedType(uint16_t left_id, uint16_t right_id,
+                       const RegTypeCache* reg_type_cache, uint16_t cache_id)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : UnresolvedType("", cache_id), reg_type_cache_(reg_type_cache), merged_types_(left_id, right_id) {
+      : UnresolvedType("", cache_id),
+        reg_type_cache_(reg_type_cache),
+        merged_types_(left_id, right_id) {
     if (kIsDebugBuild) {
       CheckInvariants();
     }
@@ -914,11 +883,11 @@
   // The complete set of merged types.
   std::set<uint16_t> GetMergedTypes() const;
 
-  bool IsUnresolvedMergedReference() const {
-    return true;
-  }
+  bool IsUnresolvedMergedReference() const OVERRIDE { return true; }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+  std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
   void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h
index fdf96a8..9024a7d 100644
--- a/runtime/verifier/reg_type_cache-inl.h
+++ b/runtime/verifier/reg_type_cache-inl.h
@@ -17,21 +17,24 @@
 #ifndef ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
 #define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
 
+#include "class_linker.h"
+#include "mirror/class-inl.h"
+#include "mirror/string.h"
+#include "mirror/throwable.h"
 #include "reg_type.h"
 #include "reg_type_cache.h"
-#include "class_linker.h"
 
 namespace art {
 namespace verifier {
 
-inline RegType& RegTypeCache::GetFromId(uint16_t id) const {
+inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const {
   DCHECK_LT(id, entries_.size());
-  RegType* result = entries_[id];
+  const RegType* result = entries_[id];
   DCHECK(result != NULL);
   return *result;
 }
 
-inline ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
+inline const ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
   // We only expect 0 to be a precise constant.
   DCHECK(value != 0 || precise);
   if (precise && (value >= kMinSmallConstant) && (value <= kMaxSmallConstant)) {
@@ -40,6 +43,81 @@
   return FromCat1NonSmallConstant(value, precise);
 }
 
+inline const ImpreciseConstType& RegTypeCache::ByteConstant() {
+  const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+  DCHECK(result.IsImpreciseConstant());
+  return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::CharConstant() {
+  int32_t jchar_max = static_cast<int32_t>(std::numeric_limits<jchar>::max());
+  const ConstantType& result =  FromCat1Const(jchar_max, false);
+  DCHECK(result.IsImpreciseConstant());
+  return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::ShortConstant() {
+  const ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::min(), false);
+  DCHECK(result.IsImpreciseConstant());
+  return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::IntConstant() {
+  const ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
+  DCHECK(result.IsImpreciseConstant());
+  return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::PosByteConstant() {
+  const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::max(), false);
+  DCHECK(result.IsImpreciseConstant());
+  return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::PosShortConstant() {
+  const ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::max(), false);
+  DCHECK(result.IsImpreciseConstant());
+  return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const PreciseReferenceType& RegTypeCache::JavaLangClass() {
+  const RegType* result = &FromClass("Ljava/lang/Class;", mirror::Class::GetJavaLangClass(), true);
+  DCHECK(result->IsPreciseReference());
+  return *down_cast<const PreciseReferenceType*>(result);
+}
+
+inline const PreciseReferenceType& RegTypeCache::JavaLangString() {
+  // String is final and therefore always precise.
+  const RegType* result = &FromClass("Ljava/lang/String;", mirror::String::GetJavaLangString(),
+                                     true);
+  DCHECK(result->IsPreciseReference());
+  return *down_cast<const PreciseReferenceType*>(result);
+}
+
+inline const RegType&  RegTypeCache::JavaLangThrowable(bool precise) {
+  const RegType* result = &FromClass("Ljava/lang/Throwable;",
+                                     mirror::Throwable::GetJavaLangThrowable(), precise);
+  if (precise) {
+    DCHECK(result->IsPreciseReference());
+    return *down_cast<const PreciseReferenceType*>(result);
+  } else {
+    DCHECK(result->IsReference());
+    return *down_cast<const ReferenceType*>(result);
+  }
+}
+
+inline const RegType& RegTypeCache::JavaLangObject(bool precise) {
+  const RegType* result = &FromClass("Ljava/lang/Object;",
+                                     mirror::Class::GetJavaLangClass()->GetSuperClass(), precise);
+  if (precise) {
+    DCHECK(result->IsPreciseReference());
+    return *down_cast<const PreciseReferenceType*>(result);
+  } else {
+    DCHECK(result->IsReference());
+    return *down_cast<const ReferenceType*>(result);
+  }
+}
+
 }  // namespace verifier
 }  // namespace art
 #endif  // ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 63fb2d9..7c40945 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -21,15 +21,16 @@
 #include "dex_file-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
+#include "reg_type-inl.h"
 
 namespace art {
 namespace verifier {
 
 bool RegTypeCache::primitive_initialized_ = false;
 uint16_t RegTypeCache::primitive_count_ = 0;
-PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
+const PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
 
-static bool MatchingPrecisionForClass(RegType* entry, bool precise)
+static bool MatchingPrecisionForClass(const RegType* entry, bool precise)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (entry->IsPreciseReference() == precise) {
     // We were or weren't looking for a precise reference and we found what we need.
@@ -65,8 +66,8 @@
   DCHECK_EQ(entries_.size(), primitive_count_);
 }
 
-RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor,
-                                      bool precise) {
+const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor,
+                                            bool precise) {
   DCHECK(RegTypeCache::primitive_initialized_);
   if (descriptor[1] == '\0') {
     switch (descriptor[0]) {
@@ -95,10 +96,10 @@
   } else {
     return Conflict();
   }
-};
+}
 
-RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const {
-  CHECK(RegTypeCache::primitive_initialized_);
+const RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const {
+  DCHECK(RegTypeCache::primitive_initialized_);
   switch (prim_type) {
     case Primitive::kPrimBoolean:
       return *BooleanType::GetInstance();
@@ -123,7 +124,7 @@
 }
 
 bool RegTypeCache::MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise) {
-  RegType* entry = entries_[idx];
+  const RegType* entry = entries_[idx];
   if (descriptor != entry->descriptor_) {
     return false;
   }
@@ -143,11 +144,11 @@
   Thread* self = Thread::Current();
   StackHandleScope<1> hs(self);
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(loader));
-  mirror::Class* klass = NULL;
+  mirror::Class* klass = nullptr;
   if (can_load_classes_) {
     klass = class_linker->FindClass(self, descriptor, class_loader);
   } else {
-    klass = class_linker->LookupClass(descriptor, loader);
+    klass = class_linker->LookupClass(self, descriptor, loader);
     if (klass != nullptr && !klass->IsLoaded()) {
       // We found the class but without it being loaded its not safe for use.
       klass = nullptr;
@@ -156,8 +157,8 @@
   return klass;
 }
 
-RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descriptor,
-                            bool precise) {
+const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descriptor,
+                                  bool precise) {
   // Try looking up the class in the cache first. We use a StringPiece to avoid continual strlen
   // operations on the descriptor.
   StringPiece descriptor_sp(descriptor);
@@ -169,7 +170,7 @@
   // Class not found in the cache, will create a new type for that.
   // Try resolving class.
   mirror::Class* klass = ResolveClass(descriptor, loader);
-  if (klass != NULL) {
+  if (klass != nullptr) {
     // Class resolved, first look for the class in the list of entries
     // Class was not found, must create new type.
     // To pass the verification, the type should be imprecise,
@@ -210,7 +211,7 @@
   }
 }
 
-RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) {
+const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) {
   DCHECK(klass != nullptr);
   if (klass->IsPrimitive()) {
     // Note: precise isn't used for primitive classes. A char is assignable to an int. All
@@ -219,7 +220,7 @@
   } else {
     // Look for the reference in the list of entries to have.
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
-      RegType* cur_entry = entries_[i];
+      const RegType* cur_entry = entries_[i];
       if (cur_entry->klass_.Read() == klass && MatchingPrecisionForClass(cur_entry, precise)) {
         return *cur_entry;
       }
@@ -237,8 +238,8 @@
 }
 
 RegTypeCache::RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) {
-  if (kIsDebugBuild && can_load_classes) {
-    Thread::Current()->AssertThreadSuspensionIsAllowable();
+  if (kIsDebugBuild) {
+    Thread::Current()->AssertThreadSuspensionIsAllowable(gAborting == 0);
   }
   entries_.reserve(64);
   FillPrimitiveAndSmallConstantTypes();
@@ -251,7 +252,7 @@
     // All entries are from the global pool, nothing to delete.
     return;
   }
-  std::vector<RegType*>::iterator non_primitive_begin = entries_.begin();
+  std::vector<const RegType*>::iterator non_primitive_begin = entries_.begin();
   std::advance(non_primitive_begin, kNumPrimitivesAndSmallConstants);
   STLDeleteContainerPointers(non_primitive_begin, entries_.end());
 }
@@ -271,7 +272,7 @@
     DoubleLoType::Destroy();
     DoubleHiType::Destroy();
     for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
-      PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
+      const PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
       delete type;
       small_precise_constants_[value - kMinSmallConstant] = nullptr;
     }
@@ -281,14 +282,15 @@
 }
 
 template <class Type>
-Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
-  mirror::Class* klass = NULL;
+const Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
+  mirror::Class* klass = nullptr;
   // Try loading the class from linker.
   if (!descriptor.empty()) {
     klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(),
                                                                        descriptor.c_str());
+    DCHECK(klass != nullptr);
   }
-  Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
+  const Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
   RegTypeCache::primitive_count_++;
   return entry;
 }
@@ -313,25 +315,27 @@
   }
 }
 
-RegType& RegTypeCache::FromUnresolvedMerge(RegType& left, RegType& right) {
+const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) {
   std::set<uint16_t> types;
   if (left.IsUnresolvedMergedReference()) {
-    types = (down_cast<UnresolvedMergedType*>(&left))->GetMergedTypes();
+    RegType& non_const(const_cast<RegType&>(left));
+    types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
   } else {
     types.insert(left.GetId());
   }
   if (right.IsUnresolvedMergedReference()) {
-    std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&right))->GetMergedTypes();
+    RegType& non_const(const_cast<RegType&>(right));
+    std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
     types.insert(right_types.begin(), right_types.end());
   } else {
     types.insert(right.GetId());
   }
   // Check if entry already exists.
   for (size_t i = primitive_count_; i < entries_.size(); i++) {
-    RegType* cur_entry = entries_[i];
+    const RegType* cur_entry = entries_[i];
     if (cur_entry->IsUnresolvedMergedReference()) {
       std::set<uint16_t> cur_entry_types =
-          (down_cast<UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
+          (down_cast<const UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
       if (cur_entry_types == types) {
         return *cur_entry;
       }
@@ -348,13 +352,13 @@
   return *entry;
 }
 
-RegType& RegTypeCache::FromUnresolvedSuperClass(RegType& child) {
+const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) {
   // Check if entry already exists.
   for (size_t i = primitive_count_; i < entries_.size(); i++) {
-    RegType* cur_entry = entries_[i];
+    const RegType* cur_entry = entries_[i];
     if (cur_entry->IsUnresolvedSuperClass()) {
-      UnresolvedSuperClass* tmp_entry =
-          down_cast<UnresolvedSuperClass*>(cur_entry);
+      const UnresolvedSuperClass* tmp_entry =
+          down_cast<const UnresolvedSuperClass*>(cur_entry);
       uint16_t unresolved_super_child_id =
           tmp_entry->GetUnresolvedSuperClassChildId();
       if (unresolved_super_child_id == child.GetId()) {
@@ -367,28 +371,29 @@
   return *entry;
 }
 
-UninitializedType& RegTypeCache::Uninitialized(RegType& type, uint32_t allocation_pc) {
-  UninitializedType* entry = NULL;
+const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
+  UninitializedType* entry = nullptr;
   const std::string& descriptor(type.GetDescriptor());
   if (type.IsUnresolvedTypes()) {
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
-      RegType* cur_entry = entries_[i];
+      const RegType* cur_entry = entries_[i];
       if (cur_entry->IsUnresolvedAndUninitializedReference() &&
-          down_cast<UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc() == allocation_pc &&
+          down_cast<const UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc()
+              == allocation_pc &&
           (cur_entry->GetDescriptor() == descriptor)) {
-        return *down_cast<UnresolvedUninitializedRefType*>(cur_entry);
+        return *down_cast<const UnresolvedUninitializedRefType*>(cur_entry);
       }
     }
     entry = new UnresolvedUninitializedRefType(descriptor, allocation_pc, entries_.size());
   } else {
     mirror::Class* klass = type.GetClass();
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
-      RegType* cur_entry = entries_[i];
+      const RegType* cur_entry = entries_[i];
       if (cur_entry->IsUninitializedReference() &&
-          down_cast<UninitializedReferenceType*>(cur_entry)
+          down_cast<const UninitializedReferenceType*>(cur_entry)
               ->GetAllocationPc() == allocation_pc &&
           cur_entry->GetClass() == klass) {
-        return *down_cast<UninitializedReferenceType*>(cur_entry);
+        return *down_cast<const UninitializedReferenceType*>(cur_entry);
       }
     }
     entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size());
@@ -397,13 +402,13 @@
   return *entry;
 }
 
-RegType& RegTypeCache::FromUninitialized(RegType& uninit_type) {
+const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) {
   RegType* entry;
 
   if (uninit_type.IsUnresolvedTypes()) {
     const std::string& descriptor(uninit_type.GetDescriptor());
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
-      RegType* cur_entry = entries_[i];
+      const RegType* cur_entry = entries_[i];
       if (cur_entry->IsUnresolvedReference() &&
           cur_entry->GetDescriptor() == descriptor) {
         return *cur_entry;
@@ -415,7 +420,7 @@
     if (uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) {
       // For uninitialized "this reference" look for reference types that are not precise.
       for (size_t i = primitive_count_; i < entries_.size(); i++) {
-        RegType* cur_entry = entries_[i];
+        const RegType* cur_entry = entries_[i];
         if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
           return *cur_entry;
         }
@@ -425,7 +430,7 @@
       // We're uninitialized because of allocation, look or create a precise type as allocations
       // may only create objects of that type.
       for (size_t i = primitive_count_; i < entries_.size(); i++) {
-        RegType* cur_entry = entries_[i];
+        const RegType* cur_entry = entries_[i];
         if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) {
           return *cur_entry;
         }
@@ -439,61 +444,24 @@
   return *entry;
 }
 
-ImpreciseConstType& RegTypeCache::ByteConstant() {
-  ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
-  DCHECK(result.IsImpreciseConstant());
-  return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::CharConstant() {
-  int32_t jchar_max = static_cast<int32_t>(std::numeric_limits<jchar>::max());
-  ConstantType& result =  FromCat1Const(jchar_max, false);
-  DCHECK(result.IsImpreciseConstant());
-  return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::ShortConstant() {
-  ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::min(), false);
-  DCHECK(result.IsImpreciseConstant());
-  return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::IntConstant() {
-  ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
-  DCHECK(result.IsImpreciseConstant());
-  return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::PosByteConstant() {
-  ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::max(), false);
-  DCHECK(result.IsImpreciseConstant());
-  return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::PosShortConstant() {
-  ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::max(), false);
-  DCHECK(result.IsImpreciseConstant());
-  return *down_cast<ImpreciseConstType*>(&result);
-}
-
-UninitializedType& RegTypeCache::UninitializedThisArgument(RegType& type) {
+const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
   UninitializedType* entry;
   const std::string& descriptor(type.GetDescriptor());
   if (type.IsUnresolvedTypes()) {
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
-      RegType* cur_entry = entries_[i];
+      const RegType* cur_entry = entries_[i];
       if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
           cur_entry->GetDescriptor() == descriptor) {
-        return *down_cast<UninitializedType*>(cur_entry);
+        return *down_cast<const UninitializedType*>(cur_entry);
       }
     }
     entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size());
   } else {
     mirror::Class* klass = type.GetClass();
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
-      RegType* cur_entry = entries_[i];
+      const RegType* cur_entry = entries_[i];
       if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
-        return *down_cast<UninitializedType*>(cur_entry);
+        return *down_cast<const UninitializedType*>(cur_entry);
       }
     }
     entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size());
@@ -502,13 +470,13 @@
   return *entry;
 }
 
-ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) {
   for (size_t i = primitive_count_; i < entries_.size(); i++) {
-    RegType* cur_entry = entries_[i];
+    const RegType* cur_entry = entries_[i];
     if (cur_entry->klass_.IsNull() && cur_entry->IsConstant() &&
         cur_entry->IsPreciseConstant() == precise &&
-        (down_cast<ConstantType*>(cur_entry))->ConstantValue() == value) {
-      return *down_cast<ConstantType*>(cur_entry);
+        (down_cast<const ConstantType*>(cur_entry))->ConstantValue() == value) {
+      return *down_cast<const ConstantType*>(cur_entry);
     }
   }
   ConstantType* entry;
@@ -521,12 +489,12 @@
   return *entry;
 }
 
-ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
   for (size_t i = primitive_count_; i < entries_.size(); i++) {
-    RegType* cur_entry = entries_[i];
+    const RegType* cur_entry = entries_[i];
     if (cur_entry->IsConstantLo() && (cur_entry->IsPrecise() == precise) &&
-        (down_cast<ConstantType*>(cur_entry))->ConstantValueLo() == value) {
-      return *down_cast<ConstantType*>(cur_entry);
+        (down_cast<const ConstantType*>(cur_entry))->ConstantValueLo() == value) {
+      return *down_cast<const ConstantType*>(cur_entry);
     }
   }
   ConstantType* entry;
@@ -539,12 +507,12 @@
   return *entry;
 }
 
-ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
   for (size_t i = primitive_count_; i < entries_.size(); i++) {
-    RegType* cur_entry = entries_[i];
+    const RegType* cur_entry = entries_[i];
     if (cur_entry->IsConstantHi() && (cur_entry->IsPrecise() == precise) &&
-        (down_cast<ConstantType*>(cur_entry))->ConstantValueHi() == value) {
-      return *down_cast<ConstantType*>(cur_entry);
+        (down_cast<const ConstantType*>(cur_entry))->ConstantValueHi() == value) {
+      return *down_cast<const ConstantType*>(cur_entry);
     }
   }
   ConstantType* entry;
@@ -557,7 +525,7 @@
   return *entry;
 }
 
-RegType& RegTypeCache::GetComponentType(RegType& array, mirror::ClassLoader* loader) {
+const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) {
   if (!array.IsArrayTypes()) {
     return Conflict();
   } else if (array.IsUnresolvedTypes()) {
@@ -581,8 +549,8 @@
 
 void RegTypeCache::Dump(std::ostream& os) {
   for (size_t i = 0; i < entries_.size(); i++) {
-    RegType* cur_entry = entries_[i];
-    if (cur_entry != NULL) {
+    const RegType* cur_entry = entries_[i];
+    if (cur_entry != nullptr) {
       os << i << ": " << cur_entry->Dump() << "\n";
     }
   }
@@ -592,18 +560,18 @@
   // Visit the primitive types, this is required since if there are no active verifiers they wont
   // be in the entries array, and therefore not visited as roots.
   if (primitive_initialized_) {
-    Undefined().VisitRoots(callback, arg);
-    Conflict().VisitRoots(callback, arg);
-    Boolean().VisitRoots(callback, arg);
-    Byte().VisitRoots(callback, arg);
-    Short().VisitRoots(callback, arg);
-    Char().VisitRoots(callback, arg);
-    Integer().VisitRoots(callback, arg);
-    LongLo().VisitRoots(callback, arg);
-    LongHi().VisitRoots(callback, arg);
-    Float().VisitRoots(callback, arg);
-    DoubleLo().VisitRoots(callback, arg);
-    DoubleHi().VisitRoots(callback, arg);
+    UndefinedType::GetInstance()->VisitRoots(callback, arg);
+    ConflictType::GetInstance()->VisitRoots(callback, arg);
+    BooleanType::GetInstance()->VisitRoots(callback, arg);
+    ByteType::GetInstance()->VisitRoots(callback, arg);
+    ShortType::GetInstance()->VisitRoots(callback, arg);
+    CharType::GetInstance()->VisitRoots(callback, arg);
+    IntegerType::GetInstance()->VisitRoots(callback, arg);
+    LongLoType::GetInstance()->VisitRoots(callback, arg);
+    LongHiType::GetInstance()->VisitRoots(callback, arg);
+    FloatType::GetInstance()->VisitRoots(callback, arg);
+    DoubleLoType::GetInstance()->VisitRoots(callback, arg);
+    DoubleHiType::GetInstance()->VisitRoots(callback, arg);
     for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
       small_precise_constants_[value - kMinSmallConstant]->VisitRoots(callback, arg);
     }
@@ -611,7 +579,7 @@
 }
 
 void RegTypeCache::VisitRoots(RootCallback* callback, void* arg) {
-  for (RegType* entry : entries_) {
+  for (const RegType* entry : entries_) {
     entry->VisitRoots(callback, arg);
   }
 }
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index a75aaab..ff7b1f3 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -51,99 +51,91 @@
     }
   }
   static void ShutDown();
-  RegType& GetFromId(uint16_t id) const;
-  RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise)
+  const art::verifier::RegType& GetFromId(uint16_t id) const;
+  const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
+  const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ConstantType& FromCat1Const(int32_t value, bool precise)
+  const ConstantType& FromCat1Const(int32_t value, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ConstantType& FromCat2ConstLo(int32_t value, bool precise)
+  const ConstantType& FromCat2ConstLo(int32_t value, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ConstantType& FromCat2ConstHi(int32_t value, bool precise)
+  const ConstantType& FromCat2ConstHi(int32_t value, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise)
+  const RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& FromUnresolvedMerge(RegType& left, RegType& right)
+  const RegType& FromUnresolvedMerge(const RegType& left, const RegType& right)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& FromUnresolvedSuperClass(RegType& child)
+  const RegType& FromUnresolvedSuperClass(const RegType& child)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& JavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // String is final and therefore always precise.
-    return From(NULL, "Ljava/lang/String;", true);
-  }
-  RegType& JavaLangThrowable(bool precise)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return From(NULL, "Ljava/lang/Throwable;", precise);
-  }
-  ConstantType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const ConstantType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return FromCat1Const(0, true);
   }
-  ConstantType& One() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const ConstantType& One() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return FromCat1Const(1, true);
   }
   size_t GetCacheSize() {
     return entries_.size();
   }
-  static RegType& Boolean() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const BooleanType& Boolean() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *BooleanType::GetInstance();
   }
-  static RegType& Byte() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const ByteType& Byte() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *ByteType::GetInstance();
   }
-  static RegType& Char() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const CharType& Char() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *CharType::GetInstance();
   }
-  static RegType& Short() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const ShortType& Short() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *ShortType::GetInstance();
   }
-  static RegType& Integer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const IntegerType& Integer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *IntegerType::GetInstance();
   }
-  static RegType& Float() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const FloatType& Float() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *FloatType::GetInstance();
   }
-  static RegType& LongLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const LongLoType& LongLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *LongLoType::GetInstance();
   }
-  static RegType& LongHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const LongHiType& LongHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *LongHiType::GetInstance();
   }
-  static RegType& DoubleLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const DoubleLoType& DoubleLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *DoubleLoType::GetInstance();
   }
-  static RegType& DoubleHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const DoubleHiType& DoubleHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *DoubleHiType::GetInstance();
   }
-  static RegType& Undefined() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const UndefinedType& Undefined() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return *UndefinedType::GetInstance();
   }
-  static RegType& Conflict() {
+  const ConflictType& Conflict() {
     return *ConflictType::GetInstance();
   }
-  RegType& JavaLangClass(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return From(NULL, "Ljava/lang/Class;", precise);
-  }
-  RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return From(NULL, "Ljava/lang/Object;", precise);
-  }
-  UninitializedType& Uninitialized(RegType& type, uint32_t allocation_pc)
+
+  const PreciseReferenceType& JavaLangClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const PreciseReferenceType& JavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const RegType& JavaLangThrowable(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  const UninitializedType& Uninitialized(const RegType& type, uint32_t allocation_pc)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   // Create an uninitialized 'this' argument for the given type.
-  UninitializedType& UninitializedThisArgument(RegType& type)
+  const UninitializedType& UninitializedThisArgument(const RegType& type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& FromUninitialized(RegType& uninit_type)
+  const RegType& FromUninitialized(const RegType& uninit_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ImpreciseConstType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ImpreciseConstType& CharConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ImpreciseConstType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ImpreciseConstType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ImpreciseConstType& PosByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ImpreciseConstType& PosShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& GetComponentType(RegType& array, mirror::ClassLoader* loader)
+  const ImpreciseConstType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const ImpreciseConstType& CharConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const ImpreciseConstType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const ImpreciseConstType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const ImpreciseConstType& PosByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const ImpreciseConstType& PosShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const RegType& GetComponentType(const RegType& array, mirror::ClassLoader* loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  RegType& RegTypeFromPrimitiveType(Primitive::Type) const;
+  const RegType& RegTypeFromPrimitiveType(Primitive::Type) const;
 
   void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void VisitStaticRoots(RootCallback* callback, void* arg)
@@ -155,23 +147,20 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
+  const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void AddEntry(RegType* new_entry);
 
   template <class Type>
-  static Type* CreatePrimitiveTypeInstance(const std::string& descriptor)
+  static const Type* CreatePrimitiveTypeInstance(const std::string& descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void CreatePrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // The actual storage for the RegTypes.
-  std::vector<RegType*> entries_;
-
   // A quick look up for popular small constants.
   static constexpr int32_t kMinSmallConstant = -1;
   static constexpr int32_t kMaxSmallConstant = 4;
-  static PreciseConstType* small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
+  static const PreciseConstType* small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
 
   static constexpr size_t kNumPrimitivesAndSmallConstants =
       12 + (kMaxSmallConstant - kMinSmallConstant + 1);
@@ -182,6 +171,9 @@
   // Number of well known primitives that will be copied into a RegTypeCache upon construction.
   static uint16_t primitive_count_;
 
+  // The actual storage for the RegTypes.
+  std::vector<const RegType*> entries_;
+
   // Whether or not we're allowed to load classes.
   const bool can_load_classes_;
 
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index e27558a..2fecc8b 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -21,6 +21,7 @@
 #include "base/casts.h"
 #include "common_runtime_test.h"
 #include "reg_type_cache-inl.h"
+#include "reg_type-inl.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
 
@@ -33,21 +34,21 @@
   // Tests creating primitive types types.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
-  RegType& ref_type_const_0 = cache.FromCat1Const(10, true);
-  RegType& ref_type_const_1 = cache.FromCat1Const(10, true);
-  RegType& ref_type_const_2 = cache.FromCat1Const(30, true);
-  RegType& ref_type_const_3 = cache.FromCat1Const(30, false);
+  const RegType& ref_type_const_0 = cache.FromCat1Const(10, true);
+  const RegType& ref_type_const_1 = cache.FromCat1Const(10, true);
+  const RegType& ref_type_const_2 = cache.FromCat1Const(30, true);
+  const RegType& ref_type_const_3 = cache.FromCat1Const(30, false);
   EXPECT_TRUE(ref_type_const_0.Equals(ref_type_const_1));
   EXPECT_FALSE(ref_type_const_0.Equals(ref_type_const_2));
   EXPECT_FALSE(ref_type_const_0.Equals(ref_type_const_3));
 
-  RegType& ref_type_const_wide_0 = cache.FromCat2ConstHi(50, true);
-  RegType& ref_type_const_wide_1 = cache.FromCat2ConstHi(50, true);
+  const RegType& ref_type_const_wide_0 = cache.FromCat2ConstHi(50, true);
+  const RegType& ref_type_const_wide_1 = cache.FromCat2ConstHi(50, true);
   EXPECT_TRUE(ref_type_const_wide_0.Equals(ref_type_const_wide_1));
 
-  RegType& ref_type_const_wide_2 = cache.FromCat2ConstLo(50, true);
-  RegType& ref_type_const_wide_3 = cache.FromCat2ConstLo(50, true);
-  RegType& ref_type_const_wide_4 = cache.FromCat2ConstLo(55, true);
+  const RegType& ref_type_const_wide_2 = cache.FromCat2ConstLo(50, true);
+  const RegType& ref_type_const_wide_3 = cache.FromCat2ConstLo(50, true);
+  const RegType& ref_type_const_wide_4 = cache.FromCat2ConstLo(55, true);
   EXPECT_TRUE(ref_type_const_wide_2.Equals(ref_type_const_wide_3));
   EXPECT_FALSE(ref_type_const_wide_2.Equals(ref_type_const_wide_4));
 }
@@ -56,11 +57,11 @@
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
   int64_t val = static_cast<int32_t>(1234);
-  RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true);
-  RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
-  RegType& precise_const = cache.FromCat1Const(static_cast<int32_t>(val >> 32), true);
-  RegType& long_lo = cache.LongLo();
-  RegType& long_hi = cache.LongHi();
+  const RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true);
+  const RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+  const RegType& precise_const = cache.FromCat1Const(static_cast<int32_t>(val >> 32), true);
+  const RegType& long_lo = cache.LongLo();
+  const RegType& long_hi = cache.LongHi();
   // Check sanity of types.
   EXPECT_TRUE(precise_lo.IsLowHalf());
   EXPECT_FALSE(precise_hi.IsLowHalf());
@@ -80,7 +81,7 @@
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
 
-  RegType& bool_reg_type = cache.Boolean();
+  const RegType& bool_reg_type = cache.Boolean();
   EXPECT_FALSE(bool_reg_type.IsUndefined());
   EXPECT_FALSE(bool_reg_type.IsConflict());
   EXPECT_FALSE(bool_reg_type.IsZero());
@@ -111,8 +112,9 @@
   EXPECT_FALSE(bool_reg_type.IsDoubleTypes());
   EXPECT_TRUE(bool_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(bool_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(bool_reg_type.HasClass());
 
-  RegType& byte_reg_type = cache.Byte();
+  const RegType& byte_reg_type = cache.Byte();
   EXPECT_FALSE(byte_reg_type.IsUndefined());
   EXPECT_FALSE(byte_reg_type.IsConflict());
   EXPECT_FALSE(byte_reg_type.IsZero());
@@ -143,8 +145,9 @@
   EXPECT_FALSE(byte_reg_type.IsDoubleTypes());
   EXPECT_TRUE(byte_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(byte_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(byte_reg_type.HasClass());
 
-  RegType& char_reg_type = cache.Char();
+  const RegType& char_reg_type = cache.Char();
   EXPECT_FALSE(char_reg_type.IsUndefined());
   EXPECT_FALSE(char_reg_type.IsConflict());
   EXPECT_FALSE(char_reg_type.IsZero());
@@ -175,8 +178,9 @@
   EXPECT_FALSE(char_reg_type.IsDoubleTypes());
   EXPECT_TRUE(char_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(char_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(char_reg_type.HasClass());
 
-  RegType& short_reg_type = cache.Short();
+  const RegType& short_reg_type = cache.Short();
   EXPECT_FALSE(short_reg_type.IsUndefined());
   EXPECT_FALSE(short_reg_type.IsConflict());
   EXPECT_FALSE(short_reg_type.IsZero());
@@ -207,8 +211,9 @@
   EXPECT_FALSE(short_reg_type.IsDoubleTypes());
   EXPECT_TRUE(short_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(short_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(short_reg_type.HasClass());
 
-  RegType& int_reg_type = cache.Integer();
+  const RegType& int_reg_type = cache.Integer();
   EXPECT_FALSE(int_reg_type.IsUndefined());
   EXPECT_FALSE(int_reg_type.IsConflict());
   EXPECT_FALSE(int_reg_type.IsZero());
@@ -239,8 +244,9 @@
   EXPECT_FALSE(int_reg_type.IsDoubleTypes());
   EXPECT_TRUE(int_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(int_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(int_reg_type.HasClass());
 
-  RegType& long_reg_type = cache.LongLo();
+  const RegType& long_reg_type = cache.LongLo();
   EXPECT_FALSE(long_reg_type.IsUndefined());
   EXPECT_FALSE(long_reg_type.IsConflict());
   EXPECT_FALSE(long_reg_type.IsZero());
@@ -271,8 +277,9 @@
   EXPECT_FALSE(long_reg_type.IsDoubleTypes());
   EXPECT_FALSE(long_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(long_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(long_reg_type.HasClass());
 
-  RegType& float_reg_type = cache.Float();
+  const RegType& float_reg_type = cache.Float();
   EXPECT_FALSE(float_reg_type.IsUndefined());
   EXPECT_FALSE(float_reg_type.IsConflict());
   EXPECT_FALSE(float_reg_type.IsZero());
@@ -303,8 +310,9 @@
   EXPECT_FALSE(float_reg_type.IsDoubleTypes());
   EXPECT_FALSE(float_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(float_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(float_reg_type.HasClass());
 
-  RegType& double_reg_type = cache.DoubleLo();
+  const RegType& double_reg_type = cache.DoubleLo();
   EXPECT_FALSE(double_reg_type.IsUndefined());
   EXPECT_FALSE(double_reg_type.IsConflict());
   EXPECT_FALSE(double_reg_type.IsZero());
@@ -335,6 +343,7 @@
   EXPECT_TRUE(double_reg_type.IsDoubleTypes());
   EXPECT_FALSE(double_reg_type.IsArrayIndexTypes());
   EXPECT_FALSE(double_reg_type.IsNonZeroReferenceTypes());
+  EXPECT_TRUE(double_reg_type.HasClass());
 }
 
 class RegTypeReferenceTest : public CommonRuntimeTest {};
@@ -344,9 +353,9 @@
   // match the one that is imprecise.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
-  RegType& imprecise_obj = cache.JavaLangObject(false);
-  RegType& precise_obj = cache.JavaLangObject(true);
-  RegType& precise_obj_2 = cache.FromDescriptor(NULL, "Ljava/lang/Object;", true);
+  const RegType& imprecise_obj = cache.JavaLangObject(false);
+  const RegType& precise_obj = cache.JavaLangObject(true);
+  const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
 
   EXPECT_TRUE(precise_obj.Equals(precise_obj_2));
   EXPECT_FALSE(imprecise_obj.Equals(precise_obj));
@@ -359,14 +368,14 @@
   // a hit second time.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
-  RegType& ref_type_0 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+  const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
   EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes());
 
-  RegType& ref_type_1 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+  const RegType& ref_type_1 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   EXPECT_TRUE(ref_type_0.Equals(ref_type_1));
 
-  RegType& unresolved_super_class =  cache.FromUnresolvedSuperClass(ref_type_0);
+  const RegType& unresolved_super_class =  cache.FromUnresolvedSuperClass(ref_type_0);
   EXPECT_TRUE(unresolved_super_class.IsUnresolvedSuperClass());
   EXPECT_TRUE(unresolved_super_class.IsNonZeroReferenceTypes());
 }
@@ -375,21 +384,21 @@
   // Tests creating types uninitialized types from unresolved types.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
-  RegType& ref_type_0 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+  const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
-  RegType& ref_type = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+  const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   EXPECT_TRUE(ref_type_0.Equals(ref_type));
   // Create an uninitialized type of this unresolved type
-  RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull);
+  const RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull);
   EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference());
   EXPECT_TRUE(unresolved_unintialised.IsUninitializedTypes());
   EXPECT_TRUE(unresolved_unintialised.IsNonZeroReferenceTypes());
   // Create an uninitialized type of this unresolved type with different  PC
-  RegType& ref_type_unresolved_unintialised_1 =  cache.Uninitialized(ref_type, 1102ull);
+  const RegType& ref_type_unresolved_unintialised_1 =  cache.Uninitialized(ref_type, 1102ull);
   EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference());
   EXPECT_FALSE(unresolved_unintialised.Equals(ref_type_unresolved_unintialised_1));
   // Create an uninitialized type of this unresolved type with the same PC
-  RegType& unresolved_unintialised_2 = cache.Uninitialized(ref_type, 1101ull);
+  const RegType& unresolved_unintialised_2 = cache.Uninitialized(ref_type, 1101ull);
   EXPECT_TRUE(unresolved_unintialised.Equals(unresolved_unintialised_2));
 }
 
@@ -397,12 +406,12 @@
   // Tests types for proper Dump messages.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
-  RegType& unresolved_ref = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
-  RegType& unresolved_ref_another = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExistEither;", true);
-  RegType& resolved_ref = cache.JavaLangString();
-  RegType& resolved_unintialiesd = cache.Uninitialized(resolved_ref, 10);
-  RegType& unresolved_unintialized = cache.Uninitialized(unresolved_ref, 12);
-  RegType& unresolved_merged = cache.FromUnresolvedMerge(unresolved_ref, unresolved_ref_another);
+  const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
+  const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true);
+  const RegType& resolved_ref = cache.JavaLangString();
+  const RegType& resolved_unintialiesd = cache.Uninitialized(resolved_ref, 10);
+  const RegType& unresolved_unintialized = cache.Uninitialized(unresolved_ref, 12);
+  const RegType& unresolved_merged = cache.FromUnresolvedMerge(unresolved_ref, unresolved_ref_another);
 
   std::string expected = "Unresolved Reference: java.lang.DoesNotExist";
   EXPECT_EQ(expected, unresolved_ref.Dump());
@@ -422,16 +431,16 @@
   // The JavaLangObject method instead of FromDescriptor. String class is final.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
-  RegType& ref_type = cache.JavaLangString();
-  RegType& ref_type_2 = cache.JavaLangString();
-  RegType& ref_type_3 = cache.FromDescriptor(NULL, "Ljava/lang/String;", true);
+  const RegType& ref_type = cache.JavaLangString();
+  const RegType& ref_type_2 = cache.JavaLangString();
+  const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true);
 
   EXPECT_TRUE(ref_type.Equals(ref_type_2));
   EXPECT_TRUE(ref_type_2.Equals(ref_type_3));
   EXPECT_TRUE(ref_type.IsPreciseReference());
 
   // Create an uninitialized type out of this:
-  RegType& ref_type_unintialized = cache.Uninitialized(ref_type, 0110ull);
+  const RegType& ref_type_unintialized = cache.Uninitialized(ref_type, 0110ull);
   EXPECT_TRUE(ref_type_unintialized.IsUninitializedReference());
   EXPECT_FALSE(ref_type_unintialized.IsUnresolvedAndUninitializedReference());
 }
@@ -442,9 +451,9 @@
   // The JavaLangObject method instead of FromDescriptor. Object Class in not final.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache(true);
-  RegType& ref_type = cache.JavaLangObject(true);
-  RegType& ref_type_2 = cache.JavaLangObject(true);
-  RegType& ref_type_3 = cache.FromDescriptor(NULL, "Ljava/lang/Object;", true);
+  const RegType& ref_type = cache.JavaLangObject(true);
+  const RegType& ref_type_2 = cache.JavaLangObject(true);
+  const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
 
   EXPECT_TRUE(ref_type.Equals(ref_type_2));
   EXPECT_TRUE(ref_type_3.Equals(ref_type_2));
@@ -455,19 +464,20 @@
   // String and object , LUB is object.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache_new(true);
-  RegType& string = cache_new.JavaLangString();
-  RegType& Object = cache_new.JavaLangObject(true);
+  const RegType& string = cache_new.JavaLangString();
+  const RegType& Object = cache_new.JavaLangObject(true);
   EXPECT_TRUE(string.Merge(Object, &cache_new).IsJavaLangObject());
   // Merge two unresolved types.
-  RegType& ref_type_0 = cache_new.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+  const RegType& ref_type_0 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
   EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
-  RegType& ref_type_1 = cache_new.FromDescriptor(NULL, "Ljava/lang/DoesNotExistToo;", true);
+  const RegType& ref_type_1 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistToo;", true);
   EXPECT_FALSE(ref_type_0.Equals(ref_type_1));
 
-  RegType& merged = ref_type_1.Merge(ref_type_0, &cache_new);
+  const RegType& merged = ref_type_1.Merge(ref_type_0, &cache_new);
   EXPECT_TRUE(merged.IsUnresolvedMergedReference());
+  RegType& merged_nonconst = const_cast<RegType&>(merged);
 
-  std::set<uint16_t> merged_ids = (down_cast<UnresolvedMergedType*>(&merged))->GetMergedTypes();
+  std::set<uint16_t> merged_ids = (down_cast<UnresolvedMergedType*>(&merged_nonconst))->GetMergedTypes();
   EXPECT_EQ(ref_type_0.GetId(), *(merged_ids.begin()));
   EXPECT_EQ(ref_type_1.GetId(), *((++merged_ids.begin())));
 }
@@ -478,27 +488,27 @@
   RegTypeCache cache_new(true);
 
   constexpr int32_t kTestConstantValue = 10;
-  RegType& float_type = cache_new.Float();
-  RegType& precise_cst = cache_new.FromCat1Const(kTestConstantValue, true);
-  RegType& imprecise_cst = cache_new.FromCat1Const(kTestConstantValue, false);
+  const RegType& float_type = cache_new.Float();
+  const RegType& precise_cst = cache_new.FromCat1Const(kTestConstantValue, true);
+  const RegType& imprecise_cst = cache_new.FromCat1Const(kTestConstantValue, false);
   {
     // float MERGE precise cst => float.
-    RegType& merged = float_type.Merge(precise_cst, &cache_new);
+    const RegType& merged = float_type.Merge(precise_cst, &cache_new);
     EXPECT_TRUE(merged.IsFloat());
   }
   {
     // precise cst MERGE float => float.
-    RegType& merged = precise_cst.Merge(float_type, &cache_new);
+    const RegType& merged = precise_cst.Merge(float_type, &cache_new);
     EXPECT_TRUE(merged.IsFloat());
   }
   {
     // float MERGE imprecise cst => float.
-    RegType& merged = float_type.Merge(imprecise_cst, &cache_new);
+    const RegType& merged = float_type.Merge(imprecise_cst, &cache_new);
     EXPECT_TRUE(merged.IsFloat());
   }
   {
     // imprecise cst MERGE float => float.
-    RegType& merged = imprecise_cst.Merge(float_type, &cache_new);
+    const RegType& merged = imprecise_cst.Merge(float_type, &cache_new);
     EXPECT_TRUE(merged.IsFloat());
   }
 }
@@ -509,50 +519,50 @@
   RegTypeCache cache_new(true);
 
   constexpr int32_t kTestConstantValue = 10;
-  RegType& long_lo_type = cache_new.LongLo();
-  RegType& long_hi_type = cache_new.LongHi();
-  RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
-  RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
-  RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
-  RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
+  const RegType& long_lo_type = cache_new.LongLo();
+  const RegType& long_hi_type = cache_new.LongHi();
+  const RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
+  const RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
+  const RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
+  const RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
   {
     // lo MERGE precise cst lo => lo.
-    RegType& merged = long_lo_type.Merge(precise_cst_lo, &cache_new);
+    const RegType& merged = long_lo_type.Merge(precise_cst_lo, &cache_new);
     EXPECT_TRUE(merged.IsLongLo());
   }
   {
     // precise cst lo MERGE lo => lo.
-    RegType& merged = precise_cst_lo.Merge(long_lo_type, &cache_new);
+    const RegType& merged = precise_cst_lo.Merge(long_lo_type, &cache_new);
     EXPECT_TRUE(merged.IsLongLo());
   }
   {
     // lo MERGE imprecise cst lo => lo.
-    RegType& merged = long_lo_type.Merge(imprecise_cst_lo, &cache_new);
+    const RegType& merged = long_lo_type.Merge(imprecise_cst_lo, &cache_new);
     EXPECT_TRUE(merged.IsLongLo());
   }
   {
     // imprecise cst lo MERGE lo => lo.
-    RegType& merged = imprecise_cst_lo.Merge(long_lo_type, &cache_new);
+    const RegType& merged = imprecise_cst_lo.Merge(long_lo_type, &cache_new);
     EXPECT_TRUE(merged.IsLongLo());
   }
   {
     // hi MERGE precise cst hi => hi.
-    RegType& merged = long_hi_type.Merge(precise_cst_hi, &cache_new);
+    const RegType& merged = long_hi_type.Merge(precise_cst_hi, &cache_new);
     EXPECT_TRUE(merged.IsLongHi());
   }
   {
     // precise cst hi MERGE hi => hi.
-    RegType& merged = precise_cst_hi.Merge(long_hi_type, &cache_new);
+    const RegType& merged = precise_cst_hi.Merge(long_hi_type, &cache_new);
     EXPECT_TRUE(merged.IsLongHi());
   }
   {
     // hi MERGE imprecise cst hi => hi.
-    RegType& merged = long_hi_type.Merge(imprecise_cst_hi, &cache_new);
+    const RegType& merged = long_hi_type.Merge(imprecise_cst_hi, &cache_new);
     EXPECT_TRUE(merged.IsLongHi());
   }
   {
     // imprecise cst hi MERGE hi => hi.
-    RegType& merged = imprecise_cst_hi.Merge(long_hi_type, &cache_new);
+    const RegType& merged = imprecise_cst_hi.Merge(long_hi_type, &cache_new);
     EXPECT_TRUE(merged.IsLongHi());
   }
 }
@@ -563,50 +573,50 @@
   RegTypeCache cache_new(true);
 
   constexpr int32_t kTestConstantValue = 10;
-  RegType& double_lo_type = cache_new.DoubleLo();
-  RegType& double_hi_type = cache_new.DoubleHi();
-  RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
-  RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
-  RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
-  RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
+  const RegType& double_lo_type = cache_new.DoubleLo();
+  const RegType& double_hi_type = cache_new.DoubleHi();
+  const RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
+  const RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
+  const RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
+  const RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
   {
     // lo MERGE precise cst lo => lo.
-    RegType& merged = double_lo_type.Merge(precise_cst_lo, &cache_new);
+    const RegType& merged = double_lo_type.Merge(precise_cst_lo, &cache_new);
     EXPECT_TRUE(merged.IsDoubleLo());
   }
   {
     // precise cst lo MERGE lo => lo.
-    RegType& merged = precise_cst_lo.Merge(double_lo_type, &cache_new);
+    const RegType& merged = precise_cst_lo.Merge(double_lo_type, &cache_new);
     EXPECT_TRUE(merged.IsDoubleLo());
   }
   {
     // lo MERGE imprecise cst lo => lo.
-    RegType& merged = double_lo_type.Merge(imprecise_cst_lo, &cache_new);
+    const RegType& merged = double_lo_type.Merge(imprecise_cst_lo, &cache_new);
     EXPECT_TRUE(merged.IsDoubleLo());
   }
   {
     // imprecise cst lo MERGE lo => lo.
-    RegType& merged = imprecise_cst_lo.Merge(double_lo_type, &cache_new);
+    const RegType& merged = imprecise_cst_lo.Merge(double_lo_type, &cache_new);
     EXPECT_TRUE(merged.IsDoubleLo());
   }
   {
     // hi MERGE precise cst hi => hi.
-    RegType& merged = double_hi_type.Merge(precise_cst_hi, &cache_new);
+    const RegType& merged = double_hi_type.Merge(precise_cst_hi, &cache_new);
     EXPECT_TRUE(merged.IsDoubleHi());
   }
   {
     // precise cst hi MERGE hi => hi.
-    RegType& merged = precise_cst_hi.Merge(double_hi_type, &cache_new);
+    const RegType& merged = precise_cst_hi.Merge(double_hi_type, &cache_new);
     EXPECT_TRUE(merged.IsDoubleHi());
   }
   {
     // hi MERGE imprecise cst hi => hi.
-    RegType& merged = double_hi_type.Merge(imprecise_cst_hi, &cache_new);
+    const RegType& merged = double_hi_type.Merge(imprecise_cst_hi, &cache_new);
     EXPECT_TRUE(merged.IsDoubleHi());
   }
   {
     // imprecise cst hi MERGE hi => hi.
-    RegType& merged = imprecise_cst_hi.Merge(double_hi_type, &cache_new);
+    const RegType& merged = imprecise_cst_hi.Merge(double_hi_type, &cache_new);
     EXPECT_TRUE(merged.IsDoubleHi());
   }
 }
@@ -615,8 +625,8 @@
   // Tests creating primitive types types.
   ScopedObjectAccess soa(Thread::Current());
   RegTypeCache cache_new(true);
-  RegType& imprecise_const = cache_new.FromCat1Const(10, false);
-  RegType& precise_const = cache_new.FromCat1Const(10, true);
+  const RegType& imprecise_const = cache_new.FromCat1Const(10, false);
+  const RegType& precise_const = cache_new.FromCat1Const(10, true);
 
   EXPECT_TRUE(imprecise_const.IsImpreciseConstant());
   EXPECT_TRUE(precise_const.IsPreciseConstant());
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 378c6d3..219e687 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -25,10 +25,135 @@
 namespace art {
 namespace verifier {
 
-inline RegType& RegisterLine::GetRegisterType(uint32_t vsrc) const {
+inline const RegType& RegisterLine::GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const {
   // The register index was validated during the static pass, so we don't need to check it here.
   DCHECK_LT(vsrc, num_regs_);
-  return verifier_->GetRegTypeCache()->GetFromId(line_[vsrc]);
+  return verifier->GetRegTypeCache()->GetFromId(line_[vsrc]);
+}
+
+inline bool RegisterLine::SetRegisterType(MethodVerifier* verifier, uint32_t vdst,
+                                          const RegType& new_type) {
+  DCHECK_LT(vdst, num_regs_);
+  if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
+        << new_type << "'";
+    return false;
+  } else if (new_type.IsConflict()) {  // should only be set during a merge
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
+    return false;
+  } else {
+    line_[vdst] = new_type.GetId();
+  }
+  // Clear the monitor entry bits for this register.
+  ClearAllRegToLockDepths(vdst);
+  return true;
+}
+
+inline bool RegisterLine::SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst,
+                                              const RegType& new_type1,
+                                              const RegType& new_type2) {
+  DCHECK_LT(vdst + 1, num_regs_);
+  if (!new_type1.CheckWidePair(new_type2)) {
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '"
+        << new_type1 << "' '" << new_type2 << "'";
+    return false;
+  } else {
+    line_[vdst] = new_type1.GetId();
+    line_[vdst + 1] = new_type2.GetId();
+  }
+  // Clear the monitor entry bits for this register.
+  ClearAllRegToLockDepths(vdst);
+  ClearAllRegToLockDepths(vdst + 1);
+  return true;
+}
+
+inline void RegisterLine::SetResultTypeToUnknown(MethodVerifier* verifier) {
+  result_[0] = verifier->GetRegTypeCache()->Undefined().GetId();
+  result_[1] = result_[0];
+}
+
+inline void RegisterLine::SetResultRegisterType(MethodVerifier* verifier, const RegType& new_type) {
+  DCHECK(!new_type.IsLowHalf());
+  DCHECK(!new_type.IsHighHalf());
+  result_[0] = new_type.GetId();
+  result_[1] = verifier->GetRegTypeCache()->Undefined().GetId();
+}
+
+inline void RegisterLine::SetResultRegisterTypeWide(const RegType& new_type1,
+                                                    const RegType& new_type2) {
+  DCHECK(new_type1.CheckWidePair(new_type2));
+  result_[0] = new_type1.GetId();
+  result_[1] = new_type2.GetId();
+}
+
+inline void RegisterLine::CopyRegister1(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc,
+                                 TypeCategory cat) {
+  DCHECK(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
+  const RegType& type = GetRegisterType(verifier, vsrc);
+  if (!SetRegisterType(verifier, vdst, type)) {
+    return;
+  }
+  if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
+      (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
+                                                 << " cat=" << static_cast<int>(cat);
+  } else if (cat == kTypeCategoryRef) {
+    CopyRegToLockDepth(vdst, vsrc);
+  }
+}
+
+inline void RegisterLine::CopyRegister2(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc) {
+  const RegType& type_l = GetRegisterType(verifier, vsrc);
+  const RegType& type_h = GetRegisterType(verifier, vsrc + 1);
+
+  if (!type_l.CheckWidePair(type_h)) {
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
+                                                 << " type=" << type_l << "/" << type_h;
+  } else {
+    SetRegisterTypeWide(verifier, vdst, type_l, type_h);
+  }
+}
+
+inline bool RegisterLine::VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc,
+                                             const RegType& check_type) {
+  // Verify the src register type against the check type refining the type of the register
+  const RegType& src_type = GetRegisterType(verifier, vsrc);
+  if (UNLIKELY(!check_type.IsAssignableFrom(src_type))) {
+    enum VerifyError fail_type;
+    if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) {
+      // Hard fail if one of the types is primitive, since they are concretely known.
+      fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
+    } else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) {
+      fail_type = VERIFY_ERROR_NO_CLASS;
+    } else {
+      fail_type = VERIFY_ERROR_BAD_CLASS_SOFT;
+    }
+    verifier->Fail(fail_type) << "register v" << vsrc << " has type "
+                               << src_type << " but expected " << check_type;
+    return false;
+  }
+  if (check_type.IsLowHalf()) {
+    const RegType& src_type_h = GetRegisterType(verifier, vsrc + 1);
+    if (UNLIKELY(!src_type.CheckWidePair(src_type_h))) {
+      verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
+                                                   << src_type << "/" << src_type_h;
+      return false;
+    }
+  }
+  // The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
+  // precise than the subtype in vsrc so leave it for reference types. For primitive types
+  // if they are a defined type then they are as precise as we can get, however, for constant
+  // types we may wish to refine them. Unfortunately constant propagation has rendered this useless.
+  return true;
+}
+
+inline bool RegisterLine::VerifyMonitorStackEmpty(MethodVerifier* verifier) const {
+  if (MonitorStackDepth() != 0) {
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected empty monitor stack";
+    return false;
+  } else {
+    return true;
+  }
 }
 
 }  // namespace verifier
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 4d67cfb..3139204 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -20,15 +20,16 @@
 #include "dex_instruction-inl.h"
 #include "method_verifier.h"
 #include "register_line-inl.h"
+#include "reg_type-inl.h"
 
 namespace art {
 namespace verifier {
 
-bool RegisterLine::CheckConstructorReturn() const {
+bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const {
   for (size_t i = 0; i < num_regs_; i++) {
-    if (GetRegisterType(i).IsUninitializedThisReference() ||
-        GetRegisterType(i).IsUnresolvedAndUninitializedThisReference()) {
-      verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
+    if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
+        GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) {
+      verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
           << "Constructor returning without calling superclass constructor";
       return false;
     }
@@ -36,122 +37,38 @@
   return true;
 }
 
-bool RegisterLine::SetRegisterType(uint32_t vdst, RegType& new_type) {
-  DCHECK_LT(vdst, num_regs_);
-  if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
-        << new_type << "'";
-    return false;
-  } else if (new_type.IsConflict()) {  // should only be set during a merge
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
-    return false;
-  } else {
-    line_[vdst] = new_type.GetId();
-  }
-  // Clear the monitor entry bits for this register.
-  ClearAllRegToLockDepths(vdst);
-  return true;
-}
-
-bool RegisterLine::SetRegisterTypeWide(uint32_t vdst, RegType& new_type1,
-                                       RegType& new_type2) {
-  DCHECK_LT(vdst + 1, num_regs_);
-  if (!new_type1.CheckWidePair(new_type2)) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '"
-        << new_type1 << "' '" << new_type2 << "'";
-    return false;
-  } else {
-    line_[vdst] = new_type1.GetId();
-    line_[vdst + 1] = new_type2.GetId();
-  }
-  // Clear the monitor entry bits for this register.
-  ClearAllRegToLockDepths(vdst);
-  ClearAllRegToLockDepths(vdst + 1);
-  return true;
-}
-
-void RegisterLine::SetResultTypeToUnknown() {
-  result_[0] = verifier_->GetRegTypeCache()->Undefined().GetId();
-  result_[1] = result_[0];
-}
-
-void RegisterLine::SetResultRegisterType(RegType& new_type) {
-  DCHECK(!new_type.IsLowHalf());
-  DCHECK(!new_type.IsHighHalf());
-  result_[0] = new_type.GetId();
-  result_[1] = verifier_->GetRegTypeCache()->Undefined().GetId();
-}
-
-void RegisterLine::SetResultRegisterTypeWide(RegType& new_type1,
-                                             RegType& new_type2) {
-  DCHECK(new_type1.CheckWidePair(new_type2));
-  result_[0] = new_type1.GetId();
-  result_[1] = new_type2.GetId();
-}
-
-RegType& RegisterLine::GetInvocationThis(const Instruction* inst, bool is_range) {
+const RegType& RegisterLine::GetInvocationThis(MethodVerifier* verifier, const Instruction* inst,
+                                               bool is_range) {
   const size_t args_count = is_range ? inst->VRegA_3rc() : inst->VRegA_35c();
   if (args_count < 1) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'";
-    return verifier_->GetRegTypeCache()->Conflict();
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'";
+    return verifier->GetRegTypeCache()->Conflict();
   }
   /* Get the element type of the array held in vsrc */
   const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
-  RegType& this_type = GetRegisterType(this_reg);
+  const RegType& this_type = GetRegisterType(verifier, this_reg);
   if (!this_type.IsReferenceTypes()) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v"
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v"
                                                  << this_reg << " (type=" << this_type << ")";
-    return verifier_->GetRegTypeCache()->Conflict();
+    return verifier->GetRegTypeCache()->Conflict();
   }
   return this_type;
 }
 
-bool RegisterLine::VerifyRegisterType(uint32_t vsrc,
-                                      RegType& check_type) {
-  // Verify the src register type against the check type refining the type of the register
-  RegType& src_type = GetRegisterType(vsrc);
-  if (!(check_type.IsAssignableFrom(src_type))) {
-    enum VerifyError fail_type;
-    if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) {
-      // Hard fail if one of the types is primitive, since they are concretely known.
-      fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
-    } else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) {
-      fail_type = VERIFY_ERROR_NO_CLASS;
-    } else {
-      fail_type = VERIFY_ERROR_BAD_CLASS_SOFT;
-    }
-    verifier_->Fail(fail_type) << "register v" << vsrc << " has type "
-                               << src_type << " but expected " << check_type;
-    return false;
-  }
-  if (check_type.IsLowHalf()) {
-    RegType& src_type_h = GetRegisterType(vsrc + 1);
-    if (!src_type.CheckWidePair(src_type_h)) {
-      verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
-                                                   << src_type << "/" << src_type_h;
-      return false;
-    }
-  }
-  // The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
-  // precise than the subtype in vsrc so leave it for reference types. For primitive types
-  // if they are a defined type then they are as precise as we can get, however, for constant
-  // types we may wish to refine them. Unfortunately constant propagation has rendered this useless.
-  return true;
-}
-
-bool RegisterLine::VerifyRegisterTypeWide(uint32_t vsrc, RegType& check_type1,
-                                          RegType& check_type2) {
+bool RegisterLine::VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsrc,
+                                          const RegType& check_type1,
+                                          const RegType& check_type2) {
   DCHECK(check_type1.CheckWidePair(check_type2));
   // Verify the src register type against the check type refining the type of the register
-  RegType& src_type = GetRegisterType(vsrc);
+  const RegType& src_type = GetRegisterType(verifier, vsrc);
   if (!check_type1.IsAssignableFrom(src_type)) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << vsrc << " has type " << src_type
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << vsrc << " has type " << src_type
                                << " but expected " << check_type1;
     return false;
   }
-  RegType& src_type_h = GetRegisterType(vsrc + 1);
+  const RegType& src_type_h = GetRegisterType(verifier, vsrc + 1);
   if (!src_type.CheckWidePair(src_type_h)) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
         << src_type << "/" << src_type_h;
     return false;
   }
@@ -162,12 +79,12 @@
   return true;
 }
 
-void RegisterLine::MarkRefsAsInitialized(RegType& uninit_type) {
+void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) {
   DCHECK(uninit_type.IsUninitializedTypes());
-  RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
+  const RegType& init_type = verifier->GetRegTypeCache()->FromUninitialized(uninit_type);
   size_t changed = 0;
   for (uint32_t i = 0; i < num_regs_; i++) {
-    if (GetRegisterType(i).Equals(uninit_type)) {
+    if (GetRegisterType(verifier, i).Equals(uninit_type)) {
       line_[i] = init_type.GetId();
       changed++;
     }
@@ -175,15 +92,15 @@
   DCHECK_GT(changed, 0u);
 }
 
-void RegisterLine::MarkAllRegistersAsConflicts() {
-  uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+void RegisterLine::MarkAllRegistersAsConflicts(MethodVerifier* verifier) {
+  uint16_t conflict_type_id = verifier->GetRegTypeCache()->Conflict().GetId();
   for (uint32_t i = 0; i < num_regs_; i++) {
     line_[i] = conflict_type_id;
   }
 }
 
-void RegisterLine::MarkAllRegistersAsConflictsExcept(uint32_t vsrc) {
-  uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+void RegisterLine::MarkAllRegistersAsConflictsExcept(MethodVerifier* verifier, uint32_t vsrc) {
+  uint16_t conflict_type_id = verifier->GetRegTypeCache()->Conflict().GetId();
   for (uint32_t i = 0; i < num_regs_; i++) {
     if (i != vsrc) {
       line_[i] = conflict_type_id;
@@ -191,8 +108,8 @@
   }
 }
 
-void RegisterLine::MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc) {
-  uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+void RegisterLine::MarkAllRegistersAsConflictsExceptWide(MethodVerifier* verifier, uint32_t vsrc) {
+  uint16_t conflict_type_id = verifier->GetRegTypeCache()->Conflict().GetId();
   for (uint32_t i = 0; i < num_regs_; i++) {
     if ((i != vsrc) && (i != (vsrc + 1))) {
       line_[i] = conflict_type_id;
@@ -200,11 +117,11 @@
   }
 }
 
-std::string RegisterLine::Dump() {
+std::string RegisterLine::Dump(MethodVerifier* verifier) const {
   std::string result;
   for (size_t i = 0; i < num_regs_; i++) {
     result += StringPrintf("%zd:[", i);
-    result += GetRegisterType(i).Dump();
+    result += GetRegisterType(verifier, i).Dump();
     result += "],";
   }
   for (const auto& monitor : monitors_) {
@@ -213,52 +130,25 @@
   return result;
 }
 
-void RegisterLine::MarkUninitRefsAsInvalid(RegType& uninit_type) {
+void RegisterLine::MarkUninitRefsAsInvalid(MethodVerifier* verifier, const RegType& uninit_type) {
   for (size_t i = 0; i < num_regs_; i++) {
-    if (GetRegisterType(i).Equals(uninit_type)) {
-      line_[i] = verifier_->GetRegTypeCache()->Conflict().GetId();
+    if (GetRegisterType(verifier, i).Equals(uninit_type)) {
+      line_[i] = verifier->GetRegTypeCache()->Conflict().GetId();
       ClearAllRegToLockDepths(i);
     }
   }
 }
 
-void RegisterLine::CopyRegister1(uint32_t vdst, uint32_t vsrc, TypeCategory cat) {
-  DCHECK(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
-  RegType& type = GetRegisterType(vsrc);
-  if (!SetRegisterType(vdst, type)) {
-    return;
-  }
-  if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
-      (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
-                                                 << " cat=" << static_cast<int>(cat);
-  } else if (cat == kTypeCategoryRef) {
-    CopyRegToLockDepth(vdst, vsrc);
-  }
-}
-
-void RegisterLine::CopyRegister2(uint32_t vdst, uint32_t vsrc) {
-  RegType& type_l = GetRegisterType(vsrc);
-  RegType& type_h = GetRegisterType(vsrc + 1);
-
-  if (!type_l.CheckWidePair(type_h)) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
-                                                 << " type=" << type_l << "/" << type_h;
-  } else {
-    SetRegisterTypeWide(vdst, type_l, type_h);
-  }
-}
-
-void RegisterLine::CopyResultRegister1(uint32_t vdst, bool is_reference) {
-  RegType& type = verifier_->GetRegTypeCache()->GetFromId(result_[0]);
+void RegisterLine::CopyResultRegister1(MethodVerifier* verifier, uint32_t vdst, bool is_reference) {
+  const RegType& type = verifier->GetRegTypeCache()->GetFromId(result_[0]);
   if ((!is_reference && !type.IsCategory1Types()) ||
       (is_reference && !type.IsReferenceTypes())) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "copyRes1 v" << vdst << "<- result0"  << " type=" << type;
   } else {
-    DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUndefined());
-    SetRegisterType(vdst, type);
-    result_[0] = verifier_->GetRegTypeCache()->Undefined().GetId();
+    DCHECK(verifier->GetRegTypeCache()->GetFromId(result_[1]).IsUndefined());
+    SetRegisterType(verifier, vdst, type);
+    result_[0] = verifier->GetRegTypeCache()->Undefined().GetId();
   }
 }
 
@@ -266,178 +156,179 @@
  * Implement "move-result-wide". Copy the category-2 value from the result
  * register to another register, and reset the result register.
  */
-void RegisterLine::CopyResultRegister2(uint32_t vdst) {
-  RegType& type_l = verifier_->GetRegTypeCache()->GetFromId(result_[0]);
-  RegType& type_h = verifier_->GetRegTypeCache()->GetFromId(result_[1]);
+void RegisterLine::CopyResultRegister2(MethodVerifier* verifier, uint32_t vdst) {
+  const RegType& type_l = verifier->GetRegTypeCache()->GetFromId(result_[0]);
+  const RegType& type_h = verifier->GetRegTypeCache()->GetFromId(result_[1]);
   if (!type_l.IsCategory2Types()) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "copyRes2 v" << vdst << "<- result0"  << " type=" << type_l;
   } else {
     DCHECK(type_l.CheckWidePair(type_h));  // Set should never allow this case
-    SetRegisterTypeWide(vdst, type_l, type_h);  // also sets the high
-    result_[0] = verifier_->GetRegTypeCache()->Undefined().GetId();
-    result_[1] = verifier_->GetRegTypeCache()->Undefined().GetId();
+    SetRegisterTypeWide(verifier, vdst, type_l, type_h);  // also sets the high
+    result_[0] = verifier->GetRegTypeCache()->Undefined().GetId();
+    result_[1] = verifier->GetRegTypeCache()->Undefined().GetId();
   }
 }
 
-void RegisterLine::CheckUnaryOp(const Instruction* inst,
-                                RegType& dst_type,
-                                RegType& src_type) {
-  if (VerifyRegisterType(inst->VRegB_12x(), src_type)) {
-    SetRegisterType(inst->VRegA_12x(), dst_type);
+void RegisterLine::CheckUnaryOp(MethodVerifier* verifier, const Instruction* inst,
+                                const RegType& dst_type, const RegType& src_type) {
+  if (VerifyRegisterType(verifier, inst->VRegB_12x(), src_type)) {
+    SetRegisterType(verifier, inst->VRegA_12x(), dst_type);
   }
 }
 
-void RegisterLine::CheckUnaryOpWide(const Instruction* inst,
-                                    RegType& dst_type1, RegType& dst_type2,
-                                    RegType& src_type1, RegType& src_type2) {
-  if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) {
-    SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2);
+void RegisterLine::CheckUnaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+                                    const RegType& dst_type1, const RegType& dst_type2,
+                                    const RegType& src_type1, const RegType& src_type2) {
+  if (VerifyRegisterTypeWide(verifier, inst->VRegB_12x(), src_type1, src_type2)) {
+    SetRegisterTypeWide(verifier, inst->VRegA_12x(), dst_type1, dst_type2);
   }
 }
 
-void RegisterLine::CheckUnaryOpToWide(const Instruction* inst,
-                                      RegType& dst_type1, RegType& dst_type2,
-                                      RegType& src_type) {
-  if (VerifyRegisterType(inst->VRegB_12x(), src_type)) {
-    SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2);
+void RegisterLine::CheckUnaryOpToWide(MethodVerifier* verifier, const Instruction* inst,
+                                      const RegType& dst_type1, const RegType& dst_type2,
+                                      const RegType& src_type) {
+  if (VerifyRegisterType(verifier, inst->VRegB_12x(), src_type)) {
+    SetRegisterTypeWide(verifier, inst->VRegA_12x(), dst_type1, dst_type2);
   }
 }
 
-void RegisterLine::CheckUnaryOpFromWide(const Instruction* inst,
-                                        RegType& dst_type,
-                                        RegType& src_type1, RegType& src_type2) {
-  if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) {
-    SetRegisterType(inst->VRegA_12x(), dst_type);
+void RegisterLine::CheckUnaryOpFromWide(MethodVerifier* verifier, const Instruction* inst,
+                                        const RegType& dst_type,
+                                        const RegType& src_type1, const RegType& src_type2) {
+  if (VerifyRegisterTypeWide(verifier, inst->VRegB_12x(), src_type1, src_type2)) {
+    SetRegisterType(verifier, inst->VRegA_12x(), dst_type);
   }
 }
 
-void RegisterLine::CheckBinaryOp(const Instruction* inst,
-                                 RegType& dst_type,
-                                 RegType& src_type1, RegType& src_type2,
+void RegisterLine::CheckBinaryOp(MethodVerifier* verifier, const Instruction* inst,
+                                 const RegType& dst_type,
+                                 const RegType& src_type1, const RegType& src_type2,
                                  bool check_boolean_op) {
   const uint32_t vregB = inst->VRegB_23x();
   const uint32_t vregC = inst->VRegC_23x();
-  if (VerifyRegisterType(vregB, src_type1) &&
-      VerifyRegisterType(vregC, src_type2)) {
+  if (VerifyRegisterType(verifier, vregB, src_type1) &&
+      VerifyRegisterType(verifier, vregC, src_type2)) {
     if (check_boolean_op) {
       DCHECK(dst_type.IsInteger());
-      if (GetRegisterType(vregB).IsBooleanTypes() &&
-          GetRegisterType(vregC).IsBooleanTypes()) {
-        SetRegisterType(inst->VRegA_23x(), verifier_->GetRegTypeCache()->Boolean());
+      if (GetRegisterType(verifier, vregB).IsBooleanTypes() &&
+          GetRegisterType(verifier, vregC).IsBooleanTypes()) {
+        SetRegisterType(verifier, inst->VRegA_23x(), verifier->GetRegTypeCache()->Boolean());
         return;
       }
     }
-    SetRegisterType(inst->VRegA_23x(), dst_type);
+    SetRegisterType(verifier, inst->VRegA_23x(), dst_type);
   }
 }
 
-void RegisterLine::CheckBinaryOpWide(const Instruction* inst,
-                                     RegType& dst_type1, RegType& dst_type2,
-                                     RegType& src_type1_1, RegType& src_type1_2,
-                                     RegType& src_type2_1, RegType& src_type2_2) {
-  if (VerifyRegisterTypeWide(inst->VRegB_23x(), src_type1_1, src_type1_2) &&
-      VerifyRegisterTypeWide(inst->VRegC_23x(), src_type2_1, src_type2_2)) {
-    SetRegisterTypeWide(inst->VRegA_23x(), dst_type1, dst_type2);
+void RegisterLine::CheckBinaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+                                     const RegType& dst_type1, const RegType& dst_type2,
+                                     const RegType& src_type1_1, const RegType& src_type1_2,
+                                     const RegType& src_type2_1, const RegType& src_type2_2) {
+  if (VerifyRegisterTypeWide(verifier, inst->VRegB_23x(), src_type1_1, src_type1_2) &&
+      VerifyRegisterTypeWide(verifier, inst->VRegC_23x(), src_type2_1, src_type2_2)) {
+    SetRegisterTypeWide(verifier, inst->VRegA_23x(), dst_type1, dst_type2);
   }
 }
 
-void RegisterLine::CheckBinaryOpWideShift(const Instruction* inst,
-                                          RegType& long_lo_type, RegType& long_hi_type,
-                                          RegType& int_type) {
-  if (VerifyRegisterTypeWide(inst->VRegB_23x(), long_lo_type, long_hi_type) &&
-      VerifyRegisterType(inst->VRegC_23x(), int_type)) {
-    SetRegisterTypeWide(inst->VRegA_23x(), long_lo_type, long_hi_type);
+void RegisterLine::CheckBinaryOpWideShift(MethodVerifier* verifier, const Instruction* inst,
+                                          const RegType& long_lo_type, const RegType& long_hi_type,
+                                          const RegType& int_type) {
+  if (VerifyRegisterTypeWide(verifier, inst->VRegB_23x(), long_lo_type, long_hi_type) &&
+      VerifyRegisterType(verifier, inst->VRegC_23x(), int_type)) {
+    SetRegisterTypeWide(verifier, inst->VRegA_23x(), long_lo_type, long_hi_type);
   }
 }
 
-void RegisterLine::CheckBinaryOp2addr(const Instruction* inst,
-                                      RegType& dst_type, RegType& src_type1,
-                                      RegType& src_type2, bool check_boolean_op) {
+void RegisterLine::CheckBinaryOp2addr(MethodVerifier* verifier, const Instruction* inst,
+                                      const RegType& dst_type, const RegType& src_type1,
+                                      const RegType& src_type2, bool check_boolean_op) {
   const uint32_t vregA = inst->VRegA_12x();
   const uint32_t vregB = inst->VRegB_12x();
-  if (VerifyRegisterType(vregA, src_type1) &&
-      VerifyRegisterType(vregB, src_type2)) {
+  if (VerifyRegisterType(verifier, vregA, src_type1) &&
+      VerifyRegisterType(verifier, vregB, src_type2)) {
     if (check_boolean_op) {
       DCHECK(dst_type.IsInteger());
-      if (GetRegisterType(vregA).IsBooleanTypes() &&
-          GetRegisterType(vregB).IsBooleanTypes()) {
-        SetRegisterType(vregA, verifier_->GetRegTypeCache()->Boolean());
+      if (GetRegisterType(verifier, vregA).IsBooleanTypes() &&
+          GetRegisterType(verifier, vregB).IsBooleanTypes()) {
+        SetRegisterType(verifier, vregA, verifier->GetRegTypeCache()->Boolean());
         return;
       }
     }
-    SetRegisterType(vregA, dst_type);
+    SetRegisterType(verifier, vregA, dst_type);
   }
 }
 
-void RegisterLine::CheckBinaryOp2addrWide(const Instruction* inst,
-                                          RegType& dst_type1, RegType& dst_type2,
-                                          RegType& src_type1_1, RegType& src_type1_2,
-                                          RegType& src_type2_1, RegType& src_type2_2) {
+void RegisterLine::CheckBinaryOp2addrWide(MethodVerifier* verifier, const Instruction* inst,
+                                          const RegType& dst_type1, const RegType& dst_type2,
+                                          const RegType& src_type1_1, const RegType& src_type1_2,
+                                          const RegType& src_type2_1, const RegType& src_type2_2) {
   const uint32_t vregA = inst->VRegA_12x();
   const uint32_t vregB = inst->VRegB_12x();
-  if (VerifyRegisterTypeWide(vregA, src_type1_1, src_type1_2) &&
-      VerifyRegisterTypeWide(vregB, src_type2_1, src_type2_2)) {
-    SetRegisterTypeWide(vregA, dst_type1, dst_type2);
+  if (VerifyRegisterTypeWide(verifier, vregA, src_type1_1, src_type1_2) &&
+      VerifyRegisterTypeWide(verifier, vregB, src_type2_1, src_type2_2)) {
+    SetRegisterTypeWide(verifier, vregA, dst_type1, dst_type2);
   }
 }
 
-void RegisterLine::CheckBinaryOp2addrWideShift(const Instruction* inst,
-                                               RegType& long_lo_type, RegType& long_hi_type,
-                                               RegType& int_type) {
+void RegisterLine::CheckBinaryOp2addrWideShift(MethodVerifier* verifier, const Instruction* inst,
+                                               const RegType& long_lo_type, const RegType& long_hi_type,
+                                               const RegType& int_type) {
   const uint32_t vregA = inst->VRegA_12x();
   const uint32_t vregB = inst->VRegB_12x();
-  if (VerifyRegisterTypeWide(vregA, long_lo_type, long_hi_type) &&
-      VerifyRegisterType(vregB, int_type)) {
-    SetRegisterTypeWide(vregA, long_lo_type, long_hi_type);
+  if (VerifyRegisterTypeWide(verifier, vregA, long_lo_type, long_hi_type) &&
+      VerifyRegisterType(verifier, vregB, int_type)) {
+    SetRegisterTypeWide(verifier, vregA, long_lo_type, long_hi_type);
   }
 }
 
-void RegisterLine::CheckLiteralOp(const Instruction* inst,
-                                  RegType& dst_type, RegType& src_type,
+void RegisterLine::CheckLiteralOp(MethodVerifier* verifier, const Instruction* inst,
+                                  const RegType& dst_type, const RegType& src_type,
                                   bool check_boolean_op, bool is_lit16) {
   const uint32_t vregA = is_lit16 ? inst->VRegA_22s() : inst->VRegA_22b();
   const uint32_t vregB = is_lit16 ? inst->VRegB_22s() : inst->VRegB_22b();
-  if (VerifyRegisterType(vregB, src_type)) {
+  if (VerifyRegisterType(verifier, vregB, src_type)) {
     if (check_boolean_op) {
       DCHECK(dst_type.IsInteger());
       /* check vB with the call, then check the constant manually */
       const uint32_t val = is_lit16 ? inst->VRegC_22s() : inst->VRegC_22b();
-      if (GetRegisterType(vregB).IsBooleanTypes() && (val == 0 || val == 1)) {
-        SetRegisterType(vregA, verifier_->GetRegTypeCache()->Boolean());
+      if (GetRegisterType(verifier, vregB).IsBooleanTypes() && (val == 0 || val == 1)) {
+        SetRegisterType(verifier, vregA, verifier->GetRegTypeCache()->Boolean());
         return;
       }
     }
-    SetRegisterType(vregA, dst_type);
+    SetRegisterType(verifier, vregA, dst_type);
   }
 }
 
-void RegisterLine::PushMonitor(uint32_t reg_idx, int32_t insn_idx) {
-  RegType& reg_type = GetRegisterType(reg_idx);
+void RegisterLine::PushMonitor(MethodVerifier* verifier, uint32_t reg_idx, int32_t insn_idx) {
+  const RegType& reg_type = GetRegisterType(verifier, reg_idx);
   if (!reg_type.IsReferenceTypes()) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object (" << reg_type << ")";
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object ("
+        << reg_type << ")";
   } else if (monitors_.size() >= 32) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter stack overflow: " << monitors_.size();
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter stack overflow: "
+        << monitors_.size();
   } else {
     SetRegToLockDepth(reg_idx, monitors_.size());
     monitors_.push_back(insn_idx);
   }
 }
 
-void RegisterLine::PopMonitor(uint32_t reg_idx) {
-  RegType& reg_type = GetRegisterType(reg_idx);
+void RegisterLine::PopMonitor(MethodVerifier* verifier, uint32_t reg_idx) {
+  const RegType& reg_type = GetRegisterType(verifier, reg_idx);
   if (!reg_type.IsReferenceTypes()) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit on non-object (" << reg_type << ")";
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit on non-object (" << reg_type << ")";
   } else if (monitors_.empty()) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit stack underflow";
+    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit stack underflow";
   } else {
     monitors_.pop_back();
     if (!IsSetLockDepth(reg_idx, monitors_.size())) {
       // Bug 3215458: Locks and unlocks are on objects, if that object is a literal then before
       // format "036" the constant collector may create unlocks on the same object but referenced
       // via different registers.
-      ((verifier_->DexFileVersion() >= 36) ? verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
-                                           : verifier_->LogVerifyInfo())
+      ((verifier->DexFileVersion() >= 36) ? verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
+                                          : verifier->LogVerifyInfo())
             << "monitor-exit not unlocking the top of the monitor stack";
     } else {
       // Record the register was unlocked
@@ -446,41 +337,34 @@
   }
 }
 
-bool RegisterLine::VerifyMonitorStackEmpty() const {
-  if (MonitorStackDepth() != 0) {
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected empty monitor stack";
-    return false;
-  } else {
-    return true;
-  }
-}
-
-bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) {
+bool RegisterLine::MergeRegisters(MethodVerifier* verifier, const RegisterLine* incoming_line) {
   bool changed = false;
   DCHECK(incoming_line != nullptr);
   for (size_t idx = 0; idx < num_regs_; idx++) {
     if (line_[idx] != incoming_line->line_[idx]) {
-      RegType& incoming_reg_type = incoming_line->GetRegisterType(idx);
-      RegType& cur_type = GetRegisterType(idx);
-      RegType& new_type = cur_type.Merge(incoming_reg_type, verifier_->GetRegTypeCache());
+      const RegType& incoming_reg_type = incoming_line->GetRegisterType(verifier, idx);
+      const RegType& cur_type = GetRegisterType(verifier, idx);
+      const RegType& new_type = cur_type.Merge(incoming_reg_type, verifier->GetRegTypeCache());
       changed = changed || !cur_type.Equals(new_type);
       line_[idx] = new_type.GetId();
     }
   }
-  if (monitors_.size() != incoming_line->monitors_.size()) {
-    LOG(WARNING) << "mismatched stack depths (depth=" << MonitorStackDepth()
-                 << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")";
-  } else if (reg_to_lock_depths_ != incoming_line->reg_to_lock_depths_) {
-    for (uint32_t idx = 0; idx < num_regs_; idx++) {
-      size_t depths = reg_to_lock_depths_.count(idx);
-      size_t incoming_depths = incoming_line->reg_to_lock_depths_.count(idx);
-      if (depths != incoming_depths) {
-        if (depths == 0 || incoming_depths == 0) {
-          reg_to_lock_depths_.erase(idx);
-        } else {
-          LOG(WARNING) << "mismatched stack depths for register v" << idx
-                       << ": " << depths  << " != " << incoming_depths;
-          break;
+  if (monitors_.size() > 0 || incoming_line->monitors_.size() > 0) {
+    if (monitors_.size() != incoming_line->monitors_.size()) {
+      LOG(WARNING) << "mismatched stack depths (depth=" << MonitorStackDepth()
+                     << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")";
+    } else if (reg_to_lock_depths_ != incoming_line->reg_to_lock_depths_) {
+      for (uint32_t idx = 0; idx < num_regs_; idx++) {
+        size_t depths = reg_to_lock_depths_.count(idx);
+        size_t incoming_depths = incoming_line->reg_to_lock_depths_.count(idx);
+        if (depths != incoming_depths) {
+          if (depths == 0 || incoming_depths == 0) {
+            reg_to_lock_depths_.erase(idx);
+          } else {
+            LOG(WARNING) << "mismatched stack depths for register v" << idx
+                << ": " << depths  << " != " << incoming_depths;
+            break;
+          }
         }
       }
     }
@@ -488,12 +372,13 @@
   return changed;
 }
 
-void RegisterLine::WriteReferenceBitMap(std::vector<uint8_t>& data, size_t max_bytes) {
+void RegisterLine::WriteReferenceBitMap(MethodVerifier* verifier,
+                                        std::vector<uint8_t>* data, size_t max_bytes) {
   for (size_t i = 0; i < num_regs_; i += 8) {
     uint8_t val = 0;
     for (size_t j = 0; j < 8 && (i + j) < num_regs_; j++) {
       // Note: we write 1 for a Reference but not for Null
-      if (GetRegisterType(i + j).IsNonZeroReferenceTypes()) {
+      if (GetRegisterType(verifier, i + j).IsNonZeroReferenceTypes()) {
         val |= 1 << j;
       }
     }
@@ -502,16 +387,9 @@
       continue;
     }
     DCHECK_LT(i / 8, max_bytes) << "val=" << static_cast<uint32_t>(val);
-    data.push_back(val);
+    data->push_back(val);
   }
 }
 
-std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  RegisterLine& rhs_non_const = const_cast<RegisterLine&>(rhs);
-  os << rhs_non_const.Dump();
-  return os;
-}
-
 }  // namespace verifier
 }  // namespace art
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 06b7cca..c7fd369 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -57,50 +57,54 @@
   }
 
   // Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst".
-  void CopyRegister1(uint32_t vdst, uint32_t vsrc, TypeCategory cat)
+  void CopyRegister1(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc, TypeCategory cat)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Implement category-2 "move" instructions. Copy a 64-bit value from "vsrc" to "vdst". This
   // copies both halves of the register.
-  void CopyRegister2(uint32_t vdst, uint32_t vsrc)
+  void CopyRegister2(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Implement "move-result". Copy the category-1 value from the result register to another
   // register, and reset the result register.
-  void CopyResultRegister1(uint32_t vdst, bool is_reference)
+  void CopyResultRegister1(MethodVerifier* verifier, uint32_t vdst, bool is_reference)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Implement "move-result-wide". Copy the category-2 value from the result register to another
   // register, and reset the result register.
-  void CopyResultRegister2(uint32_t vdst)
+  void CopyResultRegister2(MethodVerifier* verifier, uint32_t vdst)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Set the invisible result register to unknown
-  void SetResultTypeToUnknown() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetResultTypeToUnknown(MethodVerifier* verifier) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Set the type of register N, verifying that the register is valid.  If "newType" is the "Lo"
   // part of a 64-bit value, register N+1 will be set to "newType+1".
   // The register index was validated during the static pass, so we don't need to check it here.
-  bool SetRegisterType(uint32_t vdst, RegType& new_type)
+  ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier, uint32_t vdst,
+                                     const RegType& new_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool SetRegisterTypeWide(uint32_t vdst, RegType& new_type1, RegType& new_type2)
+  bool SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst, const RegType& new_type1,
+                           const RegType& new_type2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /* Set the type of the "result" register. */
-  void SetResultRegisterType(RegType& new_type)
+  void SetResultRegisterType(MethodVerifier* verifier, const RegType& new_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void SetResultRegisterTypeWide(RegType& new_type1, RegType& new_type2)
+  void SetResultRegisterTypeWide(const RegType& new_type1, const RegType& new_type2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get the type of register vsrc.
-  RegType& GetRegisterType(uint32_t vsrc) const;
+  const RegType& GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const;
 
-  bool VerifyRegisterType(uint32_t vsrc, RegType& check_type)
+  ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc,
+                                        const RegType& check_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool VerifyRegisterTypeWide(uint32_t vsrc, RegType& check_type1, RegType& check_type2)
+  bool VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsrc, const RegType& check_type1,
+                              const RegType& check_type2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void CopyFromLine(const RegisterLine* src) {
@@ -110,7 +114,7 @@
     reg_to_lock_depths_ = src->reg_to_lock_depths_;
   }
 
-  std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::string Dump(MethodVerifier* verifier) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void FillWithGarbage() {
     memset(&line_, 0xf1, num_regs_ * sizeof(uint16_t));
@@ -126,7 +130,7 @@
    * to prevent them from being used (otherwise, MarkRefsAsInitialized would mark the old ones and
    * the new ones at the same time).
    */
-  void MarkUninitRefsAsInvalid(RegType& uninit_type)
+  void MarkUninitRefsAsInvalid(MethodVerifier* verifier, const RegType& uninit_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
@@ -134,15 +138,15 @@
    * reference type. This is called when an appropriate constructor is invoked -- all copies of
    * the reference must be marked as initialized.
    */
-  void MarkRefsAsInitialized(RegType& uninit_type)
+  void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
    * Update all registers to be Conflict except vsrc.
    */
-  void MarkAllRegistersAsConflicts();
-  void MarkAllRegistersAsConflictsExcept(uint32_t vsrc);
-  void MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc);
+  void MarkAllRegistersAsConflicts(MethodVerifier* verifier);
+  void MarkAllRegistersAsConflictsExcept(MethodVerifier* verifier, uint32_t vsrc);
+  void MarkAllRegistersAsConflictsExceptWide(MethodVerifier* verifier, uint32_t vsrc);
 
   /*
    * Check constraints on constructor return. Specifically, make sure that the "this" argument got
@@ -151,7 +155,7 @@
    * of the list in slot 0. If we see a register with an uninitialized slot 0 reference, we know it
    * somehow didn't get initialized.
    */
-  bool CheckConstructorReturn() const;
+  bool CheckConstructorReturn(MethodVerifier* verifier) const;
 
   // Compare two register lines. Returns 0 if they match.
   // Using this for a sort is unwise, since the value can change based on machine endianness.
@@ -173,30 +177,31 @@
    * The argument count is in vA, and the first argument is in vC, for both "simple" and "range"
    * versions. We just need to make sure vA is >= 1 and then return vC.
    */
-  RegType& GetInvocationThis(const Instruction* inst, bool is_range)
+  const RegType& GetInvocationThis(MethodVerifier* verifier, const Instruction* inst,
+                                   bool is_range)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
    * Verify types for a simple two-register instruction (e.g. "neg-int").
    * "dst_type" is stored into vA, and "src_type" is verified against vB.
    */
-  void CheckUnaryOp(const Instruction* inst, RegType& dst_type,
-                    RegType& src_type)
+  void CheckUnaryOp(MethodVerifier* verifier, const Instruction* inst, const RegType& dst_type,
+                    const RegType& src_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void CheckUnaryOpWide(const Instruction* inst,
-                        RegType& dst_type1, RegType& dst_type2,
-                        RegType& src_type1, RegType& src_type2)
+  void CheckUnaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+                        const RegType& dst_type1, const RegType& dst_type2,
+                        const RegType& src_type1, const RegType& src_type2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void CheckUnaryOpToWide(const Instruction* inst,
-                          RegType& dst_type1, RegType& dst_type2,
-                          RegType& src_type)
+  void CheckUnaryOpToWide(MethodVerifier* verifier, const Instruction* inst,
+                          const RegType& dst_type1, const RegType& dst_type2,
+                          const RegType& src_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void CheckUnaryOpFromWide(const Instruction* inst,
-                            RegType& dst_type,
-                            RegType& src_type1, RegType& src_type2)
+  void CheckUnaryOpFromWide(MethodVerifier* verifier, const Instruction* inst,
+                            const RegType& dst_type,
+                            const RegType& src_type1, const RegType& src_type2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
@@ -204,41 +209,41 @@
    * "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified
    * against vB/vC.
    */
-  void CheckBinaryOp(const Instruction* inst,
-                     RegType& dst_type, RegType& src_type1, RegType& src_type2,
+  void CheckBinaryOp(MethodVerifier* verifier, const Instruction* inst,
+                     const RegType& dst_type, const RegType& src_type1, const RegType& src_type2,
                      bool check_boolean_op)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void CheckBinaryOpWide(const Instruction* inst,
-                         RegType& dst_type1, RegType& dst_type2,
-                         RegType& src_type1_1, RegType& src_type1_2,
-                         RegType& src_type2_1, RegType& src_type2_2)
+  void CheckBinaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+                         const RegType& dst_type1, const RegType& dst_type2,
+                         const RegType& src_type1_1, const RegType& src_type1_2,
+                         const RegType& src_type2_1, const RegType& src_type2_2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void CheckBinaryOpWideShift(const Instruction* inst,
-                              RegType& long_lo_type, RegType& long_hi_type,
-                              RegType& int_type)
+  void CheckBinaryOpWideShift(MethodVerifier* verifier, const Instruction* inst,
+                              const RegType& long_lo_type, const RegType& long_hi_type,
+                              const RegType& int_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
    * Verify types for a binary "2addr" operation. "src_type1"/"src_type2"
    * are verified against vA/vB, then "dst_type" is stored into vA.
    */
-  void CheckBinaryOp2addr(const Instruction* inst,
-                          RegType& dst_type,
-                          RegType& src_type1, RegType& src_type2,
+  void CheckBinaryOp2addr(MethodVerifier* verifier, const Instruction* inst,
+                          const RegType& dst_type,
+                          const RegType& src_type1, const RegType& src_type2,
                           bool check_boolean_op)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void CheckBinaryOp2addrWide(const Instruction* inst,
-                              RegType& dst_type1, RegType& dst_type2,
-                              RegType& src_type1_1, RegType& src_type1_2,
-                              RegType& src_type2_1, RegType& src_type2_2)
+  void CheckBinaryOp2addrWide(MethodVerifier* verifier, const Instruction* inst,
+                              const RegType& dst_type1, const RegType& dst_type2,
+                              const RegType& src_type1_1, const RegType& src_type1_2,
+                              const RegType& src_type2_1, const RegType& src_type2_2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void CheckBinaryOp2addrWideShift(const Instruction* inst,
-                                   RegType& long_lo_type, RegType& long_hi_type,
-                                   RegType& int_type)
+  void CheckBinaryOp2addrWideShift(MethodVerifier* verifier, const Instruction* inst,
+                                   const RegType& long_lo_type, const RegType& long_hi_type,
+                                   const RegType& int_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
@@ -247,16 +252,18 @@
    *
    * If "check_boolean_op" is set, we use the constant value in vC.
    */
-  void CheckLiteralOp(const Instruction* inst,
-                      RegType& dst_type, RegType& src_type,
+  void CheckLiteralOp(MethodVerifier* verifier, const Instruction* inst,
+                      const RegType& dst_type, const RegType& src_type,
                       bool check_boolean_op, bool is_lit16)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Verify/push monitor onto the monitor stack, locking the value in reg_idx at location insn_idx.
-  void PushMonitor(uint32_t reg_idx, int32_t insn_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void PushMonitor(MethodVerifier* verifier, uint32_t reg_idx, int32_t insn_idx)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Verify/pop monitor from monitor stack ensuring that we believe the monitor is locked
-  void PopMonitor(uint32_t reg_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void PopMonitor(MethodVerifier* verifier, uint32_t reg_idx)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Stack of currently held monitors and where they were locked
   size_t MonitorStackDepth() const {
@@ -265,23 +272,23 @@
 
   // We expect no monitors to be held at certain points, such a method returns. Verify the stack
   // is empty, failing and returning false if not.
-  bool VerifyMonitorStackEmpty() const;
+  bool VerifyMonitorStackEmpty(MethodVerifier* verifier) const;
 
-  bool MergeRegisters(const RegisterLine* incoming_line)
+  bool MergeRegisters(MethodVerifier* verifier, const RegisterLine* incoming_line)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  size_t GetMaxNonZeroReferenceReg(size_t max_ref_reg) {
+  size_t GetMaxNonZeroReferenceReg(MethodVerifier* verifier, size_t max_ref_reg) {
     size_t i = static_cast<int>(max_ref_reg) < 0 ? 0 : max_ref_reg;
     for (; i < num_regs_; i++) {
-      if (GetRegisterType(i).IsNonZeroReferenceTypes()) {
+      if (GetRegisterType(verifier, i).IsNonZeroReferenceTypes()) {
         max_ref_reg = i;
       }
     }
     return max_ref_reg;
   }
 
-  // Write a bit at each register location that holds a reference
-  void WriteReferenceBitMap(std::vector<uint8_t>& data, size_t max_bytes);
+  // Write a bit at each register location that holds a reference.
+  void WriteReferenceBitMap(MethodVerifier* verifier, std::vector<uint8_t>* data, size_t max_bytes);
 
   size_t GetMonitorEnterCount() {
     return monitors_.size();
@@ -337,19 +344,17 @@
   }
 
   RegisterLine(size_t num_regs, MethodVerifier* verifier)
-      : verifier_(verifier), num_regs_(num_regs) {
+      : num_regs_(num_regs) {
     memset(&line_, 0, num_regs_ * sizeof(uint16_t));
-    SetResultTypeToUnknown();
+    SetResultTypeToUnknown(verifier);
   }
 
   // Storage for the result register's type, valid after an invocation
   uint16_t result_[2];
 
-  // Back link to the verifier
-  MethodVerifier* verifier_;
-
   // Length of reg_types_
   const uint32_t num_regs_;
+
   // A stack of monitor enter locations
   std::vector<uint32_t, TrackingAllocator<uint32_t, kAllocatorTagVerifier>> monitors_;
   // A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor
@@ -360,7 +365,6 @@
   // An array of RegType Ids associated with each dex register.
   uint16_t line_[0];
 };
-std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs);
 
 }  // namespace verifier
 }  // namespace art
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 6b67dfa..4a3c3ec 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -21,6 +21,7 @@
 #include "base/logging.h"
 #include "mirror/class.h"
 #include "ScopedLocalRef.h"
+#include "scoped_thread_state_change.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -28,7 +29,7 @@
 jclass WellKnownClasses::com_android_dex_Dex;
 jclass WellKnownClasses::dalvik_system_DexFile;
 jclass WellKnownClasses::dalvik_system_DexPathList;
-jclass WellKnownClasses::dalvik_system_DexPathList$Element;
+jclass WellKnownClasses::dalvik_system_DexPathList__Element;
 jclass WellKnownClasses::dalvik_system_PathClassLoader;
 jclass WellKnownClasses::java_lang_BootClassLoader;
 jclass WellKnownClasses::java_lang_ClassLoader;
@@ -47,7 +48,7 @@
 jclass WellKnownClasses::java_lang_String;
 jclass WellKnownClasses::java_lang_System;
 jclass WellKnownClasses::java_lang_Thread;
-jclass WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler;
+jclass WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler;
 jclass WellKnownClasses::java_lang_ThreadGroup;
 jclass WellKnownClasses::java_lang_Throwable;
 jclass WellKnownClasses::java_nio_DirectByteBuffer;
@@ -77,7 +78,7 @@
 jmethodID WellKnownClasses::java_lang_System_runFinalization = NULL;
 jmethodID WellKnownClasses::java_lang_Thread_init;
 jmethodID WellKnownClasses::java_lang_Thread_run;
-jmethodID WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler_uncaughtException;
+jmethodID WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler_uncaughtException;
 jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
 jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
 jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -86,7 +87,7 @@
 jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
 jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList;
 jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
-jfieldID WellKnownClasses::dalvik_system_DexPathList$Element_dexFile;
+jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
 jfieldID WellKnownClasses::java_lang_Thread_daemon;
 jfieldID WellKnownClasses::java_lang_Thread_group;
 jfieldID WellKnownClasses::java_lang_Thread_lock;
@@ -122,18 +123,32 @@
   return reinterpret_cast<jclass>(env->NewGlobalRef(c.get()));
 }
 
-static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) {
-  jfieldID fid = is_static ? env->GetStaticFieldID(c, name, signature) : env->GetFieldID(c, name, signature);
+static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static,
+                           const char* name, const char* signature) {
+  jfieldID fid = (is_static ?
+                  env->GetStaticFieldID(c, name, signature) :
+                  env->GetFieldID(c, name, signature));
   if (fid == NULL) {
-    LOG(FATAL) << "Couldn't find field \"" << name << "\" with signature \"" << signature << "\"";
+    ScopedObjectAccess soa(env);
+    std::ostringstream os;
+    WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+    LOG(FATAL) << "Couldn't find field \"" << name << "\" with signature \"" << signature << "\": "
+               << os.str();
   }
   return fid;
 }
 
-jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) {
-  jmethodID mid = is_static ? env->GetStaticMethodID(c, name, signature) : env->GetMethodID(c, name, signature);
+jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
+                      const char* name, const char* signature) {
+  jmethodID mid = (is_static ?
+                   env->GetStaticMethodID(c, name, signature) :
+                   env->GetMethodID(c, name, signature));
   if (mid == NULL) {
-    LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\"";
+    ScopedObjectAccess soa(env);
+    std::ostringstream os;
+    WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+    LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\": "
+               << os.str();
   }
   return mid;
 }
@@ -148,7 +163,7 @@
   com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
   dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
   dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
-  dalvik_system_DexPathList$Element = CacheClass(env, "dalvik/system/DexPathList$Element");
+  dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
   dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
   java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader");
   java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
@@ -167,7 +182,8 @@
   java_lang_String = CacheClass(env, "java/lang/String");
   java_lang_System = CacheClass(env, "java/lang/System");
   java_lang_Thread = CacheClass(env, "java/lang/Thread");
-  java_lang_Thread$UncaughtExceptionHandler = CacheClass(env, "java/lang/Thread$UncaughtExceptionHandler");
+  java_lang_Thread__UncaughtExceptionHandler = CacheClass(env,
+      "java/lang/Thread$UncaughtExceptionHandler");
   java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
   java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
   java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
@@ -192,7 +208,7 @@
   java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/ArtMethod;[Ljava/lang/Object;)Ljava/lang/Object;");
   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");
-  java_lang_Thread$UncaughtExceptionHandler_uncaughtException = CacheMethod(env, java_lang_Thread$UncaughtExceptionHandler, false, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+  java_lang_Thread__UncaughtExceptionHandler_uncaughtException = CacheMethod(env, java_lang_Thread__UncaughtExceptionHandler, false, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
   java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "removeThread", "(Ljava/lang/Thread;)V");
   java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
   org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
@@ -201,7 +217,7 @@
   dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "J");
   dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
   dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
-  dalvik_system_DexPathList$Element_dexFile = CacheField(env, dalvik_system_DexPathList$Element, false, "dexFile", "Ldalvik/system/DexFile;");
+  dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
   java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
   java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
   java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 3780733..790d7f7 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -42,7 +42,7 @@
   static jclass com_android_dex_Dex;
   static jclass dalvik_system_DexFile;
   static jclass dalvik_system_DexPathList;
-  static jclass dalvik_system_DexPathList$Element;
+  static jclass dalvik_system_DexPathList__Element;
   static jclass dalvik_system_PathClassLoader;
   static jclass java_lang_BootClassLoader;
   static jclass java_lang_ClassLoader;
@@ -62,7 +62,7 @@
   static jclass java_lang_System;
   static jclass java_lang_Thread;
   static jclass java_lang_ThreadGroup;
-  static jclass java_lang_Thread$UncaughtExceptionHandler;
+  static jclass java_lang_Thread__UncaughtExceptionHandler;
   static jclass java_lang_Throwable;
   static jclass java_util_Collections;
   static jclass java_nio_DirectByteBuffer;
@@ -91,7 +91,7 @@
   static jmethodID java_lang_System_runFinalization;
   static jmethodID java_lang_Thread_init;
   static jmethodID java_lang_Thread_run;
-  static jmethodID java_lang_Thread$UncaughtExceptionHandler_uncaughtException;
+  static jmethodID java_lang_Thread__UncaughtExceptionHandler_uncaughtException;
   static jmethodID java_lang_ThreadGroup_removeThread;
   static jmethodID java_nio_DirectByteBuffer_init;
   static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -99,7 +99,7 @@
 
   static jfieldID dalvik_system_DexFile_cookie;
   static jfieldID dalvik_system_DexPathList_dexElements;
-  static jfieldID dalvik_system_DexPathList$Element_dexFile;
+  static jfieldID dalvik_system_DexPathList__Element_dexFile;
   static jfieldID dalvik_system_PathClassLoader_pathList;
   static jfieldID java_lang_reflect_AbstractMethod_artMethod;
   static jfieldID java_lang_reflect_Field_artField;
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index c02f310..63bfc44 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -123,7 +123,7 @@
 
   // Resist the urge to delete the space. <: is a bigraph sequence.
   std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
-  const int32_t error = FindEntry(handle_, name, zip_entry.get());
+  const int32_t error = FindEntry(handle_, ZipEntryName(name), zip_entry.get());
   if (error) {
     *error_msg = std::string(ErrorCodeString(error));
     return nullptr;
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index c655226..74bfb7e 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -26,15 +26,18 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "sigchain.h"
+
 #if defined(__APPLE__)
 #define _NSIG NSIG
+#define sighandler_t sig_t
 #endif
 
 namespace art {
 
 class SignalAction {
  public:
-  SignalAction() : claimed_(false) {
+  SignalAction() : claimed_(false), uses_old_style_(false) {
   }
 
   // Claim the signal and keep the action specified.
@@ -60,17 +63,29 @@
   }
 
   // Change the recorded action to that specified.
-  void SetAction(const struct sigaction& action) {
+  // If oldstyle is true then this action is from an older style signal()
+  // call as opposed to sigaction().  In this case the sa_handler is
+  // used when invoking the user's handler.
+  void SetAction(const struct sigaction& action, bool oldstyle) {
     action_ = action;
+    uses_old_style_ = oldstyle;
+  }
+
+  bool OldStyle() const {
+    return uses_old_style_;
   }
 
  private:
   struct sigaction action_;     // Action to be performed.
   bool claimed_;                // Whether signal is claimed or not.
+  bool uses_old_style_;         // Action is created using signal().  Use sa_handler.
 };
 
 // User's signal handlers
 static SignalAction user_sigactions[_NSIG];
+static bool initialized;
+static void* linked_sigaction_sym;
+static void* linked_sigprocmask_sym;
 
 static void log(const char* format, ...) {
   char buf[256];
@@ -92,6 +107,7 @@
   }
 }
 
+
 // Claim a signal chain for a particular signal.
 void ClaimSignalChain(int signal, struct sigaction* oldaction) {
   CheckSignalValid(signal);
@@ -115,7 +131,7 @@
   }
 
   const struct sigaction& action = user_sigactions[sig].GetAction();
-  if ((action.sa_flags & SA_SIGINFO) == 0) {
+  if (user_sigactions[sig].OldStyle()) {
     if (action.sa_handler != NULL) {
       action.sa_handler(sig);
     } else {
@@ -145,7 +161,7 @@
       *old_action = user_sigactions[signal].GetAction();
     }
     if (new_action != NULL) {
-      user_sigactions[signal].SetAction(*new_action);
+      user_sigactions[signal].SetAction(*new_action, false);
     }
     return 0;
   }
@@ -153,14 +169,17 @@
   // Will only get here if the signal chain has not been claimed.  We want
   // to pass the sigaction on to the kernel via the real sigaction in libc.
 
-  void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
   if (linked_sigaction_sym == nullptr) {
-    linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
-    if (linked_sigaction_sym == nullptr ||
-        linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
-      log("Unable to find next sigaction in signal chain");
-      abort();
-    }
+    // Perform lazy initialization.
+    // This will only occur outside of a signal context since we have
+    // not been initialized and therefore cannot be within the ART
+    // runtime.
+    InitializeSignalChain();
+  }
+
+  if (linked_sigaction_sym == nullptr) {
+    log("Unable to find next sigaction in signal chain");
+    abort();
   }
 
   typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
@@ -168,6 +187,45 @@
   return linked_sigaction(signal, new_action, old_action);
 }
 
+sighandler_t signal(int signal, sighandler_t handler) {
+  struct sigaction sa;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_handler = handler;
+  sa.sa_flags = SA_RESTART;
+  sighandler_t oldhandler;
+
+  // If this signal has been claimed as a signal chain, record the user's
+  // action but don't pass it on to the kernel.
+  // Note that we check that the signal number is in range here.  An out of range signal
+  // number should behave exactly as the libc sigaction.
+  if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
+    oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
+    user_sigactions[signal].SetAction(sa, true);
+    return oldhandler;
+  }
+
+  // Will only get here if the signal chain has not been claimed.  We want
+  // to pass the sigaction on to the kernel via the real sigaction in libc.
+
+  if (linked_sigaction_sym == nullptr) {
+    // Perform lazy initialization.
+    InitializeSignalChain();
+  }
+
+  if (linked_sigaction_sym == nullptr) {
+    log("Unable to find next sigaction in signal chain");
+    abort();
+  }
+
+  typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
+  SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
+  if (linked_sigaction(signal, &sa, &sa) == -1) {
+    return SIG_ERR;
+  }
+
+  return reinterpret_cast<sighandler_t>(sa.sa_handler);
+}
+
 int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
   const sigset_t* new_set_ptr = bionic_new_set;
   sigset_t tmpset;
@@ -186,14 +244,14 @@
     new_set_ptr = &tmpset;
   }
 
-  void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
   if (linked_sigprocmask_sym == nullptr) {
-    linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
-    if (linked_sigprocmask_sym == nullptr ||
-        linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
-      log("Unable to find next sigprocmask in signal chain");
-      abort();
-    }
+    // Perform lazy initialization.
+    InitializeSignalChain();
+  }
+
+  if (linked_sigprocmask_sym == nullptr) {
+    log("Unable to find next sigprocmask in signal chain");
+    abort();
   }
 
   typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
@@ -201,5 +259,36 @@
   return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
 }
 }   // extern "C"
+
+void InitializeSignalChain() {
+  // Warning.
+  // Don't call this from within a signal context as it makes calls to
+  // dlsym.  Calling into the dynamic linker will result in locks being
+  // taken and if it so happens that a signal occurs while one of these
+  // locks is already taken, dlsym will block trying to reenter a
+  // mutex and we will never get out of it.
+  if (initialized) {
+    // Don't initialize twice.
+    return;
+  }
+  linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
+  if (linked_sigaction_sym == nullptr) {
+    linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
+    if (linked_sigaction_sym == nullptr ||
+      linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
+        linked_sigaction_sym = nullptr;
+    }
+  }
+
+  linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
+  if (linked_sigprocmask_sym == nullptr) {
+    linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
+    if (linked_sigprocmask_sym == nullptr ||
+        linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
+         linked_sigprocmask_sym = nullptr;
+    }
+  }
+  initialized = true;
+}
 }   // namespace art
 
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
index a4ce81c..5bc4026 100644
--- a/sigchainlib/sigchain.h
+++ b/sigchainlib/sigchain.h
@@ -21,6 +21,8 @@
 
 namespace art {
 
+void InitializeSignalChain();
+
 void ClaimSignalChain(int signal, struct sigaction* oldaction);
 
 void UnclaimSignalChain(int signal);
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index e69de29..49d9cc0 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -0,0 +1,29 @@
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+RUNNING super object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, super class, super nonstatic
+Super.<init>
+RUNNING super object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, super class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, sub nonstatic
+Subclass.nonstaticMethod
+PASSED sub object, sub class, sub nonstatic
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index f5a1d65..6fc4484 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -353,3 +353,198 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
   PthreadHelper(&testShallowGetStackClass2);
 }
+
+class JniCallNonvirtualVoidMethodTest {
+ public:
+  explicit JniCallNonvirtualVoidMethodTest(JNIEnv* env)
+      : env_(env),
+        check_jni_ri_(true),
+        check_jni_android_(true),
+        super_(GetClass("JniCallNonvirtualTest")),
+        sub_(GetClass("JniCallNonvirtualTestSubclass")),
+        super_constructor_(GetMethodID(super_, true, "<init>")),
+        super_static_(GetMethodID(super_, false, "staticMethod")),
+        super_nonstatic_(GetMethodID(super_, true, "nonstaticMethod")),
+        sub_constructor_(GetMethodID(sub_, true, "<init>")),
+        sub_static_(GetMethodID(sub_, false, "staticMethod")),
+        sub_nonstatic_(GetMethodID(sub_, true, "nonstaticMethod")),
+        super_field_(GetFieldID(super_, "nonstaticMethodSuperCalled")),
+        sub_field_(GetFieldID(super_, "nonstaticMethodSubCalled")) {}
+
+  void Test() {
+    TestStaticCallNonvirtualMethod();
+    TestNewObject();
+    TestnonstaticCallNonvirtualMethod();
+  }
+
+  JNIEnv* const env_;
+
+  bool const check_jni_ri_;
+  bool const check_jni_android_;
+
+  jclass const super_;
+  jclass const sub_;
+
+  jmethodID const super_constructor_;
+  jmethodID const super_static_;
+  jmethodID const super_nonstatic_;
+  jmethodID const sub_constructor_;
+  jmethodID const sub_static_;
+  jmethodID const sub_nonstatic_;
+
+  jfieldID const super_field_;
+  jfieldID const sub_field_;
+
+ private:
+  jclass GetClass(const char* class_name) {
+    jclass c = env_->FindClass(class_name);
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(!env_->ExceptionCheck());
+    assert(c != nullptr);
+    return c;
+  }
+
+  jmethodID GetMethodID(jclass c, bool nonstatic, const char* method_name) {
+    jmethodID m = ((nonstatic) ?
+                   env_->GetMethodID(c, method_name, "()V") :
+                   env_->GetStaticMethodID(c, method_name, "()V"));
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(m != nullptr);
+    return m;
+  }
+
+  jobject CallConstructor(jclass c, jmethodID m) {
+    jobject o = env_->NewObject(c, m);
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(o != nullptr);
+    return o;
+  }
+
+  void CallMethod(jobject o, jclass c, jmethodID m, bool nonstatic, const char* test_case) {
+    printf("RUNNING %s\n", test_case);
+    env_->CallNonvirtualVoidMethod(o, c, m);
+    bool exception_check = env_->ExceptionCheck();
+    if (c == nullptr || !nonstatic) {
+      if (!exception_check) {
+        printf("FAILED %s due to missing exception\n", test_case);
+        env_->FatalError("Expected NullPointerException with null jclass");
+      }
+      env_->ExceptionClear();
+    } else if (exception_check) {
+      printf("FAILED %s due to pending exception\n", test_case);
+      env_->ExceptionDescribe();
+      env_->FatalError(test_case);
+    }
+    printf("PASSED %s\n", test_case);
+  }
+
+  jfieldID GetFieldID(jclass c, const char* field_name) {
+    jfieldID m = env_->GetFieldID(c, field_name, "Z");
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(m != nullptr);
+    return m;
+  }
+
+  jboolean GetBooleanField(jobject o, jfieldID f) {
+    jboolean b = env_->GetBooleanField(o, f);
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    return b;
+  }
+
+  void TestStaticCallNonvirtualMethod() {
+    if (!check_jni_ri_&& !check_jni_android_) {
+      CallMethod(nullptr, nullptr, super_static_, false, "null object, null class, super static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, super_, super_static_, false, "null object, super class, super static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, sub_, super_static_, false, "null object, sub class, super static");
+    }
+
+    if (!check_jni_ri_ && !check_jni_android_) {
+      CallMethod(nullptr, nullptr, sub_static_, false, "null object, null class, sub static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, sub_, sub_static_, false, "null object, super class, sub static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, super_, sub_static_, false, "null object, super class, sub static");
+    }
+  }
+
+  void TestNewObject() {
+    jobject super_super = CallConstructor(super_, super_constructor_);
+    jobject super_sub = CallConstructor(super_, sub_constructor_);
+    jobject sub_super = CallConstructor(sub_, super_constructor_);
+    jobject sub_sub = CallConstructor(sub_, sub_constructor_);
+
+    assert(env_->IsInstanceOf(super_super, super_));
+    assert(!env_->IsInstanceOf(super_super, sub_));
+
+    // Note that even though we called (and ran) the subclass
+    // constructor, we are not the subclass.
+    assert(env_->IsInstanceOf(super_sub, super_));
+    assert(!env_->IsInstanceOf(super_sub, sub_));
+
+    // Note that even though we called the superclass constructor, we
+    // are still the subclass.
+    assert(env_->IsInstanceOf(sub_super, super_));
+    assert(env_->IsInstanceOf(sub_super, sub_));
+
+    assert(env_->IsInstanceOf(sub_sub, super_));
+    assert(env_->IsInstanceOf(sub_sub, sub_));
+  }
+
+  void TestnonstaticCallNonvirtualMethod(bool super_object, bool super_class, bool super_method, const char* test_case) {
+    if (check_jni_android_) {
+      if (super_object && !super_method) {
+        return;  // We don't allow a call with sub class method on the super class instance.
+      }
+      if (super_class && !super_method) {
+        return;  // We don't allow a call with the sub class method with the super class argument.
+      }
+    }
+    jobject o = ((super_object) ?
+                 CallConstructor(super_, super_constructor_) :
+                 CallConstructor(sub_, sub_constructor_));
+    jclass c = (super_class) ? super_ : sub_;
+    jmethodID m = (super_method) ? super_nonstatic_ : sub_nonstatic_;
+    CallMethod(o, c, m, true, test_case);
+    jboolean super_field = GetBooleanField(o, super_field_);
+    jboolean sub_field = GetBooleanField(o, sub_field_);
+    assert(super_field == super_method);
+    assert(sub_field != super_method);
+  }
+
+  void TestnonstaticCallNonvirtualMethod() {
+    TestnonstaticCallNonvirtualMethod(true, true, true, "super object, super class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(true, false, true, "super object, sub class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(true, false, false, "super object, sub class, sub nonstatic");
+    TestnonstaticCallNonvirtualMethod(true, true, false, "super object, super class, sub nonstatic");
+
+    TestnonstaticCallNonvirtualMethod(false, true, true, "sub object, super class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(false, false, true, "sub object, sub class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(false, false, false, "sub object, sub class, sub nonstatic");
+    TestnonstaticCallNonvirtualMethod(false, true, false, "sub object, super class, sub nonstatic");
+  }
+};
+
+extern "C" void JNICALL Java_Main_testCallNonvirtual(JNIEnv* env, jclass) {
+  JniCallNonvirtualVoidMethodTest(env).Test();
+}
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 5884bc0..8e92010 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -32,6 +32,7 @@
         testIsAssignableFromOnPrimitiveTypes();
         testShallowGetCallingClassLoader();
         testShallowGetStackClass2();
+        testCallNonvirtual();
     }
 
     private static native void testFindClassOnAttachedNativeThread();
@@ -94,7 +95,7 @@
 
     // Test sign-extension for values < 32b
 
-    native static byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
+    static native byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
         byte b8, byte b9, byte b10);
 
     private static void testByteMethod() {
@@ -109,7 +110,7 @@
       }
     }
 
-    native static short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
+    private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
         short s8, short s9, short s10);
 
     private static void testShortMethod() {
@@ -126,7 +127,7 @@
 
     // Test zero-extension for values < 32b
 
-    native static boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
+    private static native boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
         boolean b8, boolean b9, boolean b10);
 
     private static void testBooleanMethod() {
@@ -139,7 +140,7 @@
       }
     }
 
-    native static char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
+    private static native char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
         char c8, char c9, char c10);
 
     private static void testCharMethod() {
@@ -168,17 +169,55 @@
       }
     }
 
-    native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
+    private static native boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
 
-    static void testShallowGetCallingClassLoader() {
+    private static void testShallowGetCallingClassLoader() {
         nativeTestShallowGetCallingClassLoader();
     }
 
-    native static void nativeTestShallowGetCallingClassLoader();
+    private native static void nativeTestShallowGetCallingClassLoader();
 
-    static void testShallowGetStackClass2() {
+    private static void testShallowGetStackClass2() {
         nativeTestShallowGetStackClass2();
     }
 
-    native static void nativeTestShallowGetStackClass2();
+    private static native void nativeTestShallowGetStackClass2();
+
+    private static native void testCallNonvirtual();
+}
+
+class JniCallNonvirtualTest {
+    public boolean nonstaticMethodSuperCalled = false;
+    public boolean nonstaticMethodSubCalled = false;
+
+    private static native void testCallNonvirtual();
+
+    public JniCallNonvirtualTest() {
+        System.out.println("Super.<init>");
+    }
+
+    public static void staticMethod() {
+        System.out.println("Super.staticMethod");
+    }
+
+    public void nonstaticMethod() {
+        System.out.println("Super.nonstaticMethod");
+        nonstaticMethodSuperCalled = true;
+    }
+}
+
+class JniCallNonvirtualTestSubclass extends JniCallNonvirtualTest {
+
+    public JniCallNonvirtualTestSubclass() {
+        System.out.println("Subclass.<init>");
+    }
+
+    public static void staticMethod() {
+        System.out.println("Subclass.staticMethod");
+    }
+
+    public void nonstaticMethod() {
+        System.out.println("Subclass.nonstaticMethod");
+        nonstaticMethodSubCalled = true;
+    }
 }
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 7929554..e914bd9 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -14,138 +14,57 @@
  * limitations under the License.
  */
 
-#include <stdio.h>
-#include <memory>
-
-#include "class_linker.h"
-#include "dex_file-inl.h"
-#include "gc_map.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
-#include "thread.h"
+#include "check_reference_map_visitor.h"
 #include "jni.h"
-#include "verifier/method_verifier.h"
 
 namespace art {
 
-#define IS_IN_REF_BITMAP(ref_bitmap, reg) \
-    (((reg) < m->GetCodeItem()->registers_size_) && \
-     ((*((ref_bitmap) + (reg)/8) >> ((reg) % 8) ) & 0x01))
+#define CHECK_REGS_CONTAIN_REFS(native_pc_offset, ...) do { \
+  int t[] = {__VA_ARGS__}; \
+  int t_size = sizeof(t) / sizeof(*t); \
+  CheckReferences(t, t_size, m->NativePcOffset(m->ToNativePc(native_pc_offset))); \
+} while (false);
 
-#define CHECK_REGS_CONTAIN_REFS(...)     \
-  do {                                   \
-    int t[] = {__VA_ARGS__};             \
-    int t_size = sizeof(t) / sizeof(*t);      \
-    for (int i = 0; i < t_size; ++i)          \
-      CHECK(IS_IN_REF_BITMAP(ref_bitmap, t[i])) \
-          << "Error: Reg @ " << i << "-th argument is not in GC map"; \
-  } while (false)
-
-struct ReferenceMap2Visitor : public StackVisitor {
-  explicit ReferenceMap2Visitor(Thread* thread)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(thread, NULL) {
-  }
+struct ReferenceMap2Visitor : public CheckReferenceMapVisitor {
+  explicit ReferenceMap2Visitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : CheckReferenceMapVisitor(thread) {}
 
   bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (CheckReferenceMapVisitor::VisitFrame()) {
+      return true;
+    }
     mirror::ArtMethod* m = GetMethod();
-    if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) {
-      return true;
-    }
-    LOG(INFO) << "At " << PrettyMethod(m, false);
-
-    NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
-
-    if (m->IsCalleeSaveMethod()) {
-      LOG(WARNING) << "no PC for " << PrettyMethod(m);
-      return true;
-    }
-
-    const uint8_t* ref_bitmap = NULL;
     std::string m_name(m->GetName());
 
     // Given the method name and the number of times the method has been called,
     // we know the Dex registers with live reference values. Assert that what we
     // find is what is expected.
     if (m_name.compare("f") == 0) {
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x03U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8);  // v8: this
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x06U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 1);  // v8: this, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x08U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 3, 1);  // v8: this, v3: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x0cU)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 3, 1);  // v8: this, v3: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x0eU)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 3, 1);  // v8: this, v3: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x10U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 3, 1);  // v8: this, v3: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x13U)));
-      CHECK(ref_bitmap);
+      CHECK_REGS_CONTAIN_REFS(0x03U, 8);  // v8: this
+      CHECK_REGS_CONTAIN_REFS(0x06U, 8, 1);  // v8: this, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x08U, 8, 3, 1);  // v8: this, v3: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x0cU, 8, 3, 1);  // v8: this, v3: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x0eU, 8, 3, 1);  // v8: this, v3: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x10U, 8, 3, 1);  // v8: this, v3: y, v1: x
       // v2 is added because of the instruction at DexPC 0024. Object merges with 0 is Object. See:
       //   0024: move-object v3, v2
       //   0025: goto 0013
       // Detaled dex instructions for ReferenceMap.java are at the end of this function.
       // CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1);  // v8: this, v3: y, v2: y, v1: x
       // We eliminate the non-live registers at a return, so only v3 is live:
-      CHECK_REGS_CONTAIN_REFS(3);  // v3: y
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x18U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1aU)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 5, 2, 1, 0);  // v8: this, v5: x[1], v2: y, v1: x, v0: ex
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1dU)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 5, 2, 1, 0);  // v8: this, v5: x[1], v2: y, v1: x, v0: ex
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1fU)));
-      CHECK(ref_bitmap);
+      CHECK_REGS_CONTAIN_REFS(0x13U);  // v3: y
+      CHECK_REGS_CONTAIN_REFS(0x18U, 8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x1aU, 8, 5, 2, 1, 0);  // v8: this, v5: x[1], v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x1dU, 8, 5, 2, 1, 0);  // v8: this, v5: x[1], v2: y, v1: x, v0: ex
       // v5 is removed from the root set because there is a "merge" operation.
       // See 0015: if-nez v2, 001f.
-      CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x21U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x27U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x29U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x2cU)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x2fU)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 4, 3, 2, 1);  // v8: this, v4: ex, v3: y, v2: y, v1: x
-
-      ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x32U)));
-      CHECK(ref_bitmap);
-      CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1, 0);  // v8: this, v3: y, v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x1fU, 8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x21U, 8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
+      CHECK_REGS_CONTAIN_REFS(0x27U, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x29U, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x2cU, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x2fU, 8, 4, 3, 2, 1);  // v8: this, v4: ex, v3: y, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x32U, 8, 3, 2, 1, 0);  // v8: this, v3: y, v2: y, v1: x, v0: ex
     }
 
     return true;
diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc
index f09fc26..a6d9b66 100644
--- a/test/004-SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -87,12 +87,12 @@
 
 // Prevent the compiler being a smart-alec and optimizing out the assignment
 // to nullptr.
-char *p = nullptr;
+char *go_away_compiler = nullptr;
 
 extern "C" JNIEXPORT jint JNICALL Java_Main_testSignal(JNIEnv*, jclass) {
 #if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
   // On supported architectures we cause a real SEGV.
-  *p = 'a';
+  *go_away_compiler = 'a';
 #else
   // On other architectures we simulate SEGV.
   kill(getpid(), SIGSEGV);
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index 30a0d59..c40de7e 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -14,54 +14,29 @@
  * limitations under the License.
  */
 
-#include <stdio.h>
-#include <memory>
-
-#include "class_linker.h"
-#include "gc_map.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/object-inl.h"
+#include "check_reference_map_visitor.h"
 #include "jni.h"
-#include "scoped_thread_state_change.h"
 
 namespace art {
 
-#define REG(reg_bitmap, reg) \
-    (((reg) < m->GetCodeItem()->registers_size_) && \
-     ((*((reg_bitmap) + (reg)/8) >> ((reg) % 8) ) & 0x01))
-
-#define CHECK_REGS(...) if (!IsShadowFrame()) { \
-    int t[] = {__VA_ARGS__}; \
-    int t_size = sizeof(t) / sizeof(*t); \
-    for (int i = 0; i < t_size; ++i) \
-      CHECK(REG(reg_bitmap, t[i])) << "Error: Reg " << i << " is not in RegisterMap"; \
-  }
+#define CHECK_REGS(...) do { \
+  int t[] = {__VA_ARGS__}; \
+  int t_size = sizeof(t) / sizeof(*t); \
+  CheckReferences(t, t_size, GetNativePcOffset()); \
+} while (false);
 
 static int gJava_StackWalk_refmap_calls = 0;
 
-struct TestReferenceMapVisitor : public StackVisitor {
-  explicit TestReferenceMapVisitor(Thread* thread)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(thread, NULL) {
-  }
+class TestReferenceMapVisitor : public CheckReferenceMapVisitor {
+ public:
+  explicit TestReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : CheckReferenceMapVisitor(thread) {}
 
   bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* m = GetMethod();
-    CHECK(m != NULL);
-    LOG(INFO) << "At " << PrettyMethod(m, false);
-
-    if (m->IsCalleeSaveMethod() || m->IsNative()) {
-      LOG(WARNING) << "no PC for " << PrettyMethod(m);
-      CHECK_EQ(GetDexPc(), DexFile::kDexNoIndex);
+    if (CheckReferenceMapVisitor::VisitFrame()) {
       return true;
     }
-    const uint8_t* reg_bitmap = NULL;
-    if (!IsShadowFrame()) {
-      NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
-      reg_bitmap = map.FindBitMap(GetNativePcOffset());
-    }
+    mirror::ArtMethod* m = GetMethod();
     StringPiece m_name(m->GetName());
 
     // Given the method name and the number of times the method has been called,
diff --git a/test/012-math/expected.txt b/test/012-math/expected.txt
index af9708e..75a559e 100644
--- a/test/012-math/expected.txt
+++ b/test/012-math/expected.txt
@@ -30,3 +30,11 @@
 f:21.0
 f:3.0
 f:3.0
+0
+0
+0
+0
+0
+0
+0
+0
diff --git a/test/012-math/src/Main.java b/test/012-math/src/Main.java
index a4a8c71..07b7540 100644
--- a/test/012-math/src/Main.java
+++ b/test/012-math/src/Main.java
@@ -99,7 +99,27 @@
         f %= g;
         System.out.println("f:" +f);
     }
+    public static void math_012_numerator(int a, int b, int d, int e, int f) {
+        int c = 0;
+        c /= b;
+        System.out.println(c);
+        c %= b;
+        System.out.println(c);
+        c = a / b;
+        System.out.println(c);
+        c = a % b;
+        System.out.println(c);
+        c = c / d;
+        System.out.println(c);
+        c = c / e;
+        System.out.println(c);
+        c = c / f;
+        System.out.println(c);
+        c = c % f;
+        System.out.println(c);
+    }
     public static void main(String args[]) {
       math_012();
+      math_012_numerator(0, 3, -1, 4, 5);
     }
 }
diff --git a/test/021-string2/src/junit/framework/Assert.java b/test/021-string2/src/junit/framework/Assert.java
index 364e646..3dcc23d 100644
--- a/test/021-string2/src/junit/framework/Assert.java
+++ b/test/021-string2/src/junit/framework/Assert.java
@@ -5,287 +5,292 @@
  */
 
 public class Assert {
-    /**
-     * Protect constructor since it is a static only class
-     */
-    protected Assert() {
-    }
+	/**
+	 * Protect constructor since it is a static only class
+	 */
+	protected Assert() {
+	}
 
-    /**
-     * Asserts that a condition is true. If it isn't it throws
-     * an AssertionFailedError with the given message.
-     */
-    static public void assertTrue(String message, boolean condition) {
-        if (!condition)
-            fail(message);
-    }
-    /**
-     * Asserts that a condition is true. If it isn't it throws
-     * an AssertionFailedError.
-     */
-    static public void assertTrue(boolean condition) {
-        assertTrue(null, condition);
-    }
-    /**
-     * Asserts that a condition is false. If it isn't it throws
-     * an AssertionFailedError with the given message.
-     */
-    static public void assertFalse(String message, boolean condition) {
-        assertTrue(message, !condition);
-    }
-    /**
-     * Asserts that a condition is false. If it isn't it throws
-     * an AssertionFailedError.
-     */
-    static public void assertFalse(boolean condition) {
-        assertFalse(null, condition);
-    }
-    /**
-     * Fails a test with the given message.
-     */
-    static public void fail(String message) {
-        throw new AssertionFailedError(message);
-    }
-    /**
-     * Fails a test with no message.
-     */
-    static public void fail() {
-        fail(null);
-    }
-    /**
-     * Asserts that two objects are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, Object expected, Object actual) {
-        if (expected == null && actual == null)
-            return;
-        if (expected != null && expected.equals(actual))
-            return;
-        failNotEquals(message, expected, actual);
-    }
-    /**
-     * Asserts that two objects are equal. If they are not
-     * an AssertionFailedError is thrown.
-     */
-    static public void assertEquals(Object expected, Object actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two Strings are equal.
-     */
-    static public void assertEquals(String message, String expected, String actual) {
-        if (expected == null && actual == null)
-            return;
-        if (expected != null && expected.equals(actual))
-            return;
-        throw new ComparisonFailure(message, expected, actual);
-    }
-    /**
-     * Asserts that two Strings are equal.
-     */
-    static public void assertEquals(String expected, String actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two doubles are equal concerning a delta.  If they are not
-     * an AssertionFailedError is thrown with the given message.  If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(String message, double expected, double actual, double delta) {
-        // handle infinity specially since subtracting to infinite values gives NaN and the
-        // the following test fails
-        if (Double.isInfinite(expected)) {
-            if (!(expected == actual))
-                failNotEquals(message, new Double(expected), new Double(actual));
-        } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
-            failNotEquals(message, new Double(expected), new Double(actual));
-    }
-    /**
-     * Asserts that two doubles are equal concerning a delta. If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(double expected, double actual, double delta) {
-        assertEquals(null, expected, actual, delta);
-    }
-    /**
-     * Asserts that two floats are equal concerning a delta. If they are not
-     * an AssertionFailedError is thrown with the given message.  If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(String message, float expected, float actual, float delta) {
-         // handle infinity specially since subtracting to infinite values gives NaN and the
-        // the following test fails
-        if (Float.isInfinite(expected)) {
-            if (!(expected == actual))
-                failNotEquals(message, new Float(expected), new Float(actual));
-        } else if (!(Math.abs(expected-actual) <= delta))
-              failNotEquals(message, new Float(expected), new Float(actual));
-    }
-    /**
-     * Asserts that two floats are equal concerning a delta. If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(float expected, float actual, float delta) {
-        assertEquals(null, expected, actual, delta);
-    }
-    /**
-     * Asserts that two longs are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, long expected, long actual) {
-        assertEquals(message, new Long(expected), new Long(actual));
-    }
-    /**
-     * Asserts that two longs are equal.
-     */
-    static public void assertEquals(long expected, long actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two booleans are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, boolean expected, boolean actual) {
-            assertEquals(message, new Boolean(expected), new Boolean(actual));
-      }
-    /**
-     * Asserts that two booleans are equal.
-      */
-    static public void assertEquals(boolean expected, boolean actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two bytes are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, byte expected, byte actual) {
-        assertEquals(message, new Byte(expected), new Byte(actual));
-    }
-    /**
-        * Asserts that two bytes are equal.
-     */
-    static public void assertEquals(byte expected, byte actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two chars are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, char expected, char actual) {
-            assertEquals(message, new Character(expected), new Character(actual));
-      }
-    /**
-     * Asserts that two chars are equal.
-     */
-      static public void assertEquals(char expected, char actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two shorts are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, short expected, short actual) {
-            assertEquals(message, new Short(expected), new Short(actual));
-    }
-      /**
-     * Asserts that two shorts are equal.
-     */
-    static public void assertEquals(short expected, short actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two ints are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, int expected, int actual) {
-        assertEquals(message, new Integer(expected), new Integer(actual));
-      }
-      /**
-        * Asserts that two ints are equal.
-     */
-      static public void assertEquals(int expected, int actual) {
-          assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that an object isn't null.
-     */
-    static public void assertNotNull(Object object) {
-        assertNotNull(null, object);
-    }
-    /**
-     * Asserts that an object isn't null. If it is
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertNotNull(String message, Object object) {
-        assertTrue(message, object != null);
-    }
-    /**
-     * Asserts that an object is null.
-     */
-    static public void assertNull(Object object) {
-        assertNull(null, object);
-    }
-    /**
-     * Asserts that an object is null.  If it is not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertNull(String message, Object object) {
-        assertTrue(message, object == null);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertSame(String message, Object expected, Object actual) {
-        if (expected == actual)
-            return;
-        failNotSame(message, expected, actual);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * the same an AssertionFailedError is thrown.
-     */
-    static public void assertSame(Object expected, Object actual) {
-        assertSame(null, expected, actual);
-    }
-     /**
-      * Asserts that two objects refer to the same object. If they are not
-      * an AssertionFailedError is thrown with the given message.
-      */
-    static public void assertNotSame(String message, Object expected, Object actual) {
-        if (expected == actual)
-            failSame(message);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * the same an AssertionFailedError is thrown.
-     */
-    static public void assertNotSame(Object expected, Object actual) {
-        assertNotSame(null, expected, actual);
-    }
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertTrue(String message, boolean condition) {
+		if (!condition)
+			fail(message);
+	}
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertTrue(boolean condition) {
+		assertTrue(null, condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertFalse(String message, boolean condition) {
+		assertTrue(message, !condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertFalse(boolean condition) {
+		assertFalse(null, condition);
+	}
+	/**
+	 * Fails a test with the given message.
+	 */
+	static public void fail(String message) {
+		if (message == null) {
+			throw new AssertionFailedError();
+		}
+		throw new AssertionFailedError(message);
+	}
+	/**
+	 * Fails a test with no message.
+	 */
+	static public void fail() {
+		fail(null);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, Object expected, Object actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		failNotEquals(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown.
+	 */
+	static public void assertEquals(Object expected, Object actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String message, String expected, String actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		String cleanMessage= message == null ? "" : message;
+		throw new ComparisonFailure(cleanMessage, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String expected, String actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta.  If they are not
+	 * an AssertionFailedError is thrown with the given message.  If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, double expected, double actual, double delta) {
+		if (Double.compare(expected, actual) == 0)
+			return;
+		if (!(Math.abs(expected-actual) <= delta))
+			failNotEquals(message, new Double(expected), new Double(actual));
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(double expected, double actual, double delta) {
+	    assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two floats are equal concerning a positive delta. If they
+	 * are not an AssertionFailedError is thrown with the given message. If the
+	 * expected value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, float expected, float actual, float delta) {
+		if (Float.compare(expected, actual) == 0)
+			return;
+		if (!(Math.abs(expected - actual) <= delta))
+				failNotEquals(message, new Float(expected), new Float(actual));
+	}
+	/**
+	 * Asserts that two floats are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(float expected, float actual, float delta) {
+		assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two longs are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, long expected, long actual) {
+	    assertEquals(message, new Long(expected), new Long(actual));
+	}
+	/**
+	 * Asserts that two longs are equal.
+	 */
+	static public void assertEquals(long expected, long actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two booleans are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, boolean expected, boolean actual) {
+    		assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+  	}
+	/**
+	 * Asserts that two booleans are equal.
+ 	 */
+	static public void assertEquals(boolean expected, boolean actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two bytes are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, byte expected, byte actual) {
+		assertEquals(message, new Byte(expected), new Byte(actual));
+	}
+	/**
+   	 * Asserts that two bytes are equal.
+	 */
+	static public void assertEquals(byte expected, byte actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two chars are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, char expected, char actual) {
+    		assertEquals(message, new Character(expected), new Character(actual));
+  	}
+	/**
+	 * Asserts that two chars are equal.
+	 */
+  	static public void assertEquals(char expected, char actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two shorts are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, short expected, short actual) {
+    		assertEquals(message, new Short(expected), new Short(actual));
+	}
+  	/**
+	 * Asserts that two shorts are equal.
+	 */
+	static public void assertEquals(short expected, short actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two ints are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, int expected, int actual) {
+		assertEquals(message, new Integer(expected), new Integer(actual));
+  	}
+  	/**
+   	 * Asserts that two ints are equal.
+	 */
+  	static public void assertEquals(int expected, int actual) {
+  		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that an object isn't null.
+	 */
+	static public void assertNotNull(Object object) {
+		assertNotNull(null, object);
+	}
+	/**
+	 * Asserts that an object isn't null. If it is
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNotNull(String message, Object object) {
+		assertTrue(message, object != null);
+	}
+	/**
+	 * Asserts that an object is null. If it isn't an {@link AssertionError} is
+	 * thrown.
+	 * Message contains: Expected: <null> but was: object
+	 * 
+	 * @param object
+	 *            Object to check or <code>null</code>
+	 */
+	static public void assertNull(Object object) {
+		String message = "Expected: <null> but was: " + String.valueOf(object);
+		assertNull(message, object);
+	}
+	/**
+	 * Asserts that an object is null.  If it is not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNull(String message, Object object) {
+		assertTrue(message, object == null);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			return;
+		failNotSame(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * the same an AssertionFailedError is thrown.
+	 */
+	static public void assertSame(Object expected, Object actual) {
+	    assertSame(null, expected, actual);
+	}
+	/**
+	 * Asserts that two objects do not refer to the same object. If they do
+	 * refer to the same object an AssertionFailedError is thrown with the
+	 * given message.
+	 */
+	static public void assertNotSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			failSame(message);
+	}
+	/**
+	 * Asserts that two objects do not refer to the same object. If they do
+	 * refer to the same object an AssertionFailedError is thrown.
+	 */
+	static public void assertNotSame(Object expected, Object actual) {
+		assertNotSame(null, expected, actual);
+	}
 
-    static private void failSame(String message) {
-        String formatted= "";
-         if (message != null)
-             formatted= message+" ";
-         fail(formatted+"expected not same");
-    }
+	static public void failSame(String message) {
+		String formatted= "";
+ 		if (message != null)
+ 			formatted= message+" ";
+ 		fail(formatted+"expected not same");
+	}
 
-    static private void failNotSame(String message, Object expected, Object actual) {
-        String formatted= "";
-        if (message != null)
-            formatted= message+" ";
-        fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
-    }
+	static public void failNotSame(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null)
+			formatted= message+" ";
+		fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+	}
 
-    static private void failNotEquals(String message, Object expected, Object actual) {
-        fail(format(message, expected, actual));
-    }
+	static public void failNotEquals(String message, Object expected, Object actual) {
+		fail(format(message, expected, actual));
+	}
 
-    static String format(String message, Object expected, Object actual) {
-        String formatted= "";
-        if (message != null)
-            formatted= message+" ";
-        return formatted+"expected:<"+expected+"> but was:<"+actual+">";
-    }
+	public static String format(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null && message.length() > 0)
+			formatted= message+" ";
+		return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
 }
diff --git a/test/021-string2/src/junit/framework/AssertionFailedError.java b/test/021-string2/src/junit/framework/AssertionFailedError.java
index e9cb3a3..0d7802c 100644
--- a/test/021-string2/src/junit/framework/AssertionFailedError.java
+++ b/test/021-string2/src/junit/framework/AssertionFailedError.java
@@ -3,11 +3,18 @@
 /**
  * Thrown when an assertion failed.
  */
-public class AssertionFailedError extends Error {
+public class AssertionFailedError extends AssertionError {
 
-    public AssertionFailedError () {
-    }
-    public AssertionFailedError (String message) {
-        super (message);
-    }
-}
+	private static final long serialVersionUID= 1L;
+
+	public AssertionFailedError() {
+	}
+
+	public AssertionFailedError(String message) {
+		super(defaultString(message));
+	}
+
+	private static String defaultString(String message) {
+		return message == null ? "" : message;
+	}
+}
\ No newline at end of file
diff --git a/test/021-string2/src/junit/framework/ComparisonCompactor.java b/test/021-string2/src/junit/framework/ComparisonCompactor.java
new file mode 100644
index 0000000..e540f03
--- /dev/null
+++ b/test/021-string2/src/junit/framework/ComparisonCompactor.java
@@ -0,0 +1,87 @@
+package junit.framework;
+
+// android-changed add @hide
+/**
+ * @hide not needed for public API
+ */
+public class ComparisonCompactor {
+
+	private static final String ELLIPSIS= "...";
+	private static final String DELTA_END= "]";
+	private static final String DELTA_START= "[";
+	
+	private int fContextLength;
+	private String fExpected;
+	private String fActual;
+	private int fPrefix;
+	private int fSuffix;
+
+	public ComparisonCompactor(int contextLength, String expected, String actual) {
+		fContextLength= contextLength;
+		fExpected= expected;
+		fActual= actual;
+	}
+
+	public String compact(String message) {
+		if (fExpected == null || fActual == null || areStringsEqual()) {
+			// android-changed use local method instead of Assert.format, since
+			// the later is not part of Android API till API 16
+			return format(message, fExpected, fActual);
+		}
+		findCommonPrefix();
+		findCommonSuffix();
+		String expected= compactString(fExpected);
+		String actual= compactString(fActual);
+		// android-changed use local format method
+		return format(message, expected, actual);
+	}
+
+	private String compactString(String source) {
+		String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
+		if (fPrefix > 0)
+			result= computeCommonPrefix() + result;
+		if (fSuffix > 0)
+			result= result + computeCommonSuffix();
+		return result;
+	}
+
+	private void findCommonPrefix() {
+		fPrefix= 0;
+		int end= Math.min(fExpected.length(), fActual.length());
+		for (; fPrefix < end; fPrefix++) {
+			if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
+				break;
+		}
+	}
+
+	private void findCommonSuffix() {
+		int expectedSuffix= fExpected.length() - 1;
+		int actualSuffix= fActual.length() - 1;
+		for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
+			if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
+				break;
+		}
+		fSuffix=  fExpected.length() - expectedSuffix;
+	}
+
+	private String computeCommonPrefix() {
+		return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
+	}
+
+	private String computeCommonSuffix() {
+		int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
+		return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
+	}
+
+	private boolean areStringsEqual() {
+		return fExpected.equals(fActual);
+	}
+
+	// android-changed copy of Assert.format for reasons described above
+	private static String format(String message, Object expected, Object actual) {
+        	String formatted= "";
+        	if (message != null && message.length() > 0)
+            		formatted= message+" ";
+        	return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
+}
diff --git a/test/021-string2/src/junit/framework/ComparisonFailure.java b/test/021-string2/src/junit/framework/ComparisonFailure.java
index ccd476b..5077993 100644
--- a/test/021-string2/src/junit/framework/ComparisonFailure.java
+++ b/test/021-string2/src/junit/framework/ComparisonFailure.java
@@ -2,67 +2,51 @@
 
 /**
  * Thrown when an assert equals for Strings failed.
- *
+ * 
  * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
  */
 public class ComparisonFailure extends AssertionFailedError {
-    private String fExpected;
-    private String fActual;
+	private static final int MAX_CONTEXT_LENGTH= 20;
+	private static final long serialVersionUID= 1L;
+	
+	private String fExpected;
+	private String fActual;
 
-    /**
-     * Constructs a comparison failure.
-     * @param message the identifying message or null
-     * @param expected the expected string value
-     * @param actual the actual string value
-     */
-    public ComparisonFailure (String message, String expected, String actual) {
-        super (message);
-        fExpected= expected;
-        fActual= actual;
-    }
-
-    /**
-     * Returns "..." in place of common prefix and "..." in
-     * place of common suffix between expected and actual.
-     *
-     * @see java.lang.Throwable#getMessage()
-     */
-    public String getMessage() {
-        if (fExpected == null || fActual == null)
-            return Assert.format(super.getMessage(), fExpected, fActual);
-
-        int end= Math.min(fExpected.length(), fActual.length());
-
-        int i= 0;
-        for (; i < end; i++) {
-            if (fExpected.charAt(i) != fActual.charAt(i))
-                break;
-        }
-        int j= fExpected.length()-1;
-        int k= fActual.length()-1;
-        for (; k >= i && j >= i; k--,j--) {
-            if (fExpected.charAt(j) != fActual.charAt(k))
-                break;
-        }
-        String actual, expected;
-
-        // equal strings
-        if (j < i && k < i) {
-            expected= fExpected;
-            actual= fActual;
-        } else {
-            expected= fExpected.substring(i, j+1);
-            actual= fActual.substring(i, k+1);
-            if (i <= end && i > 0) {
-                expected= "..."+expected;
-                actual= "..."+actual;
-            }
-
-            if (j < fExpected.length()-1)
-                expected= expected+"...";
-            if (k < fActual.length()-1)
-                actual= actual+"...";
-        }
-        return Assert.format(super.getMessage(), expected, actual);
-    }
-}
+	/**
+	 * Constructs a comparison failure.
+	 * @param message the identifying message or null
+	 * @param expected the expected string value
+	 * @param actual the actual string value
+	 */
+	public ComparisonFailure (String message, String expected, String actual) {
+		super (message);
+		fExpected= expected;
+		fActual= actual;
+	}
+	
+	/**
+	 * Returns "..." in place of common prefix and "..." in
+	 * place of common suffix between expected and actual.
+	 * 
+	 * @see Throwable#getMessage()
+	 */
+	@Override
+	public String getMessage() {
+		return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+	}
+	
+	/**
+	 * Gets the actual string value
+	 * @return the actual string value
+	 */
+	public String getActual() {
+		return fActual;
+	}
+	/**
+	 * Gets the expected string value
+	 * @return the expected string value
+	 */
+	public String getExpected() {
+		return fExpected;
+	}
+}
\ No newline at end of file
diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt
index a2a74fc..36fa5f8 100644
--- a/test/036-finalizer/expected.txt
+++ b/test/036-finalizer/expected.txt
@@ -11,3 +11,4 @@
 sleep
 reborn: [FinalizerTest message=nothing, finalized=false]
 wimp: null
+Finalized 1024 / 1024
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 328425f..390472d 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -120,6 +120,39 @@
 
         System.out.println("reborn: " + FinalizerTest.mReborn);
         System.out.println("wimp: " + wimpString(wimp));
+        // Test runFinalization with multiple objects.
+        runFinalizationTest();
+    }
+
+    static class FinalizeCounter {
+      private static Object finalizeLock = new Object();
+      private static volatile int finalizeCount = 0;
+      private int index;
+      static int getCount() {
+        return finalizeCount;
+      }
+      FinalizeCounter(int index) {
+        this.index = index;
+      }
+      protected void finalize() {
+        synchronized(finalizeLock) {
+          ++finalizeCount;
+        }
+      }
+    }
+
+    private static void runFinalizationTest() {
+      int count = 1024;
+      Object[] objs = new Object[count];
+      for (int i = 0; i < count; ++i) {
+        objs[i] = new FinalizeCounter(i);
+      }
+      for (int i = 0; i < count; ++i) {
+        objs[i] = null;
+      }
+      System.gc();
+      System.runFinalization();
+      System.out.println("Finalized " + FinalizeCounter.getCount() + " / "  + count);
     }
 
     public static class FinalizerTest {
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index ecb3599..fa053fb 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -123,3 +123,17 @@
 fields are .equals
 methods are unique
 methods are .equals
+type1 is a ParameterizedType
+type2 is a ParameterizedType
+type3 is a ParameterizedType
+type1(java.util.Set<java.lang.String>) equals type2(java.util.Set<java.lang.String>)
+type1(java.util.Set<java.lang.String>) equals type3(java.util.Set<java.lang.String>)
+type1(java.util.Set<java.lang.String>) hashCode equals type2(java.util.Set<java.lang.String>) hashCode
+type1(java.util.Set<java.lang.String>) hashCode equals type3(java.util.Set<java.lang.String>) hashCode
+type1 is a GenericArrayType
+type2 is a GenericArrayType
+type3 is a GenericArrayType
+type1(T[]) equals type2(T[])
+type1(T[]) equals type3(T[])
+type1(T[]) hashCode equals type2(T[]) hashCode
+type1(T[]) hashCode equals type3(T[]) hashCode
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index 3e6d700..3fe3881 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -18,8 +18,10 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Reflection test.
@@ -579,16 +581,155 @@
         }
     }
 
+    public static void checkParametrizedTypeEqualsAndHashCode() {
+        Method method1;
+        Method method2;
+        Method method3;
+        try {
+            method1 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
+            method2 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
+            method3 = ParametrizedTypeTest.class.getDeclaredMethod("aMethodIdentical", Set.class);
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+
+        List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
+        List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
+        List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
+
+        Type type1 = types1.get(0);
+        Type type2 = types2.get(0);
+        Type type3 = types3.get(0);
+
+        if (type1 instanceof ParameterizedType) {
+            System.out.println("type1 is a ParameterizedType");
+        }
+        if (type2 instanceof ParameterizedType) {
+            System.out.println("type2 is a ParameterizedType");
+        }
+        if (type3 instanceof ParameterizedType) {
+            System.out.println("type3 is a ParameterizedType");
+        }
+
+        if (type1.equals(type2)) {
+            System.out.println("type1("+type1+") equals type2("+type2+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type2("+type2+")");
+        }
+
+        if (type1.equals(type3)) {
+            System.out.println("type1("+type1+") equals type3("+type3+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type3("+type3+")");
+        }
+        if (type1.hashCode() == type2.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
+        } else {
+            System.out.println(
+                   "type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
+        }
+
+        if (type1.hashCode() == type3.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
+        } else {
+            System.out.println(
+                    "type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
+        }
+    }
+
+    public static void checkGenericArrayTypeEqualsAndHashCode() {
+        Method method1;
+        Method method2;
+        Method method3;
+        try {
+            method1 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
+            method2 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
+            method3 = GenericArrayTypeTest.class.getDeclaredMethod("aMethodIdentical", Object[].class);
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+
+        List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
+        List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
+        List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
+
+        Type type1 = types1.get(0);
+        Type type2 = types2.get(0);
+        Type type3 = types3.get(0);
+
+        if (type1 instanceof GenericArrayType) {
+            System.out.println("type1 is a GenericArrayType");
+        }
+        if (type2 instanceof GenericArrayType) {
+            System.out.println("type2 is a GenericArrayType");
+        }
+        if (type3 instanceof GenericArrayType) {
+            System.out.println("type3 is a GenericArrayType");
+        }
+
+        if (type1.equals(type2)) {
+            System.out.println("type1("+type1+") equals type2("+type2+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type2("+type2+")");
+        }
+
+        if (type1.equals(type3)) {
+            System.out.println("type1("+type1+") equals type3("+type3+")");
+        } else {
+            System.out.println("type1("+type1+") does not equal type3("+type3+")");
+        }
+        if (type1.hashCode() == type2.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
+        } else {
+            System.out.println(
+                   "type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
+        }
+
+        if (type1.hashCode() == type3.hashCode()) {
+            System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
+        } else {
+            System.out.println(
+                    "type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
+        }
+    }
+
+    private static void checkGetDeclaredConstructor() {
+        try {
+            Method.class.getDeclaredConstructor().setAccessible(true);
+            System.out.print("Didn't get an exception from method getDeclaredConstructor");
+        } catch (NoSuchMethodException e) {
+        } catch (Exception e) {
+            System.out.print(e);
+        }
+        try {
+            Field.class.getDeclaredConstructor().setAccessible(true);
+            System.out.print("Didn't get an exception from field getDeclaredConstructor");
+        } catch (NoSuchMethodException e) {
+        } catch (Exception e) {
+            System.out.print(e);
+        }
+        try {
+            Class.class.getDeclaredConstructor().setAccessible(true);
+            System.out.print("Didn't get an exception from class getDeclaredConstructor()");
+        } catch (SecurityException e) {
+        } catch (Exception e) {
+            System.out.print(e);
+        }
+    }
+
     public static void main(String[] args) throws Exception {
         Main test = new Main();
         test.run();
 
+        checkGetDeclaredConstructor();
         checkAccess();
         checkType();
         checkClinitForFields();
         checkClinitForMethods();
         checkGeneric();
         checkUnique();
+        checkParametrizedTypeEqualsAndHashCode();
+        checkGenericArrayTypeEqualsAndHashCode();
     }
 }
 
@@ -696,3 +837,13 @@
     throw new UnsupportedOperationException();
   }
 }
+
+class ParametrizedTypeTest {
+    public void aMethod(Set<String> names) {}
+    public void aMethodIdentical(Set<String> names) {}
+}
+
+class GenericArrayTypeTest<T> {
+    public void aMethod(T[] names) {}
+    public void aMethodIdentical(T[] names) {}
+}
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index 035690f..c93f8bb 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -31,6 +31,7 @@
 
     static class InstanceMemEater {
         static boolean sawOome;
+        static InstanceMemEater hook;
 
         InstanceMemEater next;
         double d1, d2, d3, d4, d5, d6, d7, d8; // Bloat this object so we fill the heap faster.
@@ -45,6 +46,7 @@
         }
 
         static void confuseCompilerOptimization(InstanceMemEater instance) {
+          hook = instance;
         }
     }
 
@@ -61,6 +63,7 @@
             lastMemEater = lastMemEater.next;
         } while (lastMemEater != null);
         memEater.confuseCompilerOptimization(memEater);
+        InstanceMemEater.hook = null;
         return InstanceMemEater.sawOome;
     }
 
diff --git a/test/082-inline-execute/src/junit/framework/Assert.java b/test/082-inline-execute/src/junit/framework/Assert.java
index 364e646..3dcc23d 100644
--- a/test/082-inline-execute/src/junit/framework/Assert.java
+++ b/test/082-inline-execute/src/junit/framework/Assert.java
@@ -5,287 +5,292 @@
  */
 
 public class Assert {
-    /**
-     * Protect constructor since it is a static only class
-     */
-    protected Assert() {
-    }
+	/**
+	 * Protect constructor since it is a static only class
+	 */
+	protected Assert() {
+	}
 
-    /**
-     * Asserts that a condition is true. If it isn't it throws
-     * an AssertionFailedError with the given message.
-     */
-    static public void assertTrue(String message, boolean condition) {
-        if (!condition)
-            fail(message);
-    }
-    /**
-     * Asserts that a condition is true. If it isn't it throws
-     * an AssertionFailedError.
-     */
-    static public void assertTrue(boolean condition) {
-        assertTrue(null, condition);
-    }
-    /**
-     * Asserts that a condition is false. If it isn't it throws
-     * an AssertionFailedError with the given message.
-     */
-    static public void assertFalse(String message, boolean condition) {
-        assertTrue(message, !condition);
-    }
-    /**
-     * Asserts that a condition is false. If it isn't it throws
-     * an AssertionFailedError.
-     */
-    static public void assertFalse(boolean condition) {
-        assertFalse(null, condition);
-    }
-    /**
-     * Fails a test with the given message.
-     */
-    static public void fail(String message) {
-        throw new AssertionFailedError(message);
-    }
-    /**
-     * Fails a test with no message.
-     */
-    static public void fail() {
-        fail(null);
-    }
-    /**
-     * Asserts that two objects are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, Object expected, Object actual) {
-        if (expected == null && actual == null)
-            return;
-        if (expected != null && expected.equals(actual))
-            return;
-        failNotEquals(message, expected, actual);
-    }
-    /**
-     * Asserts that two objects are equal. If they are not
-     * an AssertionFailedError is thrown.
-     */
-    static public void assertEquals(Object expected, Object actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two Strings are equal.
-     */
-    static public void assertEquals(String message, String expected, String actual) {
-        if (expected == null && actual == null)
-            return;
-        if (expected != null && expected.equals(actual))
-            return;
-        throw new ComparisonFailure(message, expected, actual);
-    }
-    /**
-     * Asserts that two Strings are equal.
-     */
-    static public void assertEquals(String expected, String actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two doubles are equal concerning a delta.  If they are not
-     * an AssertionFailedError is thrown with the given message.  If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(String message, double expected, double actual, double delta) {
-        // handle infinity specially since subtracting to infinite values gives NaN and the
-        // the following test fails
-        if (Double.isInfinite(expected)) {
-            if (!(expected == actual))
-                failNotEquals(message, new Double(expected), new Double(actual));
-        } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
-            failNotEquals(message, new Double(expected), new Double(actual));
-    }
-    /**
-     * Asserts that two doubles are equal concerning a delta. If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(double expected, double actual, double delta) {
-        assertEquals(null, expected, actual, delta);
-    }
-    /**
-     * Asserts that two floats are equal concerning a delta. If they are not
-     * an AssertionFailedError is thrown with the given message.  If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(String message, float expected, float actual, float delta) {
-         // handle infinity specially since subtracting to infinite values gives NaN and the
-        // the following test fails
-        if (Float.isInfinite(expected)) {
-            if (!(expected == actual))
-                failNotEquals(message, new Float(expected), new Float(actual));
-        } else if (!(Math.abs(expected-actual) <= delta))
-              failNotEquals(message, new Float(expected), new Float(actual));
-    }
-    /**
-     * Asserts that two floats are equal concerning a delta. If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(float expected, float actual, float delta) {
-        assertEquals(null, expected, actual, delta);
-    }
-    /**
-     * Asserts that two longs are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, long expected, long actual) {
-        assertEquals(message, new Long(expected), new Long(actual));
-    }
-    /**
-     * Asserts that two longs are equal.
-     */
-    static public void assertEquals(long expected, long actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two booleans are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, boolean expected, boolean actual) {
-            assertEquals(message, new Boolean(expected), new Boolean(actual));
-      }
-    /**
-     * Asserts that two booleans are equal.
-      */
-    static public void assertEquals(boolean expected, boolean actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two bytes are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, byte expected, byte actual) {
-        assertEquals(message, new Byte(expected), new Byte(actual));
-    }
-    /**
-        * Asserts that two bytes are equal.
-     */
-    static public void assertEquals(byte expected, byte actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two chars are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, char expected, char actual) {
-            assertEquals(message, new Character(expected), new Character(actual));
-      }
-    /**
-     * Asserts that two chars are equal.
-     */
-      static public void assertEquals(char expected, char actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two shorts are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, short expected, short actual) {
-            assertEquals(message, new Short(expected), new Short(actual));
-    }
-      /**
-     * Asserts that two shorts are equal.
-     */
-    static public void assertEquals(short expected, short actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two ints are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, int expected, int actual) {
-        assertEquals(message, new Integer(expected), new Integer(actual));
-      }
-      /**
-        * Asserts that two ints are equal.
-     */
-      static public void assertEquals(int expected, int actual) {
-          assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that an object isn't null.
-     */
-    static public void assertNotNull(Object object) {
-        assertNotNull(null, object);
-    }
-    /**
-     * Asserts that an object isn't null. If it is
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertNotNull(String message, Object object) {
-        assertTrue(message, object != null);
-    }
-    /**
-     * Asserts that an object is null.
-     */
-    static public void assertNull(Object object) {
-        assertNull(null, object);
-    }
-    /**
-     * Asserts that an object is null.  If it is not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertNull(String message, Object object) {
-        assertTrue(message, object == null);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertSame(String message, Object expected, Object actual) {
-        if (expected == actual)
-            return;
-        failNotSame(message, expected, actual);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * the same an AssertionFailedError is thrown.
-     */
-    static public void assertSame(Object expected, Object actual) {
-        assertSame(null, expected, actual);
-    }
-     /**
-      * Asserts that two objects refer to the same object. If they are not
-      * an AssertionFailedError is thrown with the given message.
-      */
-    static public void assertNotSame(String message, Object expected, Object actual) {
-        if (expected == actual)
-            failSame(message);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * the same an AssertionFailedError is thrown.
-     */
-    static public void assertNotSame(Object expected, Object actual) {
-        assertNotSame(null, expected, actual);
-    }
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertTrue(String message, boolean condition) {
+		if (!condition)
+			fail(message);
+	}
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertTrue(boolean condition) {
+		assertTrue(null, condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertFalse(String message, boolean condition) {
+		assertTrue(message, !condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertFalse(boolean condition) {
+		assertFalse(null, condition);
+	}
+	/**
+	 * Fails a test with the given message.
+	 */
+	static public void fail(String message) {
+		if (message == null) {
+			throw new AssertionFailedError();
+		}
+		throw new AssertionFailedError(message);
+	}
+	/**
+	 * Fails a test with no message.
+	 */
+	static public void fail() {
+		fail(null);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, Object expected, Object actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		failNotEquals(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown.
+	 */
+	static public void assertEquals(Object expected, Object actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String message, String expected, String actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		String cleanMessage= message == null ? "" : message;
+		throw new ComparisonFailure(cleanMessage, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String expected, String actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta.  If they are not
+	 * an AssertionFailedError is thrown with the given message.  If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, double expected, double actual, double delta) {
+		if (Double.compare(expected, actual) == 0)
+			return;
+		if (!(Math.abs(expected-actual) <= delta))
+			failNotEquals(message, new Double(expected), new Double(actual));
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(double expected, double actual, double delta) {
+	    assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two floats are equal concerning a positive delta. If they
+	 * are not an AssertionFailedError is thrown with the given message. If the
+	 * expected value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, float expected, float actual, float delta) {
+		if (Float.compare(expected, actual) == 0)
+			return;
+		if (!(Math.abs(expected - actual) <= delta))
+				failNotEquals(message, new Float(expected), new Float(actual));
+	}
+	/**
+	 * Asserts that two floats are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(float expected, float actual, float delta) {
+		assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two longs are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, long expected, long actual) {
+	    assertEquals(message, new Long(expected), new Long(actual));
+	}
+	/**
+	 * Asserts that two longs are equal.
+	 */
+	static public void assertEquals(long expected, long actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two booleans are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, boolean expected, boolean actual) {
+    		assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+  	}
+	/**
+	 * Asserts that two booleans are equal.
+ 	 */
+	static public void assertEquals(boolean expected, boolean actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two bytes are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, byte expected, byte actual) {
+		assertEquals(message, new Byte(expected), new Byte(actual));
+	}
+	/**
+   	 * Asserts that two bytes are equal.
+	 */
+	static public void assertEquals(byte expected, byte actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two chars are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, char expected, char actual) {
+    		assertEquals(message, new Character(expected), new Character(actual));
+  	}
+	/**
+	 * Asserts that two chars are equal.
+	 */
+  	static public void assertEquals(char expected, char actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two shorts are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, short expected, short actual) {
+    		assertEquals(message, new Short(expected), new Short(actual));
+	}
+  	/**
+	 * Asserts that two shorts are equal.
+	 */
+	static public void assertEquals(short expected, short actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two ints are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, int expected, int actual) {
+		assertEquals(message, new Integer(expected), new Integer(actual));
+  	}
+  	/**
+   	 * Asserts that two ints are equal.
+	 */
+  	static public void assertEquals(int expected, int actual) {
+  		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that an object isn't null.
+	 */
+	static public void assertNotNull(Object object) {
+		assertNotNull(null, object);
+	}
+	/**
+	 * Asserts that an object isn't null. If it is
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNotNull(String message, Object object) {
+		assertTrue(message, object != null);
+	}
+	/**
+	 * Asserts that an object is null. If it isn't an {@link AssertionError} is
+	 * thrown.
+	 * Message contains: Expected: <null> but was: object
+	 * 
+	 * @param object
+	 *            Object to check or <code>null</code>
+	 */
+	static public void assertNull(Object object) {
+		String message = "Expected: <null> but was: " + String.valueOf(object);
+		assertNull(message, object);
+	}
+	/**
+	 * Asserts that an object is null.  If it is not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNull(String message, Object object) {
+		assertTrue(message, object == null);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			return;
+		failNotSame(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * the same an AssertionFailedError is thrown.
+	 */
+	static public void assertSame(Object expected, Object actual) {
+	    assertSame(null, expected, actual);
+	}
+	/**
+	 * Asserts that two objects do not refer to the same object. If they do
+	 * refer to the same object an AssertionFailedError is thrown with the
+	 * given message.
+	 */
+	static public void assertNotSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			failSame(message);
+	}
+	/**
+	 * Asserts that two objects do not refer to the same object. If they do
+	 * refer to the same object an AssertionFailedError is thrown.
+	 */
+	static public void assertNotSame(Object expected, Object actual) {
+		assertNotSame(null, expected, actual);
+	}
 
-    static private void failSame(String message) {
-        String formatted= "";
-         if (message != null)
-             formatted= message+" ";
-         fail(formatted+"expected not same");
-    }
+	static public void failSame(String message) {
+		String formatted= "";
+ 		if (message != null)
+ 			formatted= message+" ";
+ 		fail(formatted+"expected not same");
+	}
 
-    static private void failNotSame(String message, Object expected, Object actual) {
-        String formatted= "";
-        if (message != null)
-            formatted= message+" ";
-        fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
-    }
+	static public void failNotSame(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null)
+			formatted= message+" ";
+		fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+	}
 
-    static private void failNotEquals(String message, Object expected, Object actual) {
-        fail(format(message, expected, actual));
-    }
+	static public void failNotEquals(String message, Object expected, Object actual) {
+		fail(format(message, expected, actual));
+	}
 
-    static String format(String message, Object expected, Object actual) {
-        String formatted= "";
-        if (message != null)
-            formatted= message+" ";
-        return formatted+"expected:<"+expected+"> but was:<"+actual+">";
-    }
+	public static String format(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null && message.length() > 0)
+			formatted= message+" ";
+		return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
 }
diff --git a/test/082-inline-execute/src/junit/framework/AssertionFailedError.java b/test/082-inline-execute/src/junit/framework/AssertionFailedError.java
index e9cb3a3..0d7802c 100644
--- a/test/082-inline-execute/src/junit/framework/AssertionFailedError.java
+++ b/test/082-inline-execute/src/junit/framework/AssertionFailedError.java
@@ -3,11 +3,18 @@
 /**
  * Thrown when an assertion failed.
  */
-public class AssertionFailedError extends Error {
+public class AssertionFailedError extends AssertionError {
 
-    public AssertionFailedError () {
-    }
-    public AssertionFailedError (String message) {
-        super (message);
-    }
-}
+	private static final long serialVersionUID= 1L;
+
+	public AssertionFailedError() {
+	}
+
+	public AssertionFailedError(String message) {
+		super(defaultString(message));
+	}
+
+	private static String defaultString(String message) {
+		return message == null ? "" : message;
+	}
+}
\ No newline at end of file
diff --git a/test/082-inline-execute/src/junit/framework/ComparisonCompactor.java b/test/082-inline-execute/src/junit/framework/ComparisonCompactor.java
new file mode 100644
index 0000000..e540f03
--- /dev/null
+++ b/test/082-inline-execute/src/junit/framework/ComparisonCompactor.java
@@ -0,0 +1,87 @@
+package junit.framework;
+
+// android-changed add @hide
+/**
+ * @hide not needed for public API
+ */
+public class ComparisonCompactor {
+
+	private static final String ELLIPSIS= "...";
+	private static final String DELTA_END= "]";
+	private static final String DELTA_START= "[";
+	
+	private int fContextLength;
+	private String fExpected;
+	private String fActual;
+	private int fPrefix;
+	private int fSuffix;
+
+	public ComparisonCompactor(int contextLength, String expected, String actual) {
+		fContextLength= contextLength;
+		fExpected= expected;
+		fActual= actual;
+	}
+
+	public String compact(String message) {
+		if (fExpected == null || fActual == null || areStringsEqual()) {
+			// android-changed use local method instead of Assert.format, since
+			// the later is not part of Android API till API 16
+			return format(message, fExpected, fActual);
+		}
+		findCommonPrefix();
+		findCommonSuffix();
+		String expected= compactString(fExpected);
+		String actual= compactString(fActual);
+		// android-changed use local format method
+		return format(message, expected, actual);
+	}
+
+	private String compactString(String source) {
+		String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
+		if (fPrefix > 0)
+			result= computeCommonPrefix() + result;
+		if (fSuffix > 0)
+			result= result + computeCommonSuffix();
+		return result;
+	}
+
+	private void findCommonPrefix() {
+		fPrefix= 0;
+		int end= Math.min(fExpected.length(), fActual.length());
+		for (; fPrefix < end; fPrefix++) {
+			if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
+				break;
+		}
+	}
+
+	private void findCommonSuffix() {
+		int expectedSuffix= fExpected.length() - 1;
+		int actualSuffix= fActual.length() - 1;
+		for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
+			if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
+				break;
+		}
+		fSuffix=  fExpected.length() - expectedSuffix;
+	}
+
+	private String computeCommonPrefix() {
+		return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
+	}
+
+	private String computeCommonSuffix() {
+		int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
+		return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
+	}
+
+	private boolean areStringsEqual() {
+		return fExpected.equals(fActual);
+	}
+
+	// android-changed copy of Assert.format for reasons described above
+	private static String format(String message, Object expected, Object actual) {
+        	String formatted= "";
+        	if (message != null && message.length() > 0)
+            		formatted= message+" ";
+        	return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
+}
diff --git a/test/082-inline-execute/src/junit/framework/ComparisonFailure.java b/test/082-inline-execute/src/junit/framework/ComparisonFailure.java
index ccd476b..5077993 100644
--- a/test/082-inline-execute/src/junit/framework/ComparisonFailure.java
+++ b/test/082-inline-execute/src/junit/framework/ComparisonFailure.java
@@ -2,67 +2,51 @@
 
 /**
  * Thrown when an assert equals for Strings failed.
- *
+ * 
  * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
  */
 public class ComparisonFailure extends AssertionFailedError {
-    private String fExpected;
-    private String fActual;
+	private static final int MAX_CONTEXT_LENGTH= 20;
+	private static final long serialVersionUID= 1L;
+	
+	private String fExpected;
+	private String fActual;
 
-    /**
-     * Constructs a comparison failure.
-     * @param message the identifying message or null
-     * @param expected the expected string value
-     * @param actual the actual string value
-     */
-    public ComparisonFailure (String message, String expected, String actual) {
-        super (message);
-        fExpected= expected;
-        fActual= actual;
-    }
-
-    /**
-     * Returns "..." in place of common prefix and "..." in
-     * place of common suffix between expected and actual.
-     *
-     * @see java.lang.Throwable#getMessage()
-     */
-    public String getMessage() {
-        if (fExpected == null || fActual == null)
-            return Assert.format(super.getMessage(), fExpected, fActual);
-
-        int end= Math.min(fExpected.length(), fActual.length());
-
-        int i= 0;
-        for (; i < end; i++) {
-            if (fExpected.charAt(i) != fActual.charAt(i))
-                break;
-        }
-        int j= fExpected.length()-1;
-        int k= fActual.length()-1;
-        for (; k >= i && j >= i; k--,j--) {
-            if (fExpected.charAt(j) != fActual.charAt(k))
-                break;
-        }
-        String actual, expected;
-
-        // equal strings
-        if (j < i && k < i) {
-            expected= fExpected;
-            actual= fActual;
-        } else {
-            expected= fExpected.substring(i, j+1);
-            actual= fActual.substring(i, k+1);
-            if (i <= end && i > 0) {
-                expected= "..."+expected;
-                actual= "..."+actual;
-            }
-
-            if (j < fExpected.length()-1)
-                expected= expected+"...";
-            if (k < fActual.length()-1)
-                actual= actual+"...";
-        }
-        return Assert.format(super.getMessage(), expected, actual);
-    }
-}
+	/**
+	 * Constructs a comparison failure.
+	 * @param message the identifying message or null
+	 * @param expected the expected string value
+	 * @param actual the actual string value
+	 */
+	public ComparisonFailure (String message, String expected, String actual) {
+		super (message);
+		fExpected= expected;
+		fActual= actual;
+	}
+	
+	/**
+	 * Returns "..." in place of common prefix and "..." in
+	 * place of common suffix between expected and actual.
+	 * 
+	 * @see Throwable#getMessage()
+	 */
+	@Override
+	public String getMessage() {
+		return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+	}
+	
+	/**
+	 * Gets the actual string value
+	 * @return the actual string value
+	 */
+	public String getActual() {
+		return fActual;
+	}
+	/**
+	 * Gets the expected string value
+	 * @return the expected string value
+	 */
+	public String getExpected() {
+		return fExpected;
+	}
+}
\ No newline at end of file
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index 911abdf..92a4ec2 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -21,21 +21,30 @@
 RUN="${RUN/push-and-run-prebuilt-test-jar/push-and-run-test-jar}"
 
 if [ $(basename $RUN) == 'host-run-test-jar' ]; then
-  BPATH="--runtime-option -Xbootclasspath:$ANDROID_HOST_OUT/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/javalib.jar"
-  # Remove prebuild from the flags, this test is for testing not having oat files.
-  flags="${flags/--prebuild/}"
+    framework="${ANDROID_HOST_OUT}/framework"
+    bpath_suffix="-hostdex"
+    # Remove prebuild from the flags, this test is for testing not having oat files.
+    flags="${flags/--prebuild/}"
 else
-  BPATH="--runtime-option -Xbootclasspath:/system/framework/core-libart.jar"
+    framework="/system/framework"
+    bpath_suffix=""
 fi
+bpath="${framework}/core-libart${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/conscrypt${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/okhttp${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/core-junit${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar"
+bpath_arg="--runtime-option -Xbootclasspath:${bpath}"
+
 
 # Make sure we can run without an oat file,
 echo "Run -Xnoimage-dex2oat"
-${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
+${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
 
 # Make sure we can run with the oat file.
 echo "Run -Ximage-dex2oat"
-${RUN} ${flags} ${BPATH} --runtime-option -Ximage-dex2oat
+${RUN} ${flags} ${bpath_arg} --runtime-option -Ximage-dex2oat
 
 # Make sure we can run with the default settings.
 echo "Run default"
-${RUN} ${flags} ${BPATH}
+${RUN} ${flags} ${bpath_arg}
diff --git a/test/121-simple-suspend-check/expected.txt b/test/121-simple-suspend-check/expected.txt
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/test/121-simple-suspend-check/expected.txt
@@ -0,0 +1 @@
+PASS
diff --git a/test/121-simple-suspend-check/info.txt b/test/121-simple-suspend-check/info.txt
new file mode 100644
index 0000000..61611f9
--- /dev/null
+++ b/test/121-simple-suspend-check/info.txt
@@ -0,0 +1 @@
+Simple test to ensure the compiler emits suspend checks on loops.
diff --git a/test/121-simple-suspend-check/src/Main.java b/test/121-simple-suspend-check/src/Main.java
new file mode 100644
index 0000000..80daf37
--- /dev/null
+++ b/test/121-simple-suspend-check/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String args[]) {
+    SpinThread thread = new SpinThread();
+    thread.setDaemon(true);
+    thread.start();
+    Runtime.getRuntime().gc();
+    try {
+      Thread.sleep(3000);
+    } catch (InterruptedException ie) {/*ignore */}
+    Runtime.getRuntime().gc();
+    System.out.println("PASS");
+  }
+}
+
+class SpinThread extends Thread {
+  public void run() {
+    while (true) {}
+  }
+}
diff --git a/test/122-npe/expected.txt b/test/122-npe/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/122-npe/expected.txt
diff --git a/test/122-npe/info.txt b/test/122-npe/info.txt
new file mode 100644
index 0000000..eef46d8
--- /dev/null
+++ b/test/122-npe/info.txt
@@ -0,0 +1 @@
+Test that our NPE checks and stack traces work.
diff --git a/test/122-npe/src/Main.java b/test/122-npe/src/Main.java
new file mode 100644
index 0000000..2fdcb9c
--- /dev/null
+++ b/test/122-npe/src/Main.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test that null pointer exceptions are thrown by the VM.
+ */
+public class Main {
+  private int f;
+  public static void main(String[] args) {
+    methodOne();
+  }
+
+  static void methodOne() {
+    methodTwo();
+  }
+
+  private int callSpecial() {
+    return f;
+  }
+
+  final int callFinal() {
+    return f;
+  }
+
+  static void methodTwo() {
+    NullPointerException npe = null;
+
+    int thisLine = 41;
+
+    new Object().getClass(); // Ensure compiled.
+    try {
+      ((Object) null).getClass();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 4);
+
+    new Main().callSpecial();  // Ensure compiled.
+    try {
+      ((Main) null).callSpecial();  // Test invokespecial.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    new Main().callFinal();  // Ensure compiled.
+    try {
+      ((Main) null).callFinal();  // Test invokevirtual on final.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    try {
+      ((Value) null).objectField.toString();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).intField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useFloat(((Value) null).floatField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useLong(((Value) null).longField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useDouble(((Value) null).doubleField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).objectField = "Fisk";
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).intField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).floatField = 42.0F;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).longField = 42L;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).doubleField = 42.0d;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).byteField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      if (((Value) null).booleanField) { }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).charField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).shortField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).byteField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).booleanField = true;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).charField = '\u0042';
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).shortField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Object[]) null)[0].toString();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((int[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useFloat(((float[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useLong(((long[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useDouble(((double[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Object[]) null)[0] = "Fisk";
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((int[]) null)[0] = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((float[]) null)[0] = 42.0F;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((long[]) null)[0] = 42L;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((double[]) null)[0] = 42.0d;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((byte[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      if (((boolean[]) null)[0]) { }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((char[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((short[]) null)[0]);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((byte[]) null)[0] = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((boolean[]) null)[0] = true;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((char[]) null)[0] = '\u0042';
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((short[]) null)[0] = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Object[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((int[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((float[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((long[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((double[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((byte[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((boolean[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((char[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((short[]) null).length);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      Interface i = null;
+      i.methodInterface();  // Test null on invokeinterface.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    try {
+      Object o = null;
+      o.toString();  // Test null on invokevirtual.
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 8);
+
+    npe = null;
+    try {
+      String s = null;
+      try {
+        throw new AssertionError();
+      } finally {
+        // Cause an implicit NPE.
+        s.getClass();
+      }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 13);
+
+    npe = null;
+    try {
+      String s = null;
+      try {
+        throw new AssertionError();
+      } catch (AssertionError ex) {
+      }
+      s.getClass();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 14);
+  }
+
+  static void check(NullPointerException npe, int firstLine) {
+    final boolean debug = false;
+    if (debug) {
+      System.out.print("Got to line ");
+      System.out.print(firstLine);
+      System.out.println();
+    }
+    StackTraceElement[] trace = npe.getStackTrace();
+    checkElement(trace[0], "Main", "methodTwo", "Main.java", firstLine);
+    checkElement(trace[1], "Main", "methodOne", "Main.java", 27);
+    checkElement(trace[2], "Main", "main", "Main.java", 23);
+  }
+
+  static void checkElement(StackTraceElement element,
+                                  String declaringClass, String methodName,
+                                  String fileName, int lineNumber) {
+    assertEquals(declaringClass, element.getClassName());
+    assertEquals(methodName, element.getMethodName());
+    assertEquals(fileName, element.getFileName());
+    assertEquals(lineNumber, element.getLineNumber());
+  }
+
+  static void assertEquals(Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+      throw new AssertionError(msg);
+    }
+  }
+
+  static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  interface Interface {
+    void methodInterface();
+  }
+
+  static void useInt(int i) {
+  }
+
+  static void useFloat(float f) {
+  }
+
+  static void useDouble(double d) {
+  }
+
+  static void useLong(long l) {
+  }
+
+  static class Value {
+    Object objectField;
+    int intField;
+    float floatField; long longField;
+    double doubleField;
+    byte byteField;
+    boolean booleanField;
+    char charField;
+    short shortField;
+  }
+}
diff --git a/test/123-compiler-regressions-mt/expected.txt b/test/123-compiler-regressions-mt/expected.txt
new file mode 100644
index 0000000..a11e5bf
--- /dev/null
+++ b/test/123-compiler-regressions-mt/expected.txt
@@ -0,0 +1,2 @@
+b17689750TestVolatile passed.
+b17689750TestMonitor passed.
diff --git a/test/123-compiler-regressions-mt/info.txt b/test/123-compiler-regressions-mt/info.txt
new file mode 100644
index 0000000..cac7e75
--- /dev/null
+++ b/test/123-compiler-regressions-mt/info.txt
@@ -0,0 +1,6 @@
+This is a test for bad optimizations affecting multi-threaded program
+behavior.
+
+This test covers fixed AOT/JIT bugs to prevent regressions.
+
+17689750 GVN assigns the same value names across MONITOR_ENTER and volatile reads.
diff --git a/test/123-compiler-regressions-mt/src/Main.java b/test/123-compiler-regressions-mt/src/Main.java
new file mode 100644
index 0000000..11fa021
--- /dev/null
+++ b/test/123-compiler-regressions-mt/src/Main.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 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.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Test for Jit regressions.
+ */
+public class Main {
+    public static void main(String args[]) throws Exception {
+        b17689750TestVolatile();
+        b17689750TestMonitor();
+    }
+
+    static void b17689750TestVolatile() {
+        final B17689750TestVolatile test = new B17689750TestVolatile();
+        new Thread() {
+            public void run() {
+                test.thread1();
+            }
+        }.start();
+        try {
+            test.thread2();
+        } catch (NullPointerException expected) {
+            System.out.println("b17689750TestVolatile passed.");
+        }
+    }
+
+    static void b17689750TestMonitor() {
+      final B17689750TestMonitor test = new B17689750TestMonitor();
+      new Thread() {
+        public void run() {
+          test.thread1();
+        }
+      }.start();
+      try {
+        test.thread2();
+      } catch (NullPointerException expected) {
+        System.out.println("b17689750TestMonitor passed.");
+      }
+    }
+}
+
+class B17689750TestVolatile {
+  private volatile int state = 0;
+  private int[] values = { 42 };
+
+  void thread1() {
+    while (state != 1) { }  // Busy loop.
+    values = null;
+    state = 2;
+  }
+
+  void thread2() {
+    int[] vs1 = values;
+    state = 1;
+    while (state != 2) { }  // Busy loop.
+    int[] vs2 = values;
+    int v1 = vs1[0];
+    int v2 = vs2[0];
+    System.out.println("b17689750TestVolatile failed: " + v1 + ", " + v2);
+  }
+}
+
+class B17689750TestMonitor {
+  private int state = 0;
+  private Object lock = new Object();
+  private int[] values = { 42 };
+
+  void thread1() {
+    int s;
+    do {
+      synchronized (lock) {
+        s = state;
+      }
+    } while (s != 1);  // Busy loop.
+
+    synchronized (lock) {
+      values = null;
+      state = 2;
+    }
+  }
+
+  void thread2() {
+    int[] vs1;
+    synchronized (lock) {
+      vs1 = values;
+      state = 1;
+    }
+
+    int s;
+    do {
+      synchronized (lock) {
+        s = state;
+      }
+    } while (s != 2);  // Busy loop.
+
+    int[] vs2 = values;
+    int v1 = vs1[0];
+    int v2 = vs2[0];
+    System.out.println("b17689750TestMonitor failed: " + v1 + ", " + v2);
+  }
+}
diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java
index 2c6d1c2..07c407b 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -97,6 +97,11 @@
     if (exception == null) {
       throw new Error("Missing NullPointerException");
     }
+
+    result = $opt$InvokeVirtualMethod();
+    if (result != 42) {
+      throw new Error("Unexpected result: " + result);
+    }
   }
 
   public static void invokePrivate() {
@@ -205,5 +210,13 @@
     m.o = new Main();
   }
 
+  public static int $opt$InvokeVirtualMethod() {
+    return new Main().virtualMethod();
+  }
+
+  public int virtualMethod() {
+    return 42;
+  }
+
   Object o;
 }
diff --git a/test/407-arrays/src/Main.java b/test/407-arrays/src/Main.java
index 5d27e6d..b5e95b0 100644
--- a/test/407-arrays/src/Main.java
+++ b/test/407-arrays/src/Main.java
@@ -57,7 +57,7 @@
                               int[] ints, Object[] objects, long[] longs, int index) {
     bools[0] = true;
     assertEquals(true, bools[0]);
-    bools[1] = true;
+    bools[index] = true;
     assertEquals(true, bools[index]);
 
     bytes[0] = -4;
diff --git a/test/408-move-bug/expected.txt b/test/408-move-bug/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/408-move-bug/expected.txt
diff --git a/test/408-move-bug/info.txt b/test/408-move-bug/info.txt
new file mode 100644
index 0000000..27a3dbc
--- /dev/null
+++ b/test/408-move-bug/info.txt
@@ -0,0 +1,2 @@
+Regression test for the register allocator in the optimizing
+compiler. Input moves where being overridden by sibling moves.
diff --git a/test/408-move-bug/src/Main.java b/test/408-move-bug/src/Main.java
new file mode 100644
index 0000000..420298b
--- /dev/null
+++ b/test/408-move-bug/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  public static void main(String[] args) {
+    crash();
+    npe();
+  }
+
+  static void crash() {
+    boolean b = baz();
+    // Create many objects to starve registers.
+    Main foo1 = create();
+    Main foo2 = create();
+    Main foo3 = create();
+    Main foo4 = create();
+    foo1.otherField = null;
+    // On X86, we would force b to be in a byte register, which
+    // would generate moves. This code exposed a bug in the
+    // register allocator, where an input move was not just before
+    // the instruction itself, and its destination was overridden
+    // by another value.
+    foo1.field = b;
+    foo2.field = b;
+    foo3.field = b;
+    foo4.field = b;
+    foo1.lastField = b;
+  }
+
+  // Similar to `crash` but generated an NPE.
+  static void npe() {
+    boolean b = baz();
+    Main foo1 = create();
+    Main foo2 = create();
+    Main foo3 = create();
+    Main foo4 = create();
+    foo1.field = b;
+    foo2.field = b;
+    foo3.field = b;
+    foo4.field = b;
+    foo1.lastField = b;
+  }
+
+  static Main create() {
+    return new Main();
+  }
+
+  static boolean baz() {
+    return false;
+  }
+
+  boolean field;
+  Object otherField;
+  boolean lastField;
+}
diff --git a/test/409-materialized-condition/expected.txt b/test/409-materialized-condition/expected.txt
new file mode 100644
index 0000000..a0796cd
--- /dev/null
+++ b/test/409-materialized-condition/expected.txt
@@ -0,0 +1,5 @@
+foo1
+In do nothing.
+In if.
+foo2
+In do nothing.
diff --git a/test/409-materialized-condition/info.txt b/test/409-materialized-condition/info.txt
new file mode 100644
index 0000000..898560d
--- /dev/null
+++ b/test/409-materialized-condition/info.txt
@@ -0,0 +1 @@
+Test that materialized conditions are evaluated correctly.
diff --git a/test/409-materialized-condition/src/Main.java b/test/409-materialized-condition/src/Main.java
new file mode 100644
index 0000000..0c179a9
--- /dev/null
+++ b/test/409-materialized-condition/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  public static void doNothing(boolean b) {
+    System.out.println("In do nothing.");
+  }
+
+  public static void inIf() {
+    System.out.println("In if.");
+  }
+
+  public static int bar() {
+    return 42;
+  }
+
+  public static int foo1() {
+    int b = bar();
+    doNothing(b == 42);
+    // This second `b == 42` will be GVN'ed away.
+    if (b == 42) {
+      inIf();
+      return b;
+    }
+    return 0;
+  }
+
+  public static int foo2() {
+    int b = bar();
+    doNothing(b == 41);
+    // This second `b == 41` will be GVN'ed away.
+    if (b == 41) {
+      inIf();
+      return 0;
+    }
+    return b;
+  }
+
+  public static void main(String[] args) {
+    System.out.println("foo1");
+    int res = foo1();
+    if (res != 42) {
+      throw new Error("Unexpected return value for foo1: " + res + ", expected 42.");
+    }
+
+    System.out.println("foo2");
+    res = foo2();
+    if (res != 42) {
+      throw new Error("Unexpected return value for foo2: " + res + ", expected 42.");
+    }
+  }
+}
diff --git a/test/702-LargeBranchOffset/build b/test/702-LargeBranchOffset/build
new file mode 100644
index 0000000..eacf730
--- /dev/null
+++ b/test/702-LargeBranchOffset/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+# Write out a bunch of source files.
+cpp -P src/Main.java.in src/Main.java
+
+mkdir classes
+${JAVAC} -d classes src/*.java
+
+${DX} --debug --dex --output=classes.dex classes
+zip $TEST_NAME.jar classes.dex
diff --git a/test/702-LargeBranchOffset/expected.txt b/test/702-LargeBranchOffset/expected.txt
new file mode 100644
index 0000000..130678f
--- /dev/null
+++ b/test/702-LargeBranchOffset/expected.txt
@@ -0,0 +1,5 @@
+0
+0
+2
+1
+512
diff --git a/test/702-LargeBranchOffset/info.txt b/test/702-LargeBranchOffset/info.txt
new file mode 100644
index 0000000..747263e
--- /dev/null
+++ b/test/702-LargeBranchOffset/info.txt
@@ -0,0 +1 @@
+Simple test to check if large branch offset works correctly.
diff --git a/test/702-LargeBranchOffset/src/Main.java.in b/test/702-LargeBranchOffset/src/Main.java.in
new file mode 100644
index 0000000..270d766
--- /dev/null
+++ b/test/702-LargeBranchOffset/src/Main.java.in
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DO_2_TIMES(x) x x
+#define DO_4_TIMES(x) DO_2_TIMES(DO_2_TIMES(x))
+#define DO_16_TIMES(x) DO_4_TIMES(DO_4_TIMES(x))
+#define DO_256_TIMES(x) DO_16_TIMES(DO_16_TIMES(x))
+#define DO_512_TIMES(x) DO_256_TIMES(DO_2_TIMES(x))
+
+
+public class Main {
+  public static void main(String[] args) {
+    Main m = new Main();
+    System.out.println(m.foo(-1, -1));
+    System.out.println(m.foo(-1, +1));
+    System.out.println(m.foo(+1, -1));
+    System.out.println(m.foo(+1, +1));
+    System.out.println(m.value);
+  }
+
+  public int foo(int a, int b) {
+    if ( a >= 0 ) {
+      if ( b < 0 ) {
+        DO_512_TIMES( synchronized(lock) { value++; } )
+        return 2;
+      }
+      return 1;
+    }
+    return 0;
+  }
+
+  Object lock = new Object();
+  int value = 0;
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 4ff6c65..302db38 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -21,140 +21,8 @@
 TEST_ART_RUN_TESTS := $(wildcard $(LOCAL_PATH)/[0-9]*)
 TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS))
 
-# List all the test names for host and target and compiler variants.
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): undefined, -trace, -gcverify or -gcstress
-# $(3): -relocate, -norelocate, -no-prebuild, or undefined.
-define all-run-test-names
-  test-art-host-run-test$(2)-default$(3)-$(1)32 \
-  test-art-host-run-test$(2)-optimizing$(3)-$(1)32 \
-  test-art-host-run-test$(2)-interpreter$(3)-$(1)32 \
-  test-art-host-run-test$(2)-default$(3)-$(1)64 \
-  test-art-host-run-test$(2)-optimizing$(3)-$(1)64 \
-  test-art-host-run-test$(2)-interpreter$(3)-$(1)64 \
-  test-art-target-run-test$(2)-default$(3)-$(1)32 \
-  test-art-target-run-test$(2)-optimizing$(3)-$(1)32 \
-  test-art-target-run-test$(2)-interpreter$(3)-$(1)32 \
-  test-art-target-run-test$(2)-default$(3)-$(1)64 \
-  test-art-target-run-test$(2)-optimizing$(3)-$(1)64 \
-  test-art-target-run-test$(2)-interpreter$(3)-$(1)64
-endef  # all-run-test-names
-
-# Subset of the above for target only.
-define all-run-test-target-names
-  test-art-target-run-test$(2)-default$(3)-$(1)32 \
-  test-art-target-run-test$(2)-optimizing$(3)-$(1)32 \
-  test-art-target-run-test$(2)-interpreter$(3)-$(1)32 \
-  test-art-target-run-test$(2)-default$(3)-$(1)64 \
-  test-art-target-run-test$(2)-optimizing$(3)-$(1)64 \
-  test-art-target-run-test$(2)-interpreter$(3)-$(1)64
-endef  # all-run-test-target-names
-
-# Tests that are timing sensitive and flaky on heavily loaded systems.
-TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
-  053-wait-some \
-  055-enum-performance
-
- # disable timing sensitive tests on "dist" builds.
-ifdef dist_goal
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-relocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-norelocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-prebuild))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-prebuild))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-no-prebuild))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-no-prebuild))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
-endif
-
-# Tests that are broken in --trace mode.
-TEST_ART_BROKEN_TRACE_RUN_TESTS :=
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
-
-# Tests that need more than 2MB of RAM or are running into other corner cases in GC stress related
-# to OOMEs.
-TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
-
-# 115-native-bridge setup is complicated. Need to implement it correctly for the target.
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-no-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-no-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-no-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-no-prebuild)
-
-# NB 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
-# On host this is patched around by changing a run flag but we cannot do this on the target due to
-# a different run-script.
-TEST_ART_TARGET_BROKEN_PREBUILD_RUN_TESTS := \
-  116-nodex2oat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-trace,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcverify,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcstress,-prebuild))
-
-# NB 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without --prebuild --relocate
-TEST_ART_BROKEN_RELOCATE_TESTS := \
-  117-nopatchoat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
-
-TEST_ART_BROKEN_NORELOCATE_TESTS := \
-  117-nopatchoat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
-
-TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
-  117-nopatchoat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcverify,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
+########################################################################
+# The art-run-tests module, used to build all run-tests into an image.
 
 # The path where build only targets will be output, e.g.
 # out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA
@@ -189,132 +57,227 @@
 include $(BUILD_PHONY_PACKAGE)
 
 # Clear temp vars.
-all-run-test-names :=
 art_run_tests_dir :=
 define-build-art-run-test :=
 TEST_ART_RUN_TEST_BUILD_RULES :=
-TEST_ART_TIMING_SENSITIVE_RUN_TESTS :=
-TEST_ART_BROKEN_TRACE_RUN_TESTS :=
-TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
 
 ########################################################################
+# General rules to build and run a run-test.
 
-ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+# Test rule names or of the form:
+# test-art-{1: host or target}-run-test-{2: prebuild no-prebuild no-dex2oat}-
+#    {3: interpreter default optimizing}-{4: relocate no-relocate relocate-no-patchoat}-
+#    {5: trace or no-trace}-{6: gcstress gcverify cms}-{7: forcecopy checkjni jni}-
+#    {8: no-image or image}-{9: test name}{10: 32 or 64}
+TARGET_TYPES := host target
+PREBUILD_TYPES := prebuild
+ifeq ($(ART_TEST_RUN_TEST_NO_PREBUILD),true)
+  PREBUILD_TYPES += no-prebuild
+endif
+ifeq ($(ART_TEST_RUN_TEST_NO_DEX2OAT),true)
+  PREBUILD_TYPES += no-dex2oat
+endif
+COMPILER_TYPES :=
+ifeq ($(ART_TEST_DEFAULT_COMPILER),true)
+  COMPILER_TYPES += default
+endif
+ifeq ($(ART_TEST_INTERPRETER),true)
+  COMPILER_TYPES += interpreter
+endif
+ifeq ($(ART_TEST_OPTIMIZING),true)
+  COMPILER_TYPES += optimizing
+endif
+RELOCATE_TYPES := relocate
+ifeq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
+  RELOCATE_TYPES += no-relocate
+endif
+ifeq ($(ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT),true)
+  RELOCATE_TYPES := relocate-no-patchoat
+endif
+TRACE_TYPES := no-trace
+ifeq ($(ART_TEST_TRACE),true)
+  TRACE_TYPES += trace
+endif
+GC_TYPES := cms
+ifeq ($(ART_TEST_GC_STRESS),true)
+  GC_TYPES += gcstress
+endif
+ifeq ($(ART_TEST_GC_VERIFY),true)
+  GC_TYPES += gcverify
+endif
+JNI_TYPES := checkjni
+ifeq ($(ART_TEST_JNI_FORCECOPY),true)
+  JNI_TYPES += forcecopy
+endif
+IMAGE_TYPES := image
+ifeq ($(ART_TEST_RUN_TEST_NO_IMAGE),true)
+  IMAGE_TYPES += no-image
+endif
+ADDRESS_SIZES_TARGET := $(ART_PHONY_TEST_TARGET_SUFFIX) $(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
+ADDRESS_SIZES_HOST := $(ART_PHONY_TEST_HOST_SUFFIX) $(2ND_ART_PHONY_TEST_HOST_SUFFIX)
+ALL_ADDRESS_SIZES := 64 32
+
+# List all run test names with number arguments agreeing with the comment above.
+define all-run-test-names
+  $(foreach target, $(1), \
+    $(foreach prebuild, $(2), \
+      $(foreach compiler, $(3), \
+        $(foreach relocate, $(4), \
+          $(foreach trace, $(5), \
+            $(foreach gc, $(6), \
+              $(foreach jni, $(7), \
+                $(foreach image, $(8), \
+                  $(foreach test, $(9), \
+                    $(foreach address_size, $(10), \
+                      test-art-$(target)-run-test-$(prebuild)-$(compiler)-$(relocate)-$(trace)-$(gc)-$(jni)-$(image)-$(test)$(address_size) \
+                  ))))))))))
+endef  # all-run-test-names
+
+# To generate a full list or tests:
+# $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \
+#        $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+#        $(TEST_ART_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+# Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE
+define name-to-var
+$(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_')
+endef  # name-to-var
+
+# Tests that are timing sensitive and flaky on heavily loaded systems.
+TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
+  053-wait-some \
+  055-enum-performance
+
+ # disable timing sensitive tests on "dist" builds.
+ifdef dist_goal
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+        $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+        $(IMAGE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_TIMING_SENSITIVE_RUN_TESTS :=
+
+TEST_ART_BROKEN_RUN_TESTS := \
+  004-ThreadStress
+
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(TEST_ART_BROKEN_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+TEST_ART_BROKEN_RUN_TESTS :=
+
+# Note 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
+TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \
+  116-nodex2oat
+
+ifneq (,$(filter prebuild,$(PREBUILD_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),prebuild, \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(TEST_ART_BROKEN_PREBUILD_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_PREBUILD_RUN_TESTS :=
+
+TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
+  117-nopatchoat
+
+ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),no-prebuild, \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_NO_PREBUILD_TESTS :=
+
+# Note 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without
+# --prebuild --relocate
+TEST_ART_BROKEN_NO_RELOCATE_TESTS := \
+  117-nopatchoat
+
+ifneq (,$(filter no-relocate,$(RELOCATE_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES), no-relocate,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(TEST_ART_BROKEN_NO_RELOCATE_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
+
+# Tests that are broken with GC stress.
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
+  004-SignalTest
+
+ifneq (,$(filter gcstress,$(GC_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),gcstress,$(JNI_TYPES), \
+      $(IMAGE_TYPES), $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
+
+# 115-native-bridge setup is complicated. Need to implement it correctly for the target.
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(PREBUILD_TYPES),$(COMPILER_TYPES), \
+    $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),115-native-bridge, \
+    $(ALL_ADDRESS_SIZES))
+
+# All these tests check that we have sane behavior if we don't have a patchoat or dex2oat.
+# Therefore we shouldn't run them in situations where we actually don't have these since they
+# explicitly test for them. These all also assume we have an image.
+TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
+  116-nodex2oat \
+  117-nopatchoat \
+  118-noimage-dex2oat \
+  119-noimage-patchoat
+
+ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),no-dex2oat, \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+      $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+
+ifneq (,$(filter no-image,$(IMAGE_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),no-image, \
+      $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+ifneq (,$(filter relocate-no-patchoat,$(RELOCATE_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES), relocate-no-patchoat,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
+
+# Clear variables ahead of appending to them when defining tests.
+$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach prebuild, $(PREBUILD_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach compiler, $(COMPILER_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach relocate, $(RELOCATE_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach trace, $(TRACE_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach gc, $(GC_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach jni, $(JNI_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach image, $(IMAGE_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach test, $(TEST_ART_RUN_TESTS), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach address_size, $(ALL_ADDRESS_SIZES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=)))
 
 # We need dex2oat and dalvikvm on the target as well as the core image.
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT)
@@ -347,659 +310,265 @@
   $(2ND_HOST_CORE_IMG_OUT)
 endif
 
-# For a given test create all the combinations of host/target, compiler and suffix such as:
-# test-art-host-run-test-optimizing-003-omnibus-opcodes32
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): host or target
-# $(3): default, optimizing or interpreter
-# $(4): 32 or 64
-# $(5): run tests with tracing or GC verification enabled or not: trace, gcverify or undefined
-# $(6): relocate, norelocate, no-prebuild or undefined.
+# Create a rule to build and run a tests following the form:
+# test-art-{1: host or target}-run-test-{2: prebuild no-prebuild no-dex2oat}-
+#    {3: interpreter default optimizing}-{4: relocate no-relocate relocate-no-patchoat}-
+#    {5: trace or no-trace}-{6: gcstress gcverify cms}-{7: forcecopy checkjni jni}-
+#    {8: no-image image}-{9: test name}{10: 32 or 64}
 define define-test-art-run-test
   run_test_options := $(addprefix --runtime-option ,$(DALVIKVM_FLAGS))
-  run_test_rule_name :=
-  uc_host_or_target :=
   prereq_rule :=
-  skip_test := false
-  uc_reloc_type :=
+  test_groups :=
+  uc_host_or_target :=
   ifeq ($(ART_TEST_RUN_TEST_ALWAYS_CLEAN),true)
     run_test_options += --always-clean
   endif
-  ifeq ($(2),host)
+  ifeq ($(1),host)
     uc_host_or_target := HOST
+    test_groups := ART_RUN_TEST_HOST_RULES
     run_test_options += --host
     prereq_rule := $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES)
   else
-    ifeq ($(2),target)
+    ifeq ($(1),target)
       uc_host_or_target := TARGET
+      test_groups := ART_RUN_TEST_TARGET_RULES
       prereq_rule := test-art-target-sync
     else
-      $$(error found $(2) expected host or target)
+      $$(error found $(1) expected $(TARGET_TYPES))
     endif
   endif
-  ifeq ($(6),relocate)
-    uc_reloc_type := RELOCATE
-    run_test_options += --relocate --no-prebuild
-    ifneq ($(ART_TEST_RUN_TEST_RELOCATE),true)
-      skip_test := true
-    endif
+  ifeq ($(2),prebuild)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PREBUILD_RULES
+    run_test_options += --prebuild
   else
-    ifeq ($(6),no-prebuild)
-      uc_reloc_type := NO_PREBUILD
-      run_test_options += --no-relocate --no-prebuild
-      ifneq ($(ART_TEST_RUN_TEST_NO_PREBUILD),true)
-        skip_test := true
-      endif
+    ifeq ($(2),no-prebuild)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_PREBUILD_RULES
+      run_test_options += --no-prebuild
     else
-      ifeq ($(6),norelocate)
-        uc_reloc_type := NORELOCATE
-        run_test_options += --no-relocate --prebuild
-        ifneq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
-          skip_test := true
-        endif
+      ifeq ($(2),no-dex2oat)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_DEX2OAT_RULES
+        run_test_options += --no-prebuild --no-dex2oat
       else
-        uc_reloc_type := PREBUILD
-        run_test_options += --relocate --prebuild
-        ifneq ($(ART_TEST_RUN_TEST_PREBUILD),true)
-          skip_test := true
-        endif
+        $$(error found $(2) expected $(PREBUILD_TYPES))
       endif
     endif
   endif
-  uc_compiler :=
   ifeq ($(3),optimizing)
-    uc_compiler := OPTIMIZING
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_OPTIMIZING_RULES
     run_test_options += -Xcompiler-option --compiler-backend=Optimizing
-    ifneq ($$(ART_TEST_OPTIMIZING),true)
-      skip_test := true
-    endif
   else
     ifeq ($(3),interpreter)
-      uc_compiler := INTERPRETER
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_RULES
       run_test_options += --interpreter
     else
       ifeq ($(3),default)
-        uc_compiler := DEFAULT
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEFAULT_RULES
       else
-        $$(error found $(3) expected optimizing, interpreter or default)
+        $$(error found $(3) expected $(COMPILER_TYPES))
       endif
     endif
   endif
-  ifeq ($(4),64)
-    run_test_options += --64
+  ifeq ($(4),relocate)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_RULES
+    run_test_options += --relocate
   else
-    ifneq ($(4),32)
-      $$(error found $(4) expected 32 or 64)
+    ifeq ($(4),no-relocate)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_RELOCATE_RULES
+      run_test_options += --no-relocate
+    else
+      ifeq ($(4),relocate-no-patchoat)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_NO_PATCHOAT_RULES
+        run_test_options += --relocate --no-patchoat
+      else
+        $$(error found $(4) expected $(RELOCATE_TYPES))
+      endif
     endif
   endif
   ifeq ($(5),trace)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_TRACE_RULES
     run_test_options += --trace
-    run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(6)-$(1)$(4)
-    ifneq ($$(ART_TEST_TRACE),true)
-      skip_test := true
-    endif
   else
-    ifeq ($(5),gcverify)
-      run_test_options += --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify \
-        --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc
-      run_test_rule_name := test-art-$(2)-run-test-gcverify-$(3)-$(6)-$(1)$(4)
-      ifneq ($$(ART_TEST_GC_VERIFY),true)
-        skip_test := true
-      endif
+    ifeq ($(5),no-trace)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_TRACE_RULES
     else
-      ifeq ($(5),gcstress)
-        run_test_options += --runtime-option -Xgc:SS --runtime-option -Xms2m \
-          --runtime-option -Xmx2m --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify
-        run_test_rule_name := test-art-$(2)-run-test-gcstress-$(3)-$(6)-$(1)$(4)
-        ifneq ($$(ART_TEST_GC_STRESS),true)
-          skip_test := true
-        endif
+      $$(error found $(5) expected $(TRACE_TYPES))
+    endif
+  endif
+  ifeq ($(6),gcverify)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCVERIFY_RULES
+    run_test_options += --gcverify
+  else
+    ifeq ($(6),gcstress)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCSTRESS_RULES
+      run_test_options += --gcstress
+    else
+      ifeq ($(6),cms)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CMS_RULES
       else
-        ifneq (,$(5))
-          $$(error found $(5) expected undefined or gcverify, gcstress or trace)
-        endif
-        run_test_rule_name := test-art-$(2)-run-test-$(3)-$(6)-$(1)$(4)
+        $$(error found $(6) expected $(GC_TYPES))
       endif
     endif
   endif
-  ifeq ($$(skip_test),false)
-    run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \
+  ifeq ($(7),forcecopy)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_FORCECOPY_RULES
+    run_test_options += --runtime-option -Xjniopts:forcecopy
+    ifneq ($$(ART_TEST_JNI_FORCECOPY),true)
+      skip_test := true
+    endif
+  else
+    ifeq ($(7),checkjni)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CHECKJNI_RULES
+      run_test_options += --runtime-option -Xcheck:jni
+    else
+      ifeq ($(7),jni)
+        test_groups += ART_RUN_TEST_$$(uc_host_or_target)_JNI_RULES
+      else
+        $$(error found $(7) expected $(JNI_TYPES))
+      endif
+    endif
+  endif
+  ifeq ($(8),no-image)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_IMAGE_RULES
+    run_test_options += --no-image
+  else
+    ifeq ($(8),image)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES
+    else
+      $$(error found $(8) expected $(IMAGE_TYPES))
+    endif
+  endif
+  # $(9) is the test name
+  test_groups += ART_RUN_TEST_$$(uc_host_or_target)_$(call name-to-var,$(9))_RULES
+  ifeq ($(10),64)
+    test_groups += ART_RUN_TEST_$$(uc_host_or_target)_64_RULES
+    run_test_options += --64
+  else
+    ifeq ($(10),32)
+      test_groups += ART_RUN_TEST_$$(uc_host_or_target)_32_RULES
+    else
+      $$(error found $(10) expected $(ALL_ADDRESS_SIZES))
+    endif
+  endif
+  run_test_rule_name := test-art-$(1)-run-test-$(2)-$(3)-$(4)-$(5)-$(6)-$(7)-$(8)-$(9)$(10)
+  run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \
       $$(run_test_options)
 $$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
 .PHONY: $$(run_test_rule_name)
 $$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $$(prereq_rule)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
 	  DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
-	    art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(1) \
+	    art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(9) \
 	      && $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@)
 	$$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \
 	  echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \
 	  rm -r $(ART_HOST_TEST_DIR)) || true
-  else
-    .PHONY: $$(run_test_rule_name)
-$$(run_test_rule_name):
-  endif
 
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)$(4)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$(1)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)$(4)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL$(4)_RULES += $$(run_test_rule_name)
+  $$(foreach test_group,$$(test_groups), $$(eval $$(value test_group) += $$(run_test_rule_name)))
 
   # Clear locally defined variables.
-  skip_test :=
+  uc_host_or_target :=
+  test_groups :=
   run_test_options :=
   run_test_rule_name :=
-  uc_host_or_target :=
   prereq_rule :=
-  uc_reloc_type :=
-  uc_compiler :=
 endef  # define-test-art-run-test
 
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach test, $(TEST_ART_RUN_TESTS), \
+    $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), \
+      $(foreach prebuild, $(PREBUILD_TYPES), \
+        $(foreach compiler, $(COMPILER_TYPES), \
+          $(foreach relocate, $(RELOCATE_TYPES), \
+            $(foreach trace, $(TRACE_TYPES), \
+              $(foreach gc, $(GC_TYPES), \
+                $(foreach jni, $(JNI_TYPES), \
+                  $(foreach image, $(IMAGE_TYPES), \
+                    $(eval $(call define-test-art-run-test,$(target),$(prebuild),$(compiler),$(relocate),$(trace),$(gc),$(jni),$(image),$(test),$(address_size))) \
+                ))))))))))
+define-test-art-run-test :=
+
 # Define a phony rule whose purpose is to test its prerequisites.
-# $(1): rule name, e.g. test-art-host-run-test32
+# $(1): host or target
 # $(2): list of prerequisites
-define define-test-art-run-test-group-rule
+define define-test-art-run-test-group
 .PHONY: $(1)
 $(1): $(2)
 	$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
 
-endef  # define-test-art-run-test-group-rule
-
-# Create rules for a group of run tests.
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): host or target
-# $(3): relocate, norelocate or no-prebuild, or prebuild.
-define define-test-art-run-test-group-type
-  group_uc_host_or_target :=
-  ifeq ($(2),host)
-    group_uc_host_or_target := HOST
-  else
-    ifeq ($(2),target)
-      group_uc_host_or_target := TARGET
-    else
-      $$(error found $(2) expected host or target)
-    endif
-  endif
-
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
-  do_second := false
-  ifeq ($(2),host)
-    ifneq ($$(HOST_PREFER_32_BIT),true)
-      do_second := true
-    endif
-  else
-    ifdef TARGET_2ND_ARCH
-      do_second := true
-    endif
-  endif
-  ifeq (true,$$(do_second))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
-  endif
-endef  # define-test-art-run-test-group-type
-
-# Create rules for a group of run tests.
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): host or target
-define define-test-art-run-test-group
-  group_uc_host_or_target :=
-  ifeq ($(2),host)
-    group_uc_host_or_target := HOST
-  else
-    ifeq ($(2),target)
-      group_uc_host_or_target := TARGET
-    else
-      $$(error found $(2) expected host or target)
-    endif
-  endif
-  do_second := false
-  ifeq ($(2),host)
-    ifneq ($$(HOST_PREFER_32_BIT),true)
-      do_second := true
-    endif
-  else
-    ifdef TARGET_2ND_ARCH
-      do_second := true
-    endif
-  endif
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
-  ifeq ($$(do_second),true)
-    ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
-  endif
-  $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),prebuild))
-  $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),norelocate))
-  $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),relocate))
-  $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),no-prebuild))
-  $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-default-$(1), \
-    $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES)))
-  $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-interpreter-$(1), \
-    $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES)))
-  $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-optimizing-$(1), \
-    $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES)))
-  $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1), \
-    $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES)))
-  $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX), \
-    $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES)))
-  ifeq ($$(do_second),true)
-    $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX), \
-      $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES)))
-  endif
-
-  # Clear locally defined variables.
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
-  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
-  ifeq ($$(do_second),true)
-    ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
-  endif
-  group_uc_host_or_target :=
-  do_second :=
 endef  # define-test-art-run-test-group
 
-$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),target)))
-$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),host)))
 
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate, \
-  $(ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate, \
-  $(ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test, \
-  $(ART_TEST_TARGET_RUN_TEST_ALL_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default, \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter, \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing, \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild, \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate, \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate, \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate, \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate, \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate, \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate, \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
-  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-ifdef TARGET_2ND_ARCH
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
-    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-endif
+$(foreach target, $(TARGET_TYPES), $(eval \
+  $(call define-test-art-run-test-group,test-art-$(target)-run-test,$(ART_RUN_TEST_$(call name-to-var,$(target))_RULES))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach prebuild, $(PREBUILD_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(prebuild),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach compiler, $(COMPILER_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(compiler),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach relocate, $(RELOCATE_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(relocate),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach trace, $(TRACE_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(trace),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach gc, $(GC_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(gc),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach jni, $(JNI_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(jni),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach image, $(IMAGE_TYPES), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(image),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach test, $(TEST_ART_RUN_TESTS), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(test),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), $(eval \
+    $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(address_size),$(ART_RUN_TEST_$(address_size)_RULES)))))
 
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate, \
-  $(ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate, \
-  $(ART_TEST_HOST_RUN_TEST_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test, \
-  $(ART_TEST_HOST_RUN_TEST_ALL_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default, \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter, \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing, \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild, \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate, \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate, \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate, \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate, \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate, \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate, \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
-  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-ifneq ($(HOST_PREFER_32_BIT),true)
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
-    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-endif
-
-# include libarttest build rules.
-include $(LOCAL_PATH)/Android.libarttest.mk
-
-# Include libnativebridgetest build rules.
-include art/test/Android.libnativebridgetest.mk
-
-define-test-art-run-test :=
-define-test-art-run-test-group-rule :=
+# Clear variables now we're finished with them.
+$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach prebuild, $(PREBUILD_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach compiler, $(COMPILER_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach relocate, $(RELOCATE_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach trace, $(TRACE_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach gc, $(GC_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach jni, $(JNI_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach image, $(IMAGE_TYPES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach test, $(TEST_ART_RUN_TESTS), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+  $(foreach address_size, $(ALL_ADDRESS_SIZES), \
+    $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=)))
 define-test-art-run-test-group :=
-TEST_ART_RUN_TESTS :=
-ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+TARGET_TYPES :=
+PREBUILD_TYPES :=
+COMPILER_TYPES :=
+RELOCATE_TYPES :=
+TRACE_TYPES :=
+GC_TYPES :=
+JNI_TYPES :=
+IMAGE_TYPES :=
+ADDRESS_SIZES_TARGET :=
+ADDRESS_SIZES_HOST :=
+ALL_ADDRESS_SIZES :=
+
+include $(LOCAL_PATH)/Android.libarttest.mk
+include art/test/Android.libnativebridgetest.mk
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 7253a2b..c020478 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 #
 # Run the code in test.jar using the host-mode virtual machine. The jar should
 # contain a top-level class named Main to run.
@@ -23,6 +23,12 @@
 FLAGS=""
 COMPILER_FLAGS=""
 BUILD_BOOT_OPT=""
+PATCHOAT=""
+DEX2OAT=""
+FALSE_BIN="/bin/false"
+HAVE_IMAGE="y"
+TIME_OUT="y"
+TIME_OUT_VALUE=5m
 exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
 main="Main"
 
@@ -33,6 +39,12 @@
     elif [ "x$1" = "x--prebuild" ]; then
         PREBUILD="y"
         shift
+    elif [ "x$1" = "x--no-dex2oat" ]; then
+        DEX2OAT="-Xcompiler:${FALSE_BIN}"
+        shift
+    elif [ "x$1" = "x--no-patchoat" ]; then
+        PATCHOAT="-Xpatchoat:${FALSE_BIN}"
+        shift
     elif [ "x$1" = "x--lib" ]; then
         shift
         if [ "x$1" = "x" ]; then
@@ -44,6 +56,9 @@
             LIB=${LIB/%so/dylib}
         fi
         shift
+    elif [ "x$1" = "x--no-image" ]; then
+        HAVE_IMAGE="n"
+        shift
     elif [ "x$1" = "x--boot" ]; then
         shift
         option="$1"
@@ -52,10 +67,12 @@
         shift
     elif [ "x$1" = "x--debug" ]; then
         DEBUGGER="y"
+        TIME_OUT="n"
         shift
     elif [ "x$1" = "x--gdb" ]; then
         GDB="y"
         DEV_MODE="y"
+        TIME_OUT="n"
         shift
     elif [ "x$1" = "x--invoke-with" ]; then
         shift
@@ -157,6 +174,11 @@
     COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
 fi
 
+if [ "$HAVE_IMAGE" = "n" ]; then
+    # Set image to a place were there isn't one.
+    BOOT_OPT="-Ximage:/system/non-existant/core.art"
+fi
+
 if [ "$RELOCATE" = "y" ]; then
   FLAGS="${FLAGS} -Xrelocate"
   COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --include-patch-information"
@@ -180,7 +202,11 @@
 fi
 
 JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar $main"
+cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $PATCHOAT $DEX2OAT $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar $main"
+if [ "$TIME_OUT" = "y" ]; then
+  # Add timeout command if time out is desired.
+  cmdline="timeout $TIME_OUT_VALUE $cmdline"
+fi
 if [ "$DEV_MODE" = "y" ]; then
   if [ "$PREBUILD" = "y" ]; then
     echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
@@ -192,4 +218,20 @@
 fi
 
 cd $ANDROID_BUILD_TOP
-$mkdir_cmd && $prebuild_cmd && LD_PRELOAD=libsigchain.so $cmdline "$@"
+
+$mkdir_cmd || exit 1
+$prebuild_cmd || exit 2
+
+if [ "$GDB" = "y" ]; then
+  # When running under gdb, we cannot do piping and grepping...
+  LD_PRELOAD=libsigchain.so $cmdline "$@"
+else
+  # If we are execing /bin/false we might not be on the same ISA as libsigchain.so
+  # ld.so will helpfully warn us of this. Unfortunately this messes up our error
+  # checking so we will just filter out the error with a grep.
+  LD_PRELOAD=libsigchain.so $cmdline "$@" 2>&1 | grep -v -E "^ERROR: ld\.so: object '.+\.so' from LD_PRELOAD cannot be preloaded.*: ignored\.$"
+  # Add extra detail if time out is enabled.
+  if [ ${PIPESTATUS[0]} = 124 ] && [ "$TIME_OUT" = "y" ]; then
+    echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
+  fi
+fi
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
index ad23edf..91b8a0f 100755
--- a/test/etc/push-and-run-prebuilt-test-jar
+++ b/test/etc/push-and-run-prebuilt-test-jar
@@ -26,6 +26,10 @@
 TARGET_SUFFIX="32"
 GDB_TARGET_SUFFIX=""
 COMPILE_FLAGS=""
+FALSE_BIN="/system/bin/false"
+PATCHOAT=""
+DEX2OAT=""
+HAVE_IMAGE="y"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -55,12 +59,21 @@
         BOOT_OPT="$1"
         BUILD_BOOT_OPT="--boot-image=${1#-Ximage:}"
         shift
+    elif [ "x$1" = "x--no-dex2oat" ]; then
+        DEX2OAT="-Xcompiler:${FALSE_BIN}"
+        shift
+    elif [ "x$1" = "x--no-patchoat" ]; then
+        PATCHOAT="-Xpatchoat:${FALSE_BIN}"
+        shift
     elif [ "x$1" = "x--relocate" ]; then
         RELOCATE="y"
         shift
     elif [ "x$1" = "x--no-relocate" ]; then
         RELOCATE="n"
         shift
+    elif [ "x$1" = "x--no-image" ]; then
+        HAVE_IMAGE="n"
+        shift
     elif [ "x$1" = "x--debug" ]; then
         DEBUGGER="y"
         shift
@@ -136,6 +149,10 @@
 
 msg "------------------------------"
 
+if [ "$HAVE_IMAGE" = "n" ]; then
+    BOOT_OPT="-Ximage:/system/non-existant/core.art"
+fi
+
 ARCH=$(adb shell ls -F /data/dalvik-cache | grep -Ewo "${ARCHITECTURES_PATTERN}")
 if [ x"$ARCH" = "x" ]; then
   echo "Unable to determine architecture"
@@ -194,7 +211,7 @@
 cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
     mkdir -p $DEX_LOCATION/dalvik-cache/$ARCH/ && \
     $INVOKE_WITH /system/bin/dex2oatd $COMPILE_FLAGS $BUILD_BOOT_OPT $BUILD_RELOCATE_OPT  --runtime-arg -classpath --runtime-arg $DEX_LOCATION/$TEST_NAME.jar --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ARCH/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") --instruction-set=$ARCH && \
-    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
+    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $PATCHOAT $DEX2OAT $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
 cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
 echo "$cmdline" > $cmdfile
 
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index 06075c2..e398b5d 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -22,6 +22,10 @@
 FLAGS=""
 TARGET_SUFFIX="32"
 GDB_TARGET_SUFFIX=""
+FALSE_BIN="/system/bin/false"
+PATCHOAT=""
+DEX2OAT=""
+HAVE_IMAGE="y"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -40,6 +44,15 @@
         option="$1"
         FLAGS="${FLAGS} -Xcompiler-option $option"
         shift
+    elif [ "x$1" = "x--no-image" ]; then
+        HAVE_IMAGE="n"
+        shift
+    elif [ "x$1" = "x--no-dex2oat" ]; then
+        DEX2OAT="-Xcompiler:${FALSE_BIN}"
+        shift
+    elif [ "x$1" = "x--no-patchoat" ]; then
+        PATCHOAT="-Xpatchoat:${FALSE_BIN}"
+        shift
     elif [ "x$1" = "x--runtime-option" ]; then
         shift
         option="$1"
@@ -129,6 +142,10 @@
 
 msg "------------------------------"
 
+if [ "$HAVE_IMAGE" = "n" ]; then
+    BOOT_OPT="-Ximage:/system/non-existant/core.art"
+fi
+
 if [ "$QUIET" = "n" ]; then
   adb shell rm -r $DEX_LOCATION
   adb shell mkdir -p $DEX_LOCATION
@@ -172,7 +189,7 @@
 fi
 
 cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
-    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $PATCHOAT $DEX2OAT $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
 if [ "$DEV_MODE" = "y" ]; then
   echo $cmdline "$@"
 fi
diff --git a/test/run-all-tests b/test/run-all-tests
index 284cca0..318a0de 100755
--- a/test/run-all-tests
+++ b/test/run-all-tests
@@ -80,6 +80,12 @@
     elif [ "x$1" = "x--64" ]; then
         run_args="${run_args} --64"
         shift
+    elif [ "x$1" = "x--gcstress" ]; then
+        run_args="${run_args} --gcstress"
+        shift
+    elif [ "x$1" = "x--gcverify" ]; then
+        run_args="${run_args} --gcverify"
+        shift
     elif [ "x$1" = "x--trace" ]; then
         run_args="${run_args} --trace"
         shift
@@ -95,6 +101,12 @@
     elif [ "x$1" = "x--prebuild" ]; then
         run_args="${run_args} --prebuild"
         shift;
+    elif [ "x$1" = "x--no-dex2oat" ]; then
+        run_args="${run_args} --no-dex2oat"
+        shift;
+    elif [ "x$1" = "x--no-patchoat" ]; then
+        run_args="${run_args} --no-patchoat"
+        shift;
     elif [ "x$1" = "x--always-clean" ]; then
         run_args="${run_args} --always-clean"
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
@@ -116,7 +128,8 @@
              "further documentation:"
         echo "    --debug --dev --host --interpreter --jvm --no-optimize"
         echo "    --no-verify -O --update --valgrind --zygote --64 --relocate"
-        echo "    --prebuild --always-clean"
+        echo "    --prebuild --always-clean --gcstress --gcverify --trace"
+        echo "    --no-patchoat --no-dex2oat"
         echo "  Specific Runtime Options:"
         echo "    --seq                Run tests one-by-one, avoiding failures caused by busy CPU"
     ) 1>&2
diff --git a/test/run-test b/test/run-test
index 5d3cbac..b140fbf 100755
--- a/test/run-test
+++ b/test/run-test
@@ -77,7 +77,13 @@
 build_only="no"
 suffix64=""
 trace="false"
+basic_verify="false"
+gc_verify="false"
+gc_stress="false"
 always_clean="no"
+have_dex2oat="yes"
+have_patchoat="yes"
+have_image="yes"
 
 while true; do
     if [ "x$1" = "x--host" ]; then
@@ -96,6 +102,15 @@
         lib="libdvm.so"
         runtime="dalvik"
         shift
+    elif [ "x$1" = "x--no-dex2oat" ]; then
+        have_dex2oat="no"
+        shift
+    elif [ "x$1" = "x--no-patchoat" ]; then
+        have_patchoat="no"
+        shift
+    elif [ "x$1" = "x--no-image" ]; then
+        have_image="no"
+        shift
     elif [ "x$1" = "x--relocate" ]; then
         relocate="yes"
         shift
@@ -108,6 +123,14 @@
     elif [ "x$1" = "x--no-prebuild" ]; then
         prebuild_mode="no"
         shift;
+    elif [ "x$1" = "x--gcverify" ]; then
+        basic_verify="true"
+        gc_verify="true"
+        shift
+    elif [ "x$1" = "x--gcstress" ]; then
+        basic_verify="true"
+        gc_stress="true"
+        shift
     elif [ "x$1" = "x--image" ]; then
         shift
         image="$1"
@@ -202,9 +225,19 @@
 # Cannot us a simple "cd", as the path might not be created yet.
 # Cannot use readlink -m, as it does not exist on Mac.
 # Fallback to nuclear option:
+noncanonical_tmp_dir=$tmp_dir
 tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"`"
 mkdir -p $tmp_dir
 
+if [ "$basic_verify" = "true" ]; then
+  run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify"
+fi
+if [ "$gc_verify" = "true" ]; then
+  run_args="${run_args} --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc"
+fi
+if [ "$gc_stress" = "true" ]; then
+  run_args="${run_args} --runtime-option -Xgc:SS --runtime-option -Xms2m --runtime-option -Xmx2m"
+fi
 if [ "$trace" = "true" ]; then
     run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin --runtime-option -Xmethod-trace-file-size:2000000"
 fi
@@ -240,6 +273,14 @@
     fi
 fi
 
+if [ "$have_patchoat" = "no" ]; then
+  run_args="${run_args} --no-patchoat"
+fi
+
+if [ "$have_dex2oat" = "no" ]; then
+  run_args="${run_args} --no-dex2oat"
+fi
+
 if [ ! "$runtime" = "jvm" ]; then
   run_args="${run_args} --lib $lib"
 fi
@@ -275,6 +316,30 @@
     fi
 fi
 
+if [ "$have_image" = "no" ]; then
+    if [ "$runtime" != "art" ]; then
+        echo "--no-image is only supported on the art runtime"
+        exit 1
+    fi
+    if [ "$target_mode" = "no" ]; then
+        framework="${ANDROID_HOST_OUT}/framework"
+        bpath_suffix="-hostdex"
+    else
+        framework="/system/framework"
+        bpath_suffix=""
+    fi
+    # TODO If the target was compiled WITH_DEXPREOPT=true then these tests will
+    # fail since these jar files will be stripped.
+    bpath="${framework}/core-libart${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/conscrypt${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/okhttp${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/core-junit${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar"
+    # Pass down the bootclasspath
+    run_args="${run_args} --runtime-option -Xbootclasspath:${bpath}"
+    run_args="${run_args} --no-image"
+fi
+
 if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
     echo "--dev and --update are mutually exclusive" 1>&2
     usage="yes"
@@ -326,6 +391,8 @@
         echo "    --zygote             Spawn the process from the Zygote." \
              "If used, then the"
         echo "                         other runtime options are ignored."
+        echo "    --no-dex2oat         Run as though dex2oat was failing."
+        echo "    --no-patchoat        Run as though patchoat was failing."
         echo "    --prebuild           Run dex2oat on the files before starting test. (default)"
         echo "    --no-prebuild        Do not run dex2oat on the files before starting"
         echo "                         the test."
@@ -341,6 +408,8 @@
              "files."
         echo "    --64                 Run the test in 64-bit mode"
         echo "    --trace              Run with method tracing"
+        echo "    --gcstress           Run with gc stress testing"
+        echo "    --gcverify           Run with gc verification"
         echo "    --always-clean       Delete the test files even if the test fails."
     ) 1>&2
     exit 1
@@ -452,6 +521,8 @@
         "./${run}" $run_args "$@" >"$output" 2>&1
     else
         cp "$build_output" "$output"
+        echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`"
+        echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}"
         echo "build exit status: $build_exit" >>"$output"
     fi
     ./$check_cmd "$expected" "$output"
diff --git a/tools/Android.mk b/tools/Android.mk
index d3be17f..9a96f7a 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -27,3 +27,13 @@
 	@echo "Copy: $(PRIVATE_MODULE) ($@)"
 	$(copy-file-to-new-target)
 	$(hide) chmod 755 $@
+
+# Copy the art shell script to the target's bin directory
+include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := art
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
diff --git a/tools/art b/tools/art
old mode 100755
new mode 100644
index 85517d3..0103071
--- a/tools/art
+++ b/tools/art
@@ -14,8 +14,30 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-lib=-XXlib:libart.so
+function follow_links() {
+  if [ z"$BASH_SOURCE" != z ]; then
+    file="$BASH_SOURCE"
+  else
+    file="$0"
+  fi
+  while [ -h "$file" ]; do
+    # On Mac OS, readlink -f doesn't work.
+    file="$(readlink "$file")"
+  done
+  echo "$file"
+}
+
+function find_libdir() {
+  if [ "$(readlink "$ANDROID_ROOT/bin/$DALVIKVM")" = "dalvikvm64" ]; then
+    echo "lib64"
+  else
+    echo "lib"
+  fi
+}
+
 invoke_with=
+DALVIKVM=dalvikvm
+LIBART=libart.so
 
 while true; do
   if [ "$1" = "--invoke-with" ]; then
@@ -23,7 +45,13 @@
     invoke_with="$1"
     shift
   elif [ "$1" = "-d" ]; then
-    lib="-XXlib:libartd.so"
+    LIBART="libartd.so"
+    shift
+  elif [ "$1" = "--32" ]; then
+    DALVIKVM=dalvikvm32
+    shift
+  elif [ "$1" = "--64" ]; then
+    DALVIKVM=dalvikvm64
     shift
   elif expr "$1" : "--" >/dev/null 2>&1; then
     echo "unknown option: $1" 1>&2
@@ -33,38 +61,20 @@
   fi
 done
 
-function follow_links() {
-  file="$1"
-  while [ -h "$file" ]; do
-    # On Mac OS, readlink -f doesn't work.
-    file="$(readlink "$file")"
-  done
-  echo "$file"
-}
-
-PROG_NAME="$(follow_links "$BASH_SOURCE")"
+PROG_NAME="$(follow_links)"
 PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_BUILD_TOP="$(cd "${PROG_DIR}/../../../../" ; pwd -P)/"
-ANDROID_HOST_OUT=$PROG_DIR/..
+ANDROID_ROOT=$PROG_DIR/..
 ANDROID_DATA=$PWD/android-data$$
-DALVIKVM_EXECUTABLE=$ANDROID_HOST_OUT/bin/dalvikvm
+LIBDIR=$(find_libdir)
+LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
 
-function find_libdir() {
-  if [ "$(readlink "$DALVIKVM_EXECUTABLE")" = "dalvikvm64" ]; then
-    echo "lib64"
-  else
-    echo "lib"
-  fi
-}
-
-LD_LIBRARY_PATH=$ANDROID_HOST_OUT/"$(find_libdir)"
-
-mkdir -p $ANDROID_DATA/dalvik-cache/{x86,x86_64}
+mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
 ANDROID_DATA=$ANDROID_DATA \
-  ANDROID_ROOT=$ANDROID_HOST_OUT \
+  ANDROID_ROOT=$ANDROID_ROOT \
   LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
-  $invoke_with $DALVIKVM_EXECUTABLE $lib \
-    -Ximage:$ANDROID_HOST_OUT/framework/core.art \
+  $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \
+    -XXlib:$LIBART \
+    -Ximage:$ANDROID_ROOT/framework/core.art \
      "$@"
 EXIT_STATUS=$?
 rm -rf $ANDROID_DATA
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
new file mode 100755
index 0000000..b66191f
--- /dev/null
+++ b/tools/symbolize.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# Symbolize oat files from the dalvik cache of a device.
+#
+# By default, pulls everything from the dalvik cache. A simple yes/no/quit prompt for each file can
+# be requested by giving "--interactive" as a parameter.
+
+INTERACTIVE="no"
+if [ "x$1" = "x--interactive" ] ; then
+  INTERACTIVE="yes"
+fi
+
+# Pull the file from the device and symbolize it.
+function one() {
+  echo $1 $2
+  if [ "x$INTERACTIVE" = "xyes" ] ; then
+    echo -n "What to do? [Y/n/q] "
+    read -e input
+    if [ "x$input" = "xn" ] ; then
+      return
+    fi
+    if [ "x$input" = "xq" ] ; then
+      exit 0
+    fi
+  fi
+  adb pull /data/dalvik-cache/$1/$2 /tmp || exit 1
+  mkdir -p $OUT/symbols/data/dalvik-cache/$1
+  oatdump --symbolize=/tmp/$2 --output=$OUT/symbols/data/dalvik-cache/$1/$2
+}
+
+# adb shell ls seems to output in DOS format (CRLF), which messes up scripting
+function adbls() {
+  adb shell ls $@ | sed 's/\r$//'
+}
+
+# Check for all ISA directories on device.
+function all() {
+  DIRS=$(adbls /data/dalvik-cache/)
+  for DIR in $DIRS ; do
+    case $DIR in
+      arm|arm64|mips|x86|x86_64)
+        FILES=$(adbls /data/dalvik-cache/$DIR/*.oat /data/dalvik-cache/$DIR/*.dex)
+        for FILE in $FILES ; do
+          # Cannot use basename as the file doesn't exist.
+          NAME=$(echo $FILE | sed -e 's/.*\///')
+          one $DIR $NAME
+        done
+        ;;
+    esac
+  done
+}
+
+all